├── .editorconfig ├── .github └── workflows │ └── demo.yml ├── .gitignore ├── .prettierrc ├── README.md ├── license ├── package.json ├── packages └── neditor │ ├── base │ ├── browser │ │ ├── animation.ts │ │ ├── browser.ts │ │ ├── canIUse.ts │ │ ├── css.ts │ │ ├── devicePixelRatio.ts │ │ ├── dom.ts │ │ ├── event.ts │ │ ├── fastDomNode.ts │ │ ├── globalPointerMoveMonitor.ts │ │ ├── iframe.ts │ │ ├── indexedDB.ts │ │ ├── keyboardEvent.ts │ │ ├── mouseEvent.ts │ │ ├── performance.ts │ │ ├── platform.ts │ │ └── touch.ts │ ├── callback.ts │ ├── check.ts │ ├── check_op.ts │ ├── common │ │ ├── array.ts │ │ ├── arraybuffer.ts │ │ ├── assert.ts │ │ ├── async.ts │ │ ├── bignumber.ts │ │ ├── cancellation.ts │ │ ├── charCode.ts │ │ ├── collections.ts │ │ ├── color.ts │ │ ├── decorators.ts │ │ ├── errorMessage.ts │ │ ├── errors.ts │ │ ├── event.ts │ │ ├── extpath.ts │ │ ├── finalization.ts │ │ ├── format.ts │ │ ├── functional.ts │ │ ├── geometry.ts │ │ ├── iterator.ts │ │ ├── json.ts │ │ ├── jsonSchema.ts │ │ ├── keyCodes.ts │ │ ├── keybindingLabels.ts │ │ ├── lifecycle.ts │ │ ├── linkedList.ts │ │ ├── lodTransformBase.ts │ │ ├── map.ts │ │ ├── marshallingIds.ts │ │ ├── math.ts │ │ ├── mime.ts │ │ ├── network.ts │ │ ├── notreached.ts │ │ ├── number.ts │ │ ├── objects.ts │ │ ├── path.ts │ │ ├── performance.ts │ │ ├── platform.ts │ │ ├── process.ts │ │ ├── product.ts │ │ ├── reactivity │ │ │ ├── apiWatch.ts │ │ │ ├── baseHandlers.ts │ │ │ ├── dep.ts │ │ │ ├── effect.ts │ │ │ ├── effectScope.ts │ │ │ ├── errorHandling.ts │ │ │ ├── index.ts │ │ │ ├── operations.ts │ │ │ ├── patch.ts │ │ │ ├── reactive.ts │ │ │ ├── ref.ts │ │ │ ├── scheduler.ts │ │ │ └── warning.ts │ │ ├── resources.ts │ │ ├── stopwatch.ts │ │ ├── strings.ts │ │ ├── symbols.ts │ │ ├── type.ts │ │ ├── typescript.ts │ │ ├── uri.ts │ │ └── uuid.ts │ ├── logging.ts │ ├── nls │ │ └── nls.ts │ ├── numerics │ │ └── clamped_math.ts │ ├── parts │ │ └── storage │ │ │ └── common │ │ │ └── storage.ts │ ├── time │ │ └── time.ts │ ├── timer │ │ ├── elapsedTimer.ts │ │ ├── timer.ts │ │ └── tsconfig.json │ └── trace_event │ │ └── common │ │ └── trace_event_common.ts │ ├── canvas │ ├── canvas │ │ ├── browser.ts │ │ ├── canvas.ts │ │ ├── canvasContextKeys.ts │ │ ├── canvasImpl.ts │ │ ├── dom.ts │ │ ├── historyHelper.ts │ │ ├── hitTest.ts │ │ └── modelUpdater.ts │ ├── canvasCommon │ │ ├── scheduler.ts │ │ └── scope.ts │ ├── canvasExtension.ts │ ├── element │ │ ├── bounds.ts │ │ ├── collision.ts │ │ ├── textElement.ts │ │ ├── typeCheck.ts │ │ └── types.ts │ ├── ga.ts │ ├── gadirections.ts │ ├── galines.ts │ ├── gapoints.ts │ ├── gatransforms.ts │ ├── scene │ │ └── comparisons.ts │ ├── view │ │ ├── controller │ │ │ ├── mouseEventFactory.ts │ │ │ ├── mouseHandler.ts │ │ │ ├── mouseTarget.ts │ │ │ └── pointerHandler.ts │ │ ├── parts │ │ │ ├── canvas │ │ │ │ ├── adapter │ │ │ │ │ ├── app.ts │ │ │ │ │ ├── block.ts │ │ │ │ │ ├── context.ts │ │ │ │ │ ├── directives │ │ │ │ │ │ ├── bind.ts │ │ │ │ │ │ ├── for.ts │ │ │ │ │ │ ├── html.ts │ │ │ │ │ │ ├── if.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── model.ts │ │ │ │ │ │ ├── show.ts │ │ │ │ │ │ └── text.ts │ │ │ │ │ ├── eval.ts │ │ │ │ │ ├── scheduler.ts │ │ │ │ │ ├── utils.ts │ │ │ │ │ └── walk.ts │ │ │ │ ├── app.ts │ │ │ │ └── canvas.ts │ │ │ └── overlay │ │ │ │ ├── overlay.ts │ │ │ │ └── renderer.ts │ │ ├── type.ts │ │ ├── view.css │ │ ├── view.ts │ │ ├── viewController.ts │ │ ├── viewEvents.ts │ │ ├── viewImpl.ts │ │ └── viewOutgoingEvents.ts │ └── viewModel │ │ ├── path.ts │ │ ├── viewModel.ts │ │ ├── viewModelEvents.ts │ │ └── viewModelImpl.ts │ ├── common │ ├── common.ts │ ├── model.ts │ ├── node.ts │ └── style.ts │ ├── editor.tsx │ ├── engine │ ├── base │ │ ├── clock.ts │ │ ├── dom.ts │ │ ├── language.ts │ │ ├── type_id.ts │ │ └── unicode │ │ │ ├── character.ts │ │ │ ├── character_values.ts │ │ │ └── utf.ts │ ├── browser │ │ ├── render_tree_combiner.ts │ │ └── web_module.ts │ ├── css_parser │ │ ├── errorListener.ts │ │ ├── interpretor.ts │ │ ├── shorthandToLonghand.ts │ │ └── visitor.ts │ ├── cssom │ │ ├── calc_value.ts │ │ ├── cascade_precedence.ts │ │ ├── cascaded_style.ts │ │ ├── character_classification.ts │ │ ├── computed_style.ts │ │ ├── computed_style_data.ts │ │ ├── computed_style_declaration.ts │ │ ├── computed_style_utils.ts │ │ ├── declaration_data.ts │ │ ├── declared_style_data.ts │ │ ├── declared_style_declaration.ts │ │ ├── font_style_value.ts │ │ ├── font_weight_value.ts │ │ ├── initial_computed_style.ts │ │ ├── integer_value.ts │ │ ├── keyword_names.ts │ │ ├── keyword_value.ts │ │ ├── length_value.ts │ │ ├── matrix_function.ts │ │ ├── mutation_observer.ts │ │ ├── number_value.ts │ │ ├── percentage_value.ts │ │ ├── property_definitions.ts │ │ ├── property_list_value.ts │ │ ├── property_value.ts │ │ ├── property_value_visitor.ts │ │ ├── rgba_color_value.ts │ │ ├── rotate_function.ts │ │ ├── scale_function.ts │ │ ├── scoped_ref_list_value.ts │ │ ├── shadow_value.ts │ │ ├── specificity.ts │ │ ├── string_value.ts │ │ ├── style_declaration.ts │ │ ├── transform_function.ts │ │ ├── transform_function_list_value.ts │ │ ├── transform_function_visitor.ts │ │ ├── transform_property_value.ts │ │ ├── translate_function.ts │ │ ├── unicode_range_value.ts │ │ ├── url_value.ts │ │ └── viewport_size.ts │ ├── dom │ │ ├── abstract_range.ts │ │ ├── attach_context.ts │ │ ├── benchmark_stat_names.ts │ │ ├── character_data.ts │ │ ├── comment.ts │ │ ├── container_action.ts │ │ ├── container_node.ts │ │ ├── custom │ │ │ └── freehand_element.ts │ │ ├── directionality.ts │ │ ├── document.ts │ │ ├── document_timeline.ts │ │ ├── dom_exception.ts │ │ ├── element.ts │ │ ├── element_factory.ts │ │ ├── exception_code.ts │ │ ├── flat_tree_traversal.ts │ │ ├── font_cache.ts │ │ ├── font_face.ts │ │ ├── font_face_updater.ts │ │ ├── font_list.ts │ │ ├── html_anchor_element.ts │ │ ├── html_body_element.ts │ │ ├── html_br_element.ts │ │ ├── html_div_element.ts │ │ ├── html_element.ts │ │ ├── html_element_context.ts │ │ ├── html_em_element.ts │ │ ├── html_heading_element.ts │ │ ├── html_html_element.ts │ │ ├── html_image_element.ts │ │ ├── html_paragraph_element.ts │ │ ├── html_span_element.ts │ │ ├── html_strong_element.ts │ │ ├── node.ts │ │ ├── node_children_iterator.ts │ │ ├── node_descendants_iterator.ts │ │ ├── node_list.ts │ │ ├── node_list_live.ts │ │ ├── node_traversal.ts │ │ ├── performance.ts │ │ ├── performance_timing.ts │ │ ├── range.ts │ │ ├── range_boundary_point.ts │ │ ├── serializer.ts │ │ ├── text.ts │ │ ├── traversal_range.ts │ │ └── window.ts │ ├── editing │ │ ├── editing_utilities.ts │ │ └── selection.ts │ ├── geometry │ │ ├── dom_rect.ts │ │ └── dom_rect_read_only.ts │ ├── layout │ │ ├── anonymous_block_box.ts │ │ ├── base_direction.ts │ │ ├── benchmark_stat_names.ts │ │ ├── block_container_box.ts │ │ ├── block_formatting_block_container_box.ts │ │ ├── block_formatting_context.ts │ │ ├── block_level_replaced_box.ts │ │ ├── box.ts │ │ ├── box_generator.ts │ │ ├── container_box.ts │ │ ├── formatting_context.ts │ │ ├── initial_containing_block.ts │ │ ├── inline_container_box.ts │ │ ├── inline_formatting_context.ts │ │ ├── inline_level_replaced_box.ts │ │ ├── insets_layout_unit.ts │ │ ├── layout.ts │ │ ├── layout_boxes.ts │ │ ├── layout_manager.ts │ │ ├── layout_object.ts │ │ ├── layout_text.ts │ │ ├── layout_unit.ts │ │ ├── letterboxed_image.ts │ │ ├── line_box.ts │ │ ├── line_wrapping.ts │ │ ├── map_coordinates_flags.ts │ │ ├── paragraph.ts │ │ ├── point_layout_unit.ts │ │ ├── r_tree.ts │ │ ├── rect_layout_unit.ts │ │ ├── replaced_box.ts │ │ ├── selection_background.ts │ │ ├── size_layout_unit.ts │ │ ├── text_box.ts │ │ ├── used_style.ts │ │ ├── vector2d_layout_unit.ts │ │ └── white_space_processing.ts │ ├── loader │ │ ├── 354.jpg │ │ ├── decoder.ts │ │ ├── fetcher.ts │ │ ├── fetcher_cache.ts │ │ ├── fetcher_factory.ts │ │ ├── font │ │ │ ├── remote_typeface_cache.ts │ │ │ └── typeface_decoder.ts │ │ ├── image │ │ │ ├── image.ts │ │ │ ├── image_cache.ts │ │ │ ├── image_data_decoder.ts │ │ │ ├── image_decoder.ts │ │ │ ├── image_decoder_proxy.ts │ │ │ └── jpeg_image_decoder.ts │ │ ├── loader.ts │ │ ├── loader_factory.ts │ │ ├── loader_types.ts │ │ ├── net_fetcher.ts │ │ └── resource_cache.ts │ ├── math │ │ ├── insets.ts │ │ ├── insets_f.ts │ │ ├── matrix3_f.ts │ │ ├── point_base.ts │ │ ├── point_f.ts │ │ ├── quad_f.ts │ │ ├── rect.ts │ │ ├── rect_base.ts │ │ ├── rect_f.ts │ │ ├── size.ts │ │ ├── size_f.ts │ │ ├── transform_2d.ts │ │ ├── vector2d_f.ts │ │ └── vector3d_f.ts │ ├── render_tree │ │ ├── background.ts │ │ ├── border.ts │ │ ├── brush.ts │ │ ├── brush_visitor.ts │ │ ├── clear_rect_node.ts │ │ ├── color_rgba.ts │ │ ├── composition_node.ts │ │ ├── dump_render_tree_to_string.ts │ │ ├── font.ts │ │ ├── font_provider.ts │ │ ├── freehand_node.ts │ │ ├── glyph.ts │ │ ├── glyph_buffer.ts │ │ ├── image.ts │ │ ├── image_node.ts │ │ ├── matrix_transform_node.ts │ │ ├── node.ts │ │ ├── node_visitor.ts │ │ ├── path.ts │ │ ├── rect_node.ts │ │ ├── resource_provider.ts │ │ ├── rounded_corners.ts │ │ ├── shadow.ts │ │ ├── text_node.ts │ │ └── typeface.ts │ ├── renderer │ │ ├── backend │ │ │ └── render_target.ts │ │ ├── pipeline.ts │ │ ├── rasterizer │ │ │ ├── Roboto-Regular.ttf │ │ │ ├── SourceHanSansCN-Normal.ttf │ │ │ ├── common │ │ │ │ └── utils.ts │ │ │ ├── hardware_rasterizer.ts │ │ │ ├── rasterizer.ts │ │ │ ├── render_tree_node_visitor.ts │ │ │ ├── render_tree_node_visitor_draw_state.ts │ │ │ ├── sk_font.ts │ │ │ ├── sk_glyph_buffer.ts │ │ │ ├── sk_image.ts │ │ │ ├── sk_resource_provider.ts │ │ │ ├── sk_typeface.ts │ │ │ ├── skia │ │ │ │ ├── effects │ │ │ │ │ └── SkGradientShader.ts │ │ │ │ ├── sk_color.ts │ │ │ │ ├── sk_font.ts │ │ │ │ ├── sk_image.ts │ │ │ │ ├── sk_matrix.ts │ │ │ │ ├── sk_path.ts │ │ │ │ ├── sk_point.ts │ │ │ │ ├── sk_rect.ts │ │ │ │ ├── sk_scalar.ts │ │ │ │ └── sk_typeface.ts │ │ │ ├── software_image.ts │ │ │ ├── text_shaper.ts │ │ │ └── type_conversions.ts │ │ ├── renderer_module.ts │ │ ├── smoothed_value.ts │ │ ├── submission.ts │ │ └── submission_queue.ts │ └── web_animations │ │ └── animation_timeline.ts │ ├── env.d.ts │ ├── global.d.ts │ ├── index.html │ ├── index.ts │ ├── package.json │ ├── platform │ ├── canvas │ │ ├── browser │ │ │ └── canvasViews.ts │ │ └── common │ │ │ ├── canvas.ts │ │ │ └── canvasService.ts │ ├── commands │ │ └── common │ │ │ ├── commandService.ts │ │ │ └── commands.ts │ ├── configuration │ │ └── common │ │ │ ├── configuration.ts │ │ │ ├── configurationModels.ts │ │ │ ├── configurationRegistry.ts │ │ │ └── configurationService.ts │ ├── contextkey │ │ ├── browser │ │ │ └── contextKeyService.ts │ │ └── common │ │ │ ├── contextkey.ts │ │ │ └── contextkeys.ts │ ├── input │ │ ├── browser │ │ │ ├── event.ts │ │ │ └── inputService.ts │ │ └── common │ │ │ ├── input.ts │ │ │ └── inputService.ts │ ├── instantiation │ │ └── common │ │ │ ├── descriptors.ts │ │ │ ├── extensions.ts │ │ │ ├── graph.ts │ │ │ ├── instantiation.ts │ │ │ ├── instantiationService.ts │ │ │ └── serviceCollection.ts │ ├── keybinding │ │ ├── browser │ │ │ └── keybindingService.ts │ │ └── common │ │ │ ├── abstractKeybindingService.ts │ │ │ ├── baseResolvedKeybinding.ts │ │ │ ├── keybinding.ts │ │ │ ├── keybindingResolver.ts │ │ │ ├── keybindingsRegistry.ts │ │ │ ├── resolvedKeybindingItem.ts │ │ │ └── usLayoutResolvedKeybinding.ts │ ├── layout │ │ └── browser │ │ │ └── layoutService.ts │ ├── lifecycle │ │ ├── browser │ │ │ └── lifecycleService.ts │ │ └── common │ │ │ ├── lifecycle.ts │ │ │ └── lifecycleService.ts │ ├── log │ │ ├── browser │ │ │ └── console.ts │ │ └── common │ │ │ ├── bufferLog.ts │ │ │ └── log.ts │ ├── model │ │ └── common │ │ │ ├── helper.ts │ │ │ ├── location.ts │ │ │ ├── model.ts │ │ │ ├── modelChangeParticipant.ts │ │ │ ├── modelEvents.ts │ │ │ ├── modelImpl.ts │ │ │ ├── modelOperator.ts │ │ │ ├── modelService.ts │ │ │ └── operation │ │ │ └── nodes.ts │ ├── registry │ │ └── common │ │ │ ├── genericRegistry.ts │ │ │ └── platform.ts │ ├── runtime_enabled_features.ts │ ├── storage │ │ ├── browser │ │ │ └── storageService.ts │ │ └── common │ │ │ ├── storage.ts │ │ │ └── storageIpc.ts │ ├── tool │ │ ├── browser │ │ │ ├── baseTool.ts │ │ │ ├── toolController.ts │ │ │ └── toolService.ts │ │ └── common │ │ │ ├── tool.ts │ │ │ └── toolRegistry.ts │ ├── undoRedo │ │ └── common │ │ │ ├── undoRedo.ts │ │ │ └── undoRedoService.ts │ ├── workbenchLayout │ │ └── common │ │ │ └── workbenchLayout.ts │ └── workspace │ │ └── common │ │ └── workspace.ts │ ├── postcss.config.js │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── vite.config.ts │ └── workbench │ ├── browser │ ├── layout.ts │ ├── part.ts │ ├── parts │ │ ├── components │ │ │ └── IconButton.vue │ │ ├── editor │ │ │ ├── editorPart.ts │ │ │ ├── index.vue │ │ │ └── m.ts │ │ ├── globals.css │ │ ├── index.css │ │ ├── index.vue │ │ ├── injects.ts │ │ └── toolbar │ │ │ ├── EraserButton.vue │ │ │ ├── ImageButton.vue │ │ │ ├── PanButton.vue │ │ │ ├── PencilButton.vue │ │ │ ├── RectangleButton.vue │ │ │ ├── RedoButton.vue │ │ │ ├── SelectionButton.vue │ │ │ ├── TextButton.vue │ │ │ ├── UndoButton.vue │ │ │ └── index.vue │ ├── web.api.ts │ ├── web.factory.ts │ ├── web.main.ts │ └── workbench.ts │ ├── common │ └── contributions.ts │ └── contrib │ ├── action │ └── action.contributions.ts │ └── tool │ ├── brush │ ├── brushTool.ts │ └── utils.ts │ ├── eraser │ └── eraserTool.ts │ ├── line │ └── lineTool.ts │ ├── pan │ └── panTool.ts │ ├── pointer │ └── pointerTool.ts │ ├── rect │ └── rectTool.ts │ ├── select │ └── selectTool.ts │ ├── text │ ├── common.ts │ ├── editor │ │ ├── core │ │ │ ├── apply.ts │ │ │ ├── get-dirty-paths.ts │ │ │ ├── get-fragment.ts │ │ │ ├── index.ts │ │ │ ├── normalize-node.ts │ │ │ └── should-normalize.ts │ │ ├── create-editor.ts │ │ ├── editor │ │ │ ├── above.ts │ │ │ ├── add-mark.ts │ │ │ ├── after.ts │ │ │ ├── before.ts │ │ │ ├── delete-backward.ts │ │ │ ├── delete-forward.ts │ │ │ ├── delete-fragment.ts │ │ │ ├── edges.ts │ │ │ ├── element-read-only.ts │ │ │ ├── end.ts │ │ │ ├── first.ts │ │ │ ├── fragment.ts │ │ │ ├── get-void.ts │ │ │ ├── has-blocks.ts │ │ │ ├── has-inlines.ts │ │ │ ├── has-path.ts │ │ │ ├── has-texts.ts │ │ │ ├── index.ts │ │ │ ├── insert-break.ts │ │ │ ├── insert-node.ts │ │ │ ├── insert-soft-break.ts │ │ │ ├── insert-text.ts │ │ │ ├── is-block.ts │ │ │ ├── is-edge.ts │ │ │ ├── is-editor.ts │ │ │ ├── is-empty.ts │ │ │ ├── is-end.ts │ │ │ ├── is-normalizing.ts │ │ │ ├── is-start.ts │ │ │ ├── last.ts │ │ │ ├── leaf.ts │ │ │ ├── levels.ts │ │ │ ├── marks.ts │ │ │ ├── next.ts │ │ │ ├── node.ts │ │ │ ├── nodes.ts │ │ │ ├── normalize.ts │ │ │ ├── parent.ts │ │ │ ├── path-ref.ts │ │ │ ├── path-refs.ts │ │ │ ├── path.ts │ │ │ ├── point-ref.ts │ │ │ ├── point-refs.ts │ │ │ ├── point.ts │ │ │ ├── positions.ts │ │ │ ├── previous.ts │ │ │ ├── range-ref.ts │ │ │ ├── range-refs.ts │ │ │ ├── range.ts │ │ │ ├── remove-mark.ts │ │ │ ├── set-normalizing.ts │ │ │ ├── start.ts │ │ │ ├── string.ts │ │ │ ├── unhang-range.ts │ │ │ └── without-normalizing.ts │ │ ├── index.ts │ │ ├── interfaces │ │ │ ├── editor.ts │ │ │ ├── element.ts │ │ │ ├── index.ts │ │ │ ├── location.ts │ │ │ ├── node.ts │ │ │ ├── operation.ts │ │ │ ├── path-ref.ts │ │ │ ├── path.ts │ │ │ ├── point-ref.ts │ │ │ ├── point.ts │ │ │ ├── range-ref.ts │ │ │ ├── range.ts │ │ │ ├── scrubber.ts │ │ │ ├── text.ts │ │ │ └── transforms │ │ │ │ ├── general.ts │ │ │ │ ├── index.ts │ │ │ │ ├── node.ts │ │ │ │ ├── selection.ts │ │ │ │ └── text.ts │ │ ├── transforms-node │ │ │ ├── index.ts │ │ │ ├── insert-nodes.ts │ │ │ ├── lift-nodes.ts │ │ │ ├── merge-nodes.ts │ │ │ ├── move-nodes.ts │ │ │ ├── remove-nodes.ts │ │ │ ├── set-nodes.ts │ │ │ ├── split-nodes.ts │ │ │ ├── unset-nodes.ts │ │ │ ├── unwrap-nodes.ts │ │ │ └── wrap-nodes.ts │ │ ├── transforms-selection │ │ │ ├── collapse.ts │ │ │ ├── deselect.ts │ │ │ ├── index.ts │ │ │ ├── move.ts │ │ │ ├── select.ts │ │ │ ├── set-point.ts │ │ │ └── set-selection.ts │ │ ├── transforms-text │ │ │ ├── delete-text.ts │ │ │ ├── index.ts │ │ │ └── insert-fragment.ts │ │ ├── types │ │ │ ├── custom-types.ts │ │ │ ├── index.ts │ │ │ └── types.ts │ │ └── utils │ │ │ ├── deep-equal.ts │ │ │ ├── get-default-insert-location.ts │ │ │ ├── index.ts │ │ │ ├── match-path.ts │ │ │ ├── string.ts │ │ │ ├── types.ts │ │ │ └── weak-maps.ts │ ├── textTool.tsx │ ├── utils │ │ ├── dom.ts │ │ ├── key.ts │ │ └── weak-maps.ts │ └── view │ │ ├── commands.ts │ │ ├── common.ts │ │ ├── cursor │ │ ├── cursorUpdater.ts │ │ ├── textCursor.css │ │ └── textCursor.ts │ │ ├── editorInterface.ts │ │ ├── editorView.ts │ │ ├── editorViewController.ts │ │ ├── platform.ts │ │ └── type │ │ ├── textAreaHandler.css │ │ ├── textAreaHandler.ts │ │ ├── textAreaInput.ts │ │ └── textAreaState.ts │ └── tool.contributions.ts ├── screenshot.png ├── third_party ├── css-parser │ ├── CSS3.g4 │ ├── gen │ │ ├── inline.interp │ │ ├── inline.tokens │ │ ├── inlineLexer.interp │ │ ├── inlineLexer.tokens │ │ ├── inlineLexer.ts │ │ ├── inlineListener.ts │ │ ├── inlineParser.ts │ │ └── inlineVisitor.ts │ ├── inline.g4 │ └── package.json ├── icu │ ├── .gitignore │ ├── Dockerfile │ ├── Dockerfile.emsdk │ ├── Dockerfile.icu │ ├── build.sh │ ├── filters.json │ ├── icu.cc │ ├── icu.py │ ├── index.html │ ├── package.json │ ├── src │ │ ├── icu-binding.d.ts │ │ ├── icu-binding.js │ │ ├── icu-binding.wasm │ │ ├── icu-c.ts │ │ ├── index.ts │ │ └── main.ts │ ├── tsconfig.json │ ├── vite.config.ts │ └── yarn.lock └── skia │ ├── package.json │ ├── src │ └── index.ts │ └── tsconfig.json ├── tsconfig.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.github/workflows/demo.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | permissions: 8 | contents: write 9 | jobs: 10 | build-and-deploy: 11 | concurrency: ci-${{ github.ref }} 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v3 16 | 17 | - name: Setup 18 | uses: actions/setup-node@v3 19 | with: 20 | node-version: '18' 21 | cache: 'yarn' 22 | 23 | - name: Install 24 | run: yarn 25 | 26 | - name: Build 27 | run: yarn build 28 | 29 | - name: Deploy 30 | uses: JamesIves/github-pages-deploy-action@v4 31 | with: 32 | folder: packages/neditor/dist 33 | branch: gh-pages 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules/ 5 | 6 | # production 7 | /build 8 | dist 9 | 10 | # misc 11 | .DS_Store 12 | 13 | npm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | lerna-debug.log* 17 | 18 | .idea/ 19 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # neditor 2 | 3 | a rich text editor on a port of Cobalt to JavaScript aimed at running in Canvas. 4 | 5 | ## DEMO 6 | [waisiukei.github.io/neditor](https://waisiukei.github.io/neditor/) 7 | 8 | ![screenshot](./screenshot.png) 9 | 10 | ## Goals 11 | 12 | - Produce a rich text editor in pure JavaScript that supports rendering to WebGL/Canvas contexts. 13 | - Develop a framework for prototyping CSS Houdini, HTML elements and attributes. 14 | 15 | ## Contributing 16 | 17 | ### Getting around the code 18 | 19 | * `/packages/neditor/base` This is utils functions. 20 | * `/packages/neditor/canvas` It holds codes of canvas core. 21 | * `/packages/neditor/engine` This is a port of Cobalt to JavaScript. 22 | * `/packages/neditor/platform` Vscode platform features. 23 | * `/packages/neditor/workbench` User interface and functionality extensions.. 24 | * `/third_party/css-parser` This is a simple parser of HTML inline style. 25 | * `/third_party/icu` This is a port of ICU to Web. 26 | * `/third_party/skia` Loader of Skia/CanvasKit and its Typescript definations. 27 | 28 | ## Building 29 | 30 | ### Bootstrap 31 | 32 | ```shell script 33 | yarn 34 | ``` 35 | 36 | ### Dev 37 | 38 | ```shell script 39 | yarn start 40 | ``` 41 | 42 | ### Build 43 | 44 | ```shell script 45 | yarn build 46 | ``` 47 | 48 | ## License 49 | 50 | MIT License 51 | 52 | Copyright (c) 2020-2023 Waisiukei 53 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-2023 Waisiukei 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "neditor", 3 | "private": true, 4 | "scripts": { 5 | "dev": "cd ./packages/neditor && yarn run start", 6 | "build": "cd ./packages/neditor && yarn run build" 7 | }, 8 | "workspaces": [ 9 | "packages/*", 10 | "third_party/*" 11 | ], 12 | "publishConfig": { 13 | "access": "public" 14 | }, 15 | "dependencies": {} 16 | } 17 | -------------------------------------------------------------------------------- /packages/neditor/base/browser/animation.ts: -------------------------------------------------------------------------------- 1 | export class AnimationQueue { 2 | private _pending: number | null = null; 3 | push(runner: () => void) { 4 | this.clear(); 5 | this._pending = requestAnimationFrame(() => { 6 | runner(); 7 | this.clear(); 8 | }); 9 | } 10 | clear() { 11 | if (this._pending) { 12 | cancelAnimationFrame(this._pending); 13 | this._pending = null; 14 | } 15 | } 16 | dispose() { 17 | this.clear(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/neditor/base/browser/css.ts: -------------------------------------------------------------------------------- 1 | import { IMatrix } from '../common/geometry'; 2 | 3 | export function toFixedPercentage(val: number): string { 4 | return `${(val * 100).toFixed(2)}%`; 5 | } 6 | 7 | export function toFixedPx(val: number): string { 8 | return `${val.toFixed(2)}px`; 9 | } 10 | 11 | export const ZeroPercentage = '0%'; 12 | export const ZeroPX = '0px'; 13 | 14 | export function toPX(val: number): string { 15 | return `${val}px`; 16 | } 17 | 18 | export function toTramsform(mx: IMatrix): string { 19 | return `matrix(${mx.a}, ${mx.b}, ${mx.c}, ${mx.d}, ${mx.tx}, ${mx.ty})` 20 | } 21 | -------------------------------------------------------------------------------- /packages/neditor/base/browser/devicePixelRatio.ts: -------------------------------------------------------------------------------- 1 | export const devicePixelRatio = window.devicePixelRatio || 1; 2 | -------------------------------------------------------------------------------- /packages/neditor/base/browser/touch.ts: -------------------------------------------------------------------------------- 1 | export interface GestureEvent extends MouseEvent { 2 | initialTarget: EventTarget | undefined; 3 | translationX: number; 4 | translationY: number; 5 | pageX: number; 6 | pageY: number; 7 | tapCount: number; 8 | } 9 | -------------------------------------------------------------------------------- /packages/neditor/base/callback.ts: -------------------------------------------------------------------------------- 1 | export type Closure = () => void 2 | 3 | export type Callback = () => T 4 | export type Callback1 = (a1: A1) => T 5 | export type Callback2 = (a1: A1, a2: A2) => T 6 | -------------------------------------------------------------------------------- /packages/neditor/base/check.ts: -------------------------------------------------------------------------------- 1 | import { isNumber } from './common/type'; 2 | 3 | export function DCHECK(condition: unknown, ...msgs: any): asserts condition { 4 | if (isNumber(condition) ? false : !condition) { 5 | console.warn('!', ...msgs); 6 | } 7 | } 8 | export function CHECK(condition: unknown) {} 9 | -------------------------------------------------------------------------------- /packages/neditor/base/check_op.ts: -------------------------------------------------------------------------------- 1 | import { DCHECK } from './check'; 2 | 3 | export function DCHECK_GT(val1: number, val2: number) {} 4 | export function DCHECK_GE(val1: number, val2: number, ...msgs: string[]) { 5 | DCHECK(val1 >= val2); 6 | } 7 | export function DCHECK_LE(val1: number, val2: number) { 8 | DCHECK(val1 <= val2); 9 | } 10 | export function DCHECK_LT(val1: number, val2: number) {} 11 | export function DCHECK_EQ(vals: any, val2: any, ...args: string[]) {} 12 | export function DCHECK_NE(vals: any, val2: any, ...args: string[]) {} 13 | export function CHECK_NE(val1: number, val2: number) {} 14 | -------------------------------------------------------------------------------- /packages/neditor/base/common/arraybuffer.ts: -------------------------------------------------------------------------------- 1 | export function memcmpWithNums(lhs: Uint8Array, rhs: number[], offset: number = 0): boolean { 2 | for (let i = 0; i < rhs.length; i++) { 3 | if (lhs[i + offset] != rhs[i]) return false; 4 | } 5 | return true; 6 | } 7 | 8 | export function memcmpWithString(lhs: Uint8Array, rhs: string, offset: number = 0): boolean { 9 | return memcmpWithNums(lhs, rhs.split('').map(s => s.charCodeAt(0), offset)); 10 | } 11 | 12 | export function memcpy(dest: Uint8Array, src: Uint8Array, start: number, length: number) { 13 | for (let i = 0; i < length; i++) { 14 | dest[i + start] = src[i]; 15 | } 16 | } 17 | 18 | export function uint8ArrayToBuffer(array: Uint8Array): ArrayBuffer { 19 | return array.buffer.slice(array.byteOffset, array.byteLength + array.byteOffset); 20 | } 21 | 22 | export function typedArrayToBuffer(array: Uint8Array): ArrayBuffer { 23 | return array.buffer.slice(array.byteOffset, array.byteLength + array.byteOffset) 24 | } 25 | 26 | export type TypedArray = 27 | | Int8Array 28 | | Uint8Array 29 | | Uint8ClampedArray 30 | | Int16Array 31 | | Uint16Array 32 | | Int32Array 33 | | Uint32Array 34 | | Float32Array 35 | | Float64Array; 36 | -------------------------------------------------------------------------------- /packages/neditor/base/common/assert.ts: -------------------------------------------------------------------------------- 1 | /* --------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *-------------------------------------------------------------------------------------------- */ 5 | 6 | /** 7 | * Throws an error with the provided message if the provided value does not evaluate to a true Javascript value. 8 | */ 9 | export function ok(value?: unknown, message?: string) { 10 | if (!value) { 11 | throw new Error(message ? `Assertion failed (${message})` : 'Assertion Failed'); 12 | } 13 | } 14 | 15 | /** 16 | * 断言测试一个值是否为 true 17 | * @param {...any} args 18 | * @param message error message 19 | */ 20 | function assert(value: unknown, message?: string | Error): asserts value { 21 | if (!value) { 22 | throw message instanceof Error ? message : new Error(message); 23 | } 24 | } 25 | 26 | /** 27 | * 类似 assert, 但是 console.error(error) 而不是 throw error 28 | * @param value 29 | * @param message 30 | */ 31 | function should(value: unknown, message?: string | Error): asserts value { 32 | const error = message instanceof Error ? message : new Error(message); 33 | if (!value) { 34 | console.error(error); 35 | } 36 | } 37 | 38 | export { assert, should }; 39 | -------------------------------------------------------------------------------- /packages/neditor/base/common/bignumber.ts: -------------------------------------------------------------------------------- 1 | import Big from 'big.js'; 2 | 3 | /* 4 | * Return 1 if the value of this Big is greater than the value of Big y, 5 | * -1 if the value of this Big is less than the value of Big y, or 6 | * 0 if they have the same value. 7 | */ 8 | export function cmp(a: string, b: string): -1 | 0 | 1 { 9 | return (new Big(a)).cmp(new Big(b)) 10 | } 11 | 12 | export function plus(a: string, b: string): string { 13 | return (new Big(a)).plus(new Big(b)).toString() 14 | } 15 | 16 | export function devideBy2(val: string): string { 17 | return (new Big(val)).div(2).toString() 18 | } 19 | -------------------------------------------------------------------------------- /packages/neditor/base/common/collections.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * An interface for a JavaScript object that 3 | * acts a dictionary. The keys are strings. 4 | */ 5 | export type IStringDictionary = Record; 6 | 7 | export function compareCollection(set1: Set, set2: Set): boolean { 8 | if (set1.size !== set2.size) return false; 9 | for (let key of set1) { 10 | if (!set2.has(key)) return false; 11 | } 12 | return true; 13 | } 14 | -------------------------------------------------------------------------------- /packages/neditor/base/common/finalization.ts: -------------------------------------------------------------------------------- 1 | import { IDisposable } from './lifecycle'; 2 | 3 | class FinalizationHolder implements IDisposable { 4 | constructor(public toDispose: IDisposable) {} 5 | 6 | dispose() { 7 | this.toDispose.dispose(); 8 | } 9 | } 10 | 11 | export function registerFinalizable(obj: IDisposable) { 12 | finalizationRegistry.register(obj, new FinalizationHolder(obj)); 13 | } 14 | 15 | const finalizationRegistry = new FinalizationRegistry((heldValue: IDisposable) => { 16 | heldValue.dispose(); 17 | }); 18 | 19 | -------------------------------------------------------------------------------- /packages/neditor/base/common/format.ts: -------------------------------------------------------------------------------- 1 | export function toDegree(num: number): string { 2 | return `${num}°`; 3 | } 4 | -------------------------------------------------------------------------------- /packages/neditor/base/common/functional.ts: -------------------------------------------------------------------------------- 1 | export function noop() {} 2 | 3 | export function once(this: unknown, fn: T): T { 4 | const _this = this; 5 | let didCall = false; 6 | let result: unknown; 7 | 8 | return function () { 9 | if (didCall) { 10 | return result; 11 | } 12 | 13 | didCall = true; 14 | // eslint-disable-next-line prefer-rest-params 15 | result = fn.apply(_this, arguments); 16 | 17 | return result; 18 | } as unknown as T; 19 | } 20 | -------------------------------------------------------------------------------- /packages/neditor/base/common/lodTransformBase.ts: -------------------------------------------------------------------------------- 1 | export function lodToInvScale(levelOfDetail: number) { 2 | return 1 << Math.max(0, levelOfDetail); 3 | } 4 | -------------------------------------------------------------------------------- /packages/neditor/base/common/marshallingIds.ts: -------------------------------------------------------------------------------- 1 | /* --------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *-------------------------------------------------------------------------------------------- */ 5 | 6 | export const enum MarshalledId { 7 | Uri = 1, 8 | Regexp, 9 | ScmResource, 10 | ScmResourceGroup, 11 | ScmProvider, 12 | CommentController, 13 | CommentThread, 14 | CommentThreadReply, 15 | CommentNode, 16 | CommentThreadNode, 17 | TimelineActionContext, 18 | NotebookCellActionContext, 19 | TestItemContext, 20 | Date, 21 | } 22 | -------------------------------------------------------------------------------- /packages/neditor/base/common/math.ts: -------------------------------------------------------------------------------- 1 | const pi = Math.PI; 2 | 3 | // -180 ~ 180 4 | export function radiansToDegrees(radians: number) { 5 | return radians * (180 / pi); 6 | } 7 | 8 | export function radiansToDegreesClockwise(radians: number) { 9 | let val = radians * (180 / pi); 10 | return val < 0 ? (val + 360) : val; 11 | } 12 | 13 | export function degreesToRadians(degrees: number): number { 14 | return degrees * pi / 180; 15 | } 16 | 17 | export const rotate = ( 18 | x1: number, 19 | y1: number, 20 | x2: number, 21 | y2: number, 22 | angle: number, 23 | ): [number, number] => 24 | // 𝑎′𝑥=(𝑎𝑥−𝑐𝑥)cos𝜃−(𝑎𝑦−𝑐𝑦)sin𝜃+𝑐𝑥 25 | // 𝑎′𝑦=(𝑎𝑥−𝑐𝑥)sin𝜃+(𝑎𝑦−𝑐𝑦)cos𝜃+𝑐𝑦. 26 | // https://math.stackexchange.com/questions/2204520/how-do-i-rotate-a-line-segment-in-a-specific-point-on-the-line 27 | [ 28 | (x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle) + x2, 29 | (x1 - x2) * Math.sin(angle) + (y1 - y2) * Math.cos(angle) + y2, 30 | ]; 31 | 32 | -------------------------------------------------------------------------------- /packages/neditor/base/common/notreached.ts: -------------------------------------------------------------------------------- 1 | export function NOTREACHED(...msgs: string[]): never { 2 | if (msgs.length) { 3 | console.error(...msgs); 4 | } 5 | throw new Error('not reached'); 6 | } 7 | 8 | export function NOTIMPLEMENTED(...msgs: string[]): never { 9 | if (msgs.length) { 10 | console.error(...msgs); 11 | } 12 | throw new Error('not implemented'); 13 | } 14 | -------------------------------------------------------------------------------- /packages/neditor/base/common/number.ts: -------------------------------------------------------------------------------- 1 | import { isString } from './type'; 2 | 3 | export function toPercentage(val: number): string { 4 | return `${(val * 100)}%`; 5 | } 6 | 7 | export function getFloat(str: string): number { 8 | return parseFloat(str || '0'); 9 | } 10 | 11 | export function getInt(str: string): number { 12 | return parseInt(str || '0', 10); 13 | } 14 | 15 | export function isPercentage(str: string): boolean { 16 | return isString(str) && str.endsWith('%'); 17 | } 18 | 19 | export function nearly(a: number, b: number, threshold = 0.1): boolean { 20 | return Math.abs(a - b) < threshold; 21 | } 22 | 23 | export function clamp(min: number, val: number, max: number): number { 24 | return Math.min(Math.max(val, min), max); 25 | } 26 | 27 | export function castInt(val: number): number { 28 | return Math.round(val); 29 | } 30 | -------------------------------------------------------------------------------- /packages/neditor/base/common/product.ts: -------------------------------------------------------------------------------- 1 | export interface IProductConfiguration { 2 | readonly version: string; 3 | readonly date?: string; 4 | readonly quality?: string; 5 | readonly commit?: string; 6 | 7 | readonly nameShort: string; 8 | readonly nameLong: string; 9 | 10 | // readonly win32AppUserModelId?: string; 11 | // readonly win32MutexName?: string; 12 | // readonly applicationName: string; 13 | // readonly embedderIdentifier?: string; 14 | // 15 | readonly urlProtocol: string; 16 | // readonly dataFolderName: string; // location for extensions (e.g. ~/.vscode-insiders) 17 | 18 | // readonly downloadUrl?: string; 19 | // readonly updateUrl?: string; 20 | // readonly webEndpointUrlTemplate?: string; 21 | // readonly webviewContentExternalBaseUrlTemplate?: string; 22 | // readonly target?: string; 23 | // 24 | // readonly settingsSearchBuildId?: number; 25 | // readonly settingsSearchUrl?: string; 26 | // 27 | // readonly checksums?: { [path: string]: string }; 28 | // readonly checksumFailMoreInfoUrl?: string; 29 | // 30 | // readonly portable?: string; 31 | 32 | // readonly darwinUniversalAssetId?: string; 33 | } 34 | -------------------------------------------------------------------------------- /packages/neditor/base/common/reactivity/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | ref, 3 | shallowRef, 4 | isRef, 5 | toRef, 6 | toRefs, 7 | unref, 8 | proxyRefs, 9 | customRef, 10 | triggerRef, 11 | type Ref, 12 | type ToRef, 13 | type ToRefs, 14 | type UnwrapRef, 15 | type ShallowUnwrapRef, 16 | type RefUnwrapBailTypes, 17 | type CustomRefFactory 18 | } from './ref' 19 | export { 20 | reactive, 21 | isReactive, 22 | isReadonly, 23 | isProxy, 24 | toRaw, 25 | ReactiveFlags /* @remove */, 26 | type UnwrapNestedRefs 27 | } from './reactive' 28 | 29 | export { 30 | effect, 31 | stop, 32 | trigger, 33 | track, 34 | enableTracking, 35 | pauseTracking, 36 | resetTracking, 37 | ITERATE_KEY, 38 | ReactiveEffect, 39 | type ReactiveEffectRunner, 40 | type ReactiveEffectOptions, 41 | type EffectScheduler, 42 | type DebuggerOptions, 43 | type DebuggerEvent, 44 | type DebuggerEventExtraInfo 45 | } from './effect' 46 | export { 47 | effectScope, 48 | EffectScope, 49 | getCurrentScope, 50 | onScopeDispose 51 | } from './effectScope' 52 | export { 53 | TrackOpTypes /* @remove */, 54 | TriggerOpTypes /* @remove */ 55 | } from './operations' 56 | export { 57 | begin, 58 | end, 59 | setOnEnd, 60 | } from './patch' 61 | -------------------------------------------------------------------------------- /packages/neditor/base/common/reactivity/operations.ts: -------------------------------------------------------------------------------- 1 | // using literal strings instead of numbers so that it's easier to inspect 2 | // debugger events 3 | 4 | export const enum TrackOpTypes { 5 | GET = 'get', 6 | HAS = 'has', 7 | ITERATE = 'iterate', 8 | } 9 | 10 | export const enum TriggerOpTypes { 11 | SET = 'set', 12 | ADD = 'add', 13 | DELETE = 'delete', 14 | CLEAR = 'clear', 15 | } 16 | -------------------------------------------------------------------------------- /packages/neditor/base/common/reactivity/patch.ts: -------------------------------------------------------------------------------- 1 | const enum EditingMode { 2 | none, 3 | update, 4 | undoRedo, 5 | } 6 | 7 | let editingMode = EditingMode.none; 8 | 9 | export function isMutating(): boolean { 10 | return editingMode !== EditingMode.none; 11 | } 12 | 13 | export function begin() { 14 | editingMode = EditingMode.update; 15 | } 16 | 17 | const onEndCallbacks: Array<() => void> = []; 18 | export function end() { 19 | onEndCallbacks.forEach(fn => fn()); 20 | editingMode = EditingMode.none; 21 | } 22 | 23 | export function setOnEnd(fn: () => void) { 24 | onEndCallbacks.push(fn); 25 | } 26 | -------------------------------------------------------------------------------- /packages/neditor/base/common/reactivity/scheduler.ts: -------------------------------------------------------------------------------- 1 | export interface SchedulerJob extends Function { 2 | id?: number; 3 | active?: boolean; 4 | computed?: boolean; 5 | /** 6 | * Indicates whether the effect is allowed to recursively trigger itself 7 | * when managed by the scheduler. 8 | * 9 | * By default, a job cannot trigger itself because some built-in method calls, 10 | * e.g. Array.prototype.push actually performs reads as well (#1740) which 11 | * can lead to confusing infinite loops. 12 | * The allowed cases are component update functions and watch callbacks. 13 | * Component update functions may update child component props, which in turn 14 | * trigger flush: "pre" watch callbacks that mutates state that the parent 15 | * relies on (#1801). Watch callbacks doesn't track its dependencies so if it 16 | * triggers itself again, it's likely intentional and it is the user's 17 | * responsibility to perform recursive state mutation that eventually 18 | * stabilizes (#1727). 19 | */ 20 | allowRecurse?: boolean; 21 | } 22 | -------------------------------------------------------------------------------- /packages/neditor/base/common/reactivity/warning.ts: -------------------------------------------------------------------------------- 1 | export function warn(msg: string, ...args: any[]) { 2 | console.warn(`[Vue warn] ${msg}`, ...args); 3 | } 4 | -------------------------------------------------------------------------------- /packages/neditor/base/common/stopwatch.ts: -------------------------------------------------------------------------------- 1 | /* --------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *-------------------------------------------------------------------------------------------- */ 5 | 6 | import { globals } from '@neditor/core/base/common/platform'; 7 | 8 | const hasPerformanceNow = globals.performance && typeof globals.performance.now === 'function'; 9 | 10 | export class StopWatch { 11 | private _highResolution: boolean; 12 | private _startTime: number; 13 | private _stopTime: number; 14 | 15 | public static create(highResolution = true): StopWatch { 16 | return new StopWatch(highResolution); 17 | } 18 | 19 | constructor(highResolution: boolean) { 20 | this._highResolution = hasPerformanceNow && highResolution; 21 | this._startTime = this._now(); 22 | this._stopTime = -1; 23 | } 24 | 25 | public stop(): void { 26 | this._stopTime = this._now(); 27 | } 28 | 29 | public elapsed(): number { 30 | if (this._stopTime !== -1) { 31 | return this._stopTime - this._startTime; 32 | } 33 | return this._now() - this._startTime; 34 | } 35 | 36 | private _now(): number { 37 | return this._highResolution ? globals.performance.now() : Date.now(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/neditor/base/common/symbols.ts: -------------------------------------------------------------------------------- 1 | export const MicrotaskDelay = Symbol('MicrotaskDelay'); 2 | -------------------------------------------------------------------------------- /packages/neditor/base/common/typescript.ts: -------------------------------------------------------------------------------- 1 | export type ValueOf = T[keyof T]; 2 | export type EnumToString = T extends `${infer S & string}` ? S : never; 3 | export type EnumValues = ValueOf<{ [K in T]: EnumToString }>; 4 | export type EnumAndLiteral = T | EnumValues; 5 | 6 | export type RequiredKeys = { 7 | [K in keyof T]-?: 8 | ({} extends { [P in K]: T[K] } ? never : K) 9 | }[keyof T] 10 | 11 | // export type Required = { 12 | // [P in keyof T]-?: T[P] 13 | // } 14 | 15 | export type OptionalKeys = { 16 | [K in keyof T]-?: 17 | ({} extends { [P in K]: T[K] } ? K : never) 18 | }[keyof T] 19 | 20 | export type ExcludeOptionalProps = Pick> 21 | 22 | export type Optional = T | undefined 23 | 24 | export type Nullable = T | null 25 | 26 | export type Ptr = T | undefined 27 | 28 | export type Constructor = new (...args: any[]) => {}; 29 | 30 | export interface ConstructorSignature { 31 | new(...args: any[]): T; 32 | } 33 | 34 | export type AccessorCallback = (val: T) => R 35 | -------------------------------------------------------------------------------- /packages/neditor/base/logging.ts: -------------------------------------------------------------------------------- 1 | export const INFO = 0; 2 | export const WARNING = 1; 3 | export const ERROR = 2; 4 | export const FATEL = 3; 5 | export function DLOG(level: number, ...msgs: Array) { 6 | console.log(...msgs); 7 | } 8 | export function LOG(level: number, ...msgs: Array) { 9 | console.log(...msgs); 10 | } 11 | export function DLOG_IF(level: number, condition: boolean, ...msgs: Array) { 12 | if (condition) { 13 | console.log(msgs); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/neditor/base/nls/nls.ts: -------------------------------------------------------------------------------- 1 | export interface LocalizeInfo { 2 | key: string; 3 | comment: string[]; 4 | } 5 | 6 | const _isPseudo = false; 7 | 8 | function format(message: string, args: any[]): string { 9 | let result: string; 10 | 11 | if (_isPseudo) { 12 | // FF3B and FF3D is the Unicode zenkaku representation for [ and ] 13 | message = '\uFF3B' + message.replace(/[aouei]/g, '$&$&') + '\uFF3D'; 14 | } 15 | 16 | if (args.length === 0) { 17 | result = message; 18 | } else { 19 | result = message.replace(/\{(\d+)\}/g, (match, rest) => { 20 | const index = rest[0]; 21 | return typeof args[index] !== 'undefined' ? args[index] : match; 22 | }); 23 | } 24 | return result; 25 | } 26 | 27 | export function localize(key: string | LocalizeInfo, message: string, ...args: any[]): string { 28 | return format(message, args); 29 | } 30 | 31 | const nls = { 32 | localize, 33 | }; 34 | 35 | export default nls; 36 | -------------------------------------------------------------------------------- /packages/neditor/base/numerics/clamped_math.ts: -------------------------------------------------------------------------------- 1 | export function ClampMul(x: number, y: number): number { 2 | return x * y; 3 | } 4 | 5 | export function ClampSub(x: number, y: number): number { 6 | return x - y; 7 | } 8 | 9 | export function ClampAdd(x: number, y: number): number { 10 | return x + y; 11 | } 12 | -------------------------------------------------------------------------------- /packages/neditor/base/timer/elapsedTimer.ts: -------------------------------------------------------------------------------- 1 | export class ElapsedTimer { 2 | private startTime = 0 3 | constructor() { 4 | } 5 | start() { 6 | this.startTime = Date.now() 7 | } 8 | elapsed() { 9 | return Date.now() - this.startTime 10 | } 11 | restart() { 12 | this.startTime = Date.now() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/neditor/base/timer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "strict": true, 5 | "lib": [ 6 | "dom", 7 | "esnext" 8 | ] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/neditor/base/trace_event/common/trace_event_common.ts: -------------------------------------------------------------------------------- 1 | let console = { 2 | log(...args: any[]) { 3 | }, 4 | time(...args: any[]) { 5 | }, 6 | timeEnd(...args: any[]) { 7 | }, 8 | }; 9 | export function TRACE_EVENT(category_group: string, ...args: any[]) { 10 | console.log(category_group, ...args); 11 | } 12 | export function TRACE_EVENT0(category_group: string, name: string) { 13 | console.log(category_group, name); 14 | } 15 | export function TRACE_EVENT1(category_group: string, name: string, arg1: string, arg2: string) { 16 | console.log(category_group, name, arg1, arg2); 17 | } 18 | export function TRACE_EVENT2(category_group: string, name: string, arg1: string, arg2: unknown, arg3: string, arg4: unknown) {} 19 | 20 | export function TRACE_EVENT_BEGIN0(category_group: string, name: string) { 21 | let label = category_group + ' ' + name; 22 | console.time(label); 23 | } 24 | 25 | export function TRACE_EVENT_END0(category_group: string, name: string) { 26 | let label = category_group + ' ' + name; 27 | console.timeEnd(label); 28 | } 29 | 30 | export function getFunctionName(klass: any, func: Function): string { 31 | return `${Reflect.get(klass, 'name')}::${Reflect.get(func, 'name')}`; 32 | } 33 | -------------------------------------------------------------------------------- /packages/neditor/canvas/canvas/browser.ts: -------------------------------------------------------------------------------- 1 | import { EnumAndLiteral } from '@neditor/core/base/common/typescript'; 2 | import { Iterable } from '@neditor/core/base/common/iterator'; 3 | import some = Iterable.some; 4 | import { PathInfo } from '@neditor/core/platform/input/browser/event'; 5 | 6 | export const enum MouseTargetType { 7 | /** 8 | * Mouse is on top of an unknown element. 9 | */ 10 | Unknown, 11 | /** 12 | * 画布 13 | */ 14 | Canvas, 15 | } 16 | 17 | -------------------------------------------------------------------------------- /packages/neditor/canvas/canvas/canvasContextKeys.ts: -------------------------------------------------------------------------------- 1 | import { RawContextKey } from '../../platform/contextkey/common/contextkey'; 2 | import { HitTestLevel } from '../../platform/input/common/input'; 3 | 4 | export namespace CanvasContextKeys { 5 | export const hitTestLevel = new RawContextKey('hitTestLevel', HitTestLevel.BlockBox); 6 | export const isEditingText = new RawContextKey('isEditingText', false); 7 | } 8 | -------------------------------------------------------------------------------- /packages/neditor/canvas/canvas/hitTest.ts: -------------------------------------------------------------------------------- 1 | import { getCommonBounds } from '../element/bounds'; 2 | import { CanvasElement } from '../element/types'; 3 | 4 | export function isHittingCommonBoundingBoxOfSelectedElements( 5 | point: Readonly<{ x: number; y: number }>, 6 | selectedElements: readonly CanvasElement[], 7 | zoom: number, 8 | ): boolean { 9 | if (selectedElements.length < 2) { 10 | return false; 11 | } 12 | 13 | // How many pixels off the shape boundary we still consider a hit 14 | const threshold = 10 / zoom; 15 | const [x1, y1, x2, y2] = getCommonBounds(selectedElements); 16 | return ( 17 | point.x > x1 - threshold && 18 | point.x < x2 + threshold && 19 | point.y > y1 - threshold && 20 | point.y < y2 + threshold 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /packages/neditor/canvas/canvasCommon/scheduler.ts: -------------------------------------------------------------------------------- 1 | const enum EditingMode { 2 | none, 3 | update, 4 | undoRedo, 5 | } 6 | 7 | let editingMode = EditingMode.none; 8 | 9 | export function isMutating(): boolean { 10 | return editingMode !== EditingMode.none; 11 | } 12 | 13 | export function begin() { 14 | editingMode = EditingMode.update; 15 | } 16 | 17 | const onEndCallbacks: Array<() => void> = []; 18 | export function end() { 19 | onEndCallbacks.forEach(fn => fn()); 20 | editingMode = EditingMode.none; 21 | } 22 | 23 | export function setOnEnd(fn: () => void) { 24 | onEndCallbacks.push(fn); 25 | } 26 | -------------------------------------------------------------------------------- /packages/neditor/canvas/element/textElement.ts: -------------------------------------------------------------------------------- 1 | // export const getContainerElement = ( 2 | // element: 3 | // | (ExcalidrawElement & { 4 | // containerId: ExcalidrawElement["id"] | null; 5 | // }) 6 | // | null, 7 | // ) => { 8 | // if (!element) { 9 | // return null; 10 | // } 11 | // if (element.containerId) { 12 | // return Scene.getScene(element)?.getElement(element.containerId) || null; 13 | // } 14 | // return null; 15 | // }; 16 | export {} 17 | -------------------------------------------------------------------------------- /packages/neditor/canvas/element/typeCheck.ts: -------------------------------------------------------------------------------- 1 | import { NodeType } from '../../common/node'; 2 | import { getTypeAttr } from '../viewModel/path'; 3 | import { CanvasElement } from './types'; 4 | 5 | export function isTextElement(el: CanvasElement) { 6 | return getTypeAttr(el) === NodeType.Text; 7 | } 8 | export function isBlockElement(el: CanvasElement) { 9 | return getTypeAttr(el) === NodeType.Block; 10 | } 11 | export function isImageElement(el: CanvasElement) { 12 | return getTypeAttr(el) === NodeType.Image; 13 | } 14 | -------------------------------------------------------------------------------- /packages/neditor/canvas/element/types.ts: -------------------------------------------------------------------------------- 1 | import { Element } from '../../engine/dom/element'; 2 | 3 | export type Point = [number, number]; 4 | 5 | export type CanvasElement = Element 6 | -------------------------------------------------------------------------------- /packages/neditor/canvas/gadirections.ts: -------------------------------------------------------------------------------- 1 | import * as GA from "./ga"; 2 | import { Line, Direction, Point } from "./ga"; 3 | 4 | /** 5 | * A direction is stored as an array `[0, 0, 0, 0, y, x, 0, 0]` representing 6 | * vector `(x, y)`. 7 | */ 8 | 9 | export const from = (point: Point): Point => [ 10 | 0, 11 | 0, 12 | 0, 13 | 0, 14 | point[4], 15 | point[5], 16 | 0, 17 | 0, 18 | ]; 19 | 20 | export const fromTo = (from: Point, to: Point): Direction => 21 | GA.inormalized([0, 0, 0, 0, to[4] - from[4], to[5] - from[5], 0, 0]); 22 | 23 | export const orthogonal = (direction: Direction): Direction => 24 | GA.inormalized([0, 0, 0, 0, -direction[5], direction[4], 0, 0]); 25 | 26 | export const orthogonalToLine = (line: Line): Direction => GA.mul(line, GA.I); 27 | -------------------------------------------------------------------------------- /packages/neditor/canvas/gapoints.ts: -------------------------------------------------------------------------------- 1 | import * as GA from "./ga"; 2 | import * as GALine from "./galines"; 3 | import { Point, Line, join } from "./ga"; 4 | 5 | export const from = ([x, y]: readonly [number, number]): Point => [ 6 | 0, 7 | 0, 8 | 0, 9 | 0, 10 | y, 11 | x, 12 | 1, 13 | 0, 14 | ]; 15 | 16 | export const toTuple = (point: Point): [number, number] => [point[5], point[4]]; 17 | 18 | export const abs = (point: Point): Point => [ 19 | 0, 20 | 0, 21 | 0, 22 | 0, 23 | Math.abs(point[4]), 24 | Math.abs(point[5]), 25 | 1, 26 | 0, 27 | ]; 28 | 29 | export const intersect = (line1: Line, line2: Line): Point => 30 | GA.normalized(GA.meet(line1, line2)); 31 | 32 | // Projects `point` onto the `line`. 33 | // The returned point is the closest point on the `line` to the `point`. 34 | export const project = (point: Point, line: Line): Point => 35 | intersect(GALine.orthogonal(line, point), line); 36 | 37 | export const distance = (point1: Point, point2: Point): number => 38 | GA.norm(join(point1, point2)); 39 | 40 | export const distanceToLine = (point: Point, line: Line): number => 41 | GA.joinScalar(point, line); 42 | -------------------------------------------------------------------------------- /packages/neditor/canvas/gatransforms.ts: -------------------------------------------------------------------------------- 1 | import * as GA from "./ga"; 2 | import { Line, Direction, Point, Transform } from "./ga"; 3 | import * as GADirection from "./gadirections"; 4 | 5 | /** 6 | * TODO: docs 7 | */ 8 | 9 | export const rotation = (pivot: Point, angle: number): Transform => 10 | GA.add(GA.mul(pivot, Math.sin(angle / 2)), Math.cos(angle / 2)); 11 | 12 | export const translation = (direction: Direction): Transform => [ 13 | 1, 14 | 0, 15 | 0, 16 | 0, 17 | -(0.5 * direction[5]), 18 | 0.5 * direction[4], 19 | 0, 20 | 0, 21 | ]; 22 | 23 | export const translationOrthogonal = ( 24 | direction: Direction, 25 | distance: number, 26 | ): Transform => { 27 | const scale = 0.5 * distance; 28 | return [1, 0, 0, 0, scale * direction[4], scale * direction[5], 0, 0]; 29 | }; 30 | 31 | export const translationAlong = (line: Line, distance: number): Transform => 32 | GA.add(GA.mul(GADirection.orthogonalToLine(line), 0.5 * distance), 1); 33 | 34 | export const compose = (motor1: Transform, motor2: Transform): Transform => 35 | GA.mul(motor2, motor1); 36 | 37 | export const apply = ( 38 | motor: Transform, 39 | nvector: Point | Direction | Line, 40 | ): Point | Direction | Line => 41 | GA.normalized(GA.mul(GA.mul(motor, nvector), GA.reverse(motor))); 42 | -------------------------------------------------------------------------------- /packages/neditor/canvas/scene/comparisons.ts: -------------------------------------------------------------------------------- 1 | import { CanvasElement } from '../element/types'; 2 | 3 | export const getElementAtPosition = ( 4 | elements: readonly CanvasElement[], 5 | isAtPositionFn: (element: CanvasElement) => boolean, 6 | ) => { 7 | let hitElement = null; 8 | // We need to to hit testing from front (end of the array) to back (beginning of the array) 9 | // because array is ordered from lower z-index to highest and we want element z-index 10 | // with higher z-index 11 | for (let index = elements.length - 1; index >= 0; --index) { 12 | const element = elements[index]; 13 | // if (element.isDeleted) { 14 | // continue; 15 | // } 16 | if (isAtPositionFn(element)) { 17 | hitElement = element; 18 | break; 19 | } 20 | } 21 | 22 | return hitElement; 23 | }; 24 | 25 | export const getElementsAtPosition = ( 26 | elements: readonly CanvasElement[], 27 | isAtPositionFn: (element: CanvasElement) => boolean, 28 | ) => { 29 | // The parameter elements comes ordered from lower z-index to higher. 30 | // We want to preserve that order on the returned array. 31 | return elements.filter( 32 | (element) => isAtPositionFn(element), 33 | ); 34 | }; 35 | -------------------------------------------------------------------------------- /packages/neditor/canvas/view/controller/pointerHandler.ts: -------------------------------------------------------------------------------- 1 | import { IDisposable } from '@neditor/core/base/common/lifecycle'; 2 | import { IPointerHandlerHelper, MouseHandler } from './mouseHandler'; 3 | import { ViewController } from '../viewController'; 4 | 5 | export class PointerHandler implements IDisposable { 6 | private handler: MouseHandler | null; 7 | 8 | constructor(private viewController: ViewController, private viewHelper: IPointerHandlerHelper) { 9 | // if ((platform.isIOS && BrowserFeatures.pointerEvents)) { 10 | // this.handler = this._register(new PointerEventHandler(context, viewController, viewHelper)); 11 | // } else if (window.TouchEvent) { 12 | // this.handler = this._register(new TouchHandler(context, viewController, viewHelper)); 13 | // } else { 14 | // this.handler = this._register(new MouseHandler(viewController, viewHelper)); 15 | this.handler = new MouseHandler(viewController, viewHelper); 16 | // } 17 | } 18 | 19 | dispose() { 20 | if (this.handler) { 21 | this.handler.dispose(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/neditor/canvas/view/parts/canvas/adapter/app.ts: -------------------------------------------------------------------------------- 1 | import { reactive } from '@vue/reactivity'; 2 | import { Block } from './block'; 3 | import { Directive } from './directives'; 4 | import { createContext } from './context'; 5 | import { toDisplayString } from './directives/text'; 6 | import { Element } from '../../../../../engine/dom/element'; 7 | 8 | export const createApp = (initialData = {}) => { 9 | // root context 10 | const ctx = createContext(); 11 | // global internal helpers 12 | Object.assign(initialData, { 13 | $s: toDisplayString, 14 | }); 15 | ctx.scope = reactive(initialData); 16 | 17 | let rootBlocks: Block[]; 18 | 19 | return { 20 | directive(name: string, def?: Directive) { 21 | if (def) { 22 | ctx.dirs[name] = def; 23 | return this; 24 | } else { 25 | return ctx.dirs[name]; 26 | } 27 | }, 28 | 29 | mount(el: Element) { 30 | let roots = [el]; 31 | rootBlocks = roots.map((el) => new Block(el, ctx, true)); 32 | return this; 33 | }, 34 | 35 | unmount() { 36 | rootBlocks.forEach((block) => block.teardown()); 37 | } 38 | }; 39 | }; 40 | -------------------------------------------------------------------------------- /packages/neditor/canvas/view/parts/canvas/adapter/directives/html.ts: -------------------------------------------------------------------------------- 1 | import { Directive } from '.' 2 | import { NOTIMPLEMENTED } from "../../../../../../base/common/notreached"; 3 | 4 | export const html: Directive = ({ el, get, effect }) => { 5 | effect(() => { 6 | NOTIMPLEMENTED() 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /packages/neditor/canvas/view/parts/canvas/adapter/directives/index.ts: -------------------------------------------------------------------------------- 1 | import { Context } from '../context' 2 | import { effect as rawEffect } from '@vue/reactivity'; 3 | import { bind } from './bind' 4 | import { show } from './show' 5 | import { text } from './text' 6 | import { html } from './html' 7 | import { model } from './model' 8 | import { Element } from "../../../../../../engine/dom/element"; 9 | 10 | export interface Directive { 11 | (ctx: DirectiveContext): (() => void) | void 12 | } 13 | 14 | export interface DirectiveContext { 15 | el: T 16 | get: (exp?: string) => any 17 | effect: typeof rawEffect 18 | exp: string 19 | arg?: string 20 | modifiers?: Record 21 | ctx: Context 22 | } 23 | 24 | export const builtInDirectives: Record> = { 25 | bind, 26 | show, 27 | text, 28 | html, 29 | model, 30 | } 31 | -------------------------------------------------------------------------------- /packages/neditor/canvas/view/parts/canvas/adapter/directives/show.ts: -------------------------------------------------------------------------------- 1 | import { Directive } from '.' 2 | 3 | export const show: Directive = ({ el, get, effect }) => { 4 | const initialDisplay = el.style.display 5 | effect(() => { 6 | el.style.display = get() ? initialDisplay : 'none' 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /packages/neditor/canvas/view/parts/canvas/adapter/directives/text.ts: -------------------------------------------------------------------------------- 1 | import { isObject } from '@vue/shared'; 2 | import { Directive } from '.'; 3 | import { DCHECK } from '../../../../../../base/check'; 4 | import { HTMLSpanElement } from '../../../../../../engine/dom/html_span_element'; 5 | import { Text } from '../../../../../../engine/dom/text'; 6 | import { Element } from '../../../../../../engine/dom/element'; 7 | 8 | export const text: Directive = ({ el, get, effect }) => { 9 | effect(() => { 10 | const toSet = toDisplayString(get()); 11 | if (el.IsText()) { 12 | el.textContent = toSet; 13 | } else { 14 | DCHECK(el.tagName === HTMLSpanElement.kTagName); 15 | if (el.firstChild) { 16 | el.firstChild.textContent = toSet; 17 | } else { 18 | el.textContent = toSet; 19 | } 20 | } 21 | }); 22 | }; 23 | 24 | export const toDisplayString = (value: any) => 25 | value == null 26 | ? '' 27 | : isObject(value) 28 | ? JSON.stringify(value, null, 2) 29 | : String(value); 30 | -------------------------------------------------------------------------------- /packages/neditor/canvas/view/parts/canvas/adapter/eval.ts: -------------------------------------------------------------------------------- 1 | import { Node } from "../../../../../engine/dom/node"; 2 | import { DLOG, WARNING } from "../../../../../base/logging"; 3 | 4 | const evalCache: Record = Object.create(null); 5 | 6 | export const evaluate = (scope: any, exp: string, el?: Node) => 7 | execute(scope, `return(${exp})`, el); 8 | 9 | export const execute = (scope: any, exp: string, el?: Node) => { 10 | const fn = evalCache[exp] || (evalCache[exp] = toFunction(exp)); 11 | try { 12 | return fn(scope, el); 13 | } catch (e) { 14 | DLOG(WARNING, `Error when evaluating expression "${exp}":`); 15 | console.error(e); 16 | } 17 | }; 18 | 19 | const toFunction = (exp: string): Function => { 20 | try { 21 | return new Function(`$data`, `$el`, `with($data){${exp}}`); 22 | } catch (e) { 23 | console.error(`${(e as Error).message} in expression: ${exp}`); 24 | return () => {}; 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /packages/neditor/canvas/view/parts/canvas/adapter/scheduler.ts: -------------------------------------------------------------------------------- 1 | import { setOnEnd } from '../../../../canvasCommon/scheduler'; 2 | 3 | let queued = false; 4 | const queue: Function[] = []; 5 | 6 | export const queueJob = (job: Function) => { 7 | if (!queue.includes(job)) queue.push(job); 8 | if (!queued) { 9 | queued = true; 10 | } 11 | }; 12 | 13 | const flushJobs = () => { 14 | for (const job of queue) { 15 | job(); 16 | } 17 | queue.length = 0; 18 | queued = false; 19 | }; 20 | 21 | setOnEnd(flushJobs); 22 | -------------------------------------------------------------------------------- /packages/neditor/canvas/view/parts/canvas/adapter/utils.ts: -------------------------------------------------------------------------------- 1 | import { Element } from '../../../../../engine/dom/element'; 2 | 3 | export const checkAttr = (el: Element, name: string): string | null => { 4 | const val = el.getAttribute(name); 5 | if (val != null) el.removeAttribute(name); 6 | return val; 7 | }; 8 | -------------------------------------------------------------------------------- /packages/neditor/canvas/view/type.ts: -------------------------------------------------------------------------------- 1 | export type RenderConfig = { 2 | zoom: number 3 | } 4 | -------------------------------------------------------------------------------- /packages/neditor/canvas/view/view.css: -------------------------------------------------------------------------------- 1 | .overflow-guard { 2 | position: absolute; 3 | top: 0; 4 | left: 0; 5 | overflow: hidden; 6 | } 7 | -------------------------------------------------------------------------------- /packages/neditor/canvas/view/view.ts: -------------------------------------------------------------------------------- 1 | import { IMatrix } from '@neditor/core/base/common/geometry'; 2 | import { Event } from '../../base/common/event'; 3 | import { IDisposable } from '../../base/common/lifecycle'; 4 | import { Document } from '../../engine/dom/document'; 5 | import { HitTestLevel } from '../../platform/input/common/input'; 6 | import { LayoutManager } from '../../engine/layout/layout_manager'; 7 | import { ICanvasState } from '../canvas/canvas'; 8 | 9 | export enum CursorStyle { 10 | none = 'none', 11 | arrow = 'default', 12 | pointer = 'pointer', 13 | grab = 'grab', 14 | grabbing = 'grabbing', 15 | move = 'move', 16 | text = 'text', 17 | } 18 | 19 | export interface ICanvasView extends IDisposable { 20 | domNode: HTMLElement; 21 | mx: IMatrix; 22 | zoom: number; 23 | document: Document; 24 | layoutManager: LayoutManager; 25 | onCameraChagned: Event; 26 | 27 | translate(translateX: number, translateY: number): void; 28 | setCursor(s: CursorStyle): void; 29 | currentCursor(): CursorStyle; 30 | setHitTestLevel(level: HitTestLevel): void; 31 | internal_disconnect(): void; 32 | internal_connect(): void; 33 | 34 | reflowOverlay(state: ICanvasState): void; 35 | reflow(): void; 36 | 37 | focus(): void; 38 | isFocused(): boolean; 39 | } 40 | -------------------------------------------------------------------------------- /packages/neditor/canvas/viewModel/viewModelEvents.ts: -------------------------------------------------------------------------------- 1 | import { ScopedIdentifier } from '../canvasCommon/scope'; 2 | 3 | export interface ISelectionChangedEvent { 4 | oldSelection: ScopedIdentifier[]; 5 | newSelection: ScopedIdentifier[]; 6 | } 7 | -------------------------------------------------------------------------------- /packages/neditor/common/common.ts: -------------------------------------------------------------------------------- 1 | export type IIdentifier = string; 2 | -------------------------------------------------------------------------------- /packages/neditor/common/model.ts: -------------------------------------------------------------------------------- 1 | import * as Y from 'yjs'; 2 | import { INodeModel, YNode } from './node'; 3 | 4 | export interface IDocumentModel { 5 | nodes: Record; 6 | } 7 | 8 | export type IYDocumentModel = Y.Map 9 | 10 | export type IYNodeModels = Y.Map 11 | 12 | export function getModelNodes(m: IYDocumentModel) { 13 | return m.get('nodes') as IYNodeModels; 14 | } 15 | 16 | -------------------------------------------------------------------------------- /packages/neditor/engine/base/clock.ts: -------------------------------------------------------------------------------- 1 | import { TimeDelta, TimeTicks } from '@neditor/core/base/time/time'; 2 | import { DCHECK } from '@neditor/core/base/check'; 3 | 4 | export abstract class BasicClock { 5 | abstract Now(): TimeDelta 6 | } 7 | 8 | // The SystemClock calls in to the standard ::base::TimeTicks::HighResNow() 9 | // method to obtain a time. 10 | export class SystemMonotonicClock extends BasicClock { 11 | origin_: TimeTicks; 12 | constructor() { 13 | super(); 14 | this.origin_ = TimeTicks.Now(); 15 | } 16 | Now(): TimeDelta { 17 | return TimeTicks.Now().SUB(this.origin_); 18 | } 19 | } 20 | 21 | // The OffsetClock takes a parent clock and an offset upon construction, and 22 | // when queried for the time it returns the time of the parent clock offset by 23 | // the specified offset. 24 | export class OffsetClock extends BasicClock { 25 | private parent_: BasicClock; 26 | private origin_: TimeDelta; 27 | constructor(parent: BasicClock, origin: TimeDelta) { 28 | super(); 29 | DCHECK(parent); 30 | this.parent_ = parent; 31 | this.origin_ = origin; 32 | } 33 | 34 | Now(): TimeDelta { return this.parent_.Now().SUB(this.origin_); } 35 | 36 | origin(): TimeDelta { return this.origin_; } 37 | } 38 | -------------------------------------------------------------------------------- /packages/neditor/engine/base/dom.ts: -------------------------------------------------------------------------------- 1 | import { Node } from '../dom/node'; 2 | import { Optional } from '@neditor/core/base/common/typescript'; 3 | 4 | export function isAncestor(testChild: Optional, testAncestor: Node): boolean { 5 | while (testChild) { 6 | if (testChild === testAncestor) { 7 | return true; 8 | } 9 | testChild = testChild.parentNode; 10 | } 11 | 12 | return false; 13 | } 14 | -------------------------------------------------------------------------------- /packages/neditor/engine/base/language.ts: -------------------------------------------------------------------------------- 1 | // Gets the system language and ISO 3166-1 country code. 2 | // NOTE: should be in the format described by bcp47. 3 | // http://www.rfc-editor.org/rfc/bcp/bcp47.txt 4 | // Example: "en-US" or "de" 5 | export function GetSystemLanguage() { 6 | return 'en-US'; 7 | } 8 | 9 | // Gets the system language and ISO 15924 script code. 10 | // NOTE: should be in the format described by bcp47. 11 | // http://www.rfc-editor.org/rfc/bcp/bcp47.txt 12 | // Example: "en-US" or "de" 13 | export function GetSystemLanguageScript() { 14 | return 'en-US'; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /packages/neditor/engine/base/type_id.ts: -------------------------------------------------------------------------------- 1 | export type TypeId = number 2 | 3 | const idMap = new Map(); 4 | export function baseGetTypeId(type: unknown): TypeId { 5 | if (idMap.has(type)) { 6 | return idMap.get(type)!; 7 | } 8 | let newId = idMap.size + 1; 9 | idMap.set(type, newId); 10 | return newId; 11 | } 12 | -------------------------------------------------------------------------------- /packages/neditor/engine/base/unicode/utf.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Helper constant for U16_GET_SUPPLEMENTARY. 3 | * @internal 4 | */ 5 | const U16_SURROGATE_OFFSET = ((0xd800 << 10) + 0xdc00 - 0x10000); 6 | /** 7 | * Is this code unit a trail surrogate (U+dc00..U+dfff)? 8 | * @param c 16-bit code unit 9 | * @return TRUE or FALSE 10 | * @stable ICU 2.4 11 | */ 12 | export function U16_IS_TRAIL(c: number): boolean {return (((c) & 0xfffffc00) == 0xdc00);} 13 | 14 | /** 15 | * Get a supplementary code point value (U+10000..U+10ffff) 16 | * from its lead and trail surrogates. 17 | * The result is undefined if the input values are not 18 | * lead and trail surrogates. 19 | * 20 | * @param lead lead surrogate (U+d800..U+dbff) 21 | * @param trail trail surrogate (U+dc00..U+dfff) 22 | * @return supplementary code point (U+10000..U+10ffff) 23 | * @stable ICU 2.4 24 | */ 25 | export function U16_GET_SUPPLEMENTARY(lead: number, trail: number) { 26 | return (((lead) << 10) + (trail) - U16_SURROGATE_OFFSET); 27 | } 28 | -------------------------------------------------------------------------------- /packages/neditor/engine/css_parser/errorListener.ts: -------------------------------------------------------------------------------- 1 | import { ParserErrorListener } from 'antlr4ts'; 2 | import { Recognizer } from 'antlr4ts/Recognizer'; 3 | import { RecognitionException } from 'antlr4ts/RecognitionException'; 4 | 5 | export class InlineErrorListener implements ParserErrorListener { 6 | 7 | private _isValid = true; 8 | private _errorMessage: string | undefined; 9 | 10 | public get isValid() { 11 | return this._isValid; 12 | } 13 | 14 | public get errorMessage() { 15 | return this._errorMessage; 16 | } 17 | 18 | public syntaxError(recognizer: Recognizer, offendingSymbol: T | undefined, line: number, charPositionInLine: number, msg: string, e: RecognitionException | undefined) { 19 | this._isValid = false; 20 | this._errorMessage = `line ${line}:${charPositionInLine} ${msg}`; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/neditor/engine/css_parser/interpretor.ts: -------------------------------------------------------------------------------- 1 | import { CharStreams, CommonTokenStream } from 'antlr4ts'; 2 | import { InlineErrorListener } from './errorListener'; 3 | import { Visitor } from './visitor'; 4 | import type { DeclaredStyleDeclaration } from '@neditor/core/engine/cssom/declared_style_declaration'; 5 | import { inlineLexer } from '@neditor/css-parser/gen/inlineLexer'; 6 | import { inlineParser } from '@neditor/css-parser/gen/inlineParser'; 7 | 8 | export class Interpretor { 9 | apply(target: DeclaredStyleDeclaration, styleText: string) { 10 | const inputStream = CharStreams.fromString(styleText); 11 | const lexer = new inlineLexer(inputStream); 12 | const lexerErrorListener = new InlineErrorListener(); 13 | lexer.addErrorListener(lexerErrorListener); 14 | const commonTokenStream = new CommonTokenStream(lexer); 15 | const parser = new inlineParser(commonTokenStream); 16 | parser.buildParseTree = true; 17 | const parserErrorListener = new InlineErrorListener(); 18 | parser.addErrorListener(parserErrorListener); 19 | const tree = parser.inlineStyle(); 20 | const visitor = new Visitor(target); 21 | tree.accept(visitor); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/neditor/engine/cssom/character_classification.ts: -------------------------------------------------------------------------------- 1 | // Checks whether a character is affected by a CSS white space processing. 2 | // https://www.w3.org/TR/css3-text/#white-space-rules 3 | export function IsWhiteSpace(character: string): boolean { 4 | // Histogram from Apple's page load test combined with some ad hoc browsing 5 | // some other test suites. 6 | // 7 | // 82%: 216330 non-space characters, all > U+0020 8 | // 11%: 30017 plain space characters, U+0020 9 | // 5%: 12099 newline characters, U+000A 10 | // 2%: 5346 tab characters, U+0009 11 | // 12 | // No other characters seen. No U+000C or U+000D, and no other control 13 | // characters. Accordingly, we check for non-spaces first, then space, 14 | // then newline, then tab, then the other characters. 15 | 16 | return character.codePointAt(0)! <= (' ').codePointAt(0)! && 17 | (character == ' ' || character == '\n' || character == '\t' || 18 | character == '\r' || character == '\f'); 19 | } 20 | -------------------------------------------------------------------------------- /packages/neditor/engine/cssom/computed_style_utils.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | // Returns true if "overflow" should be treated as cropped. This is true for 4 | // overflow "auto", "hidden", and "scroll". 5 | // https://www.w3.org/TR/CSS21/visufx.html#overflow 6 | import { ComputedStyleData } from './computed_style_data'; 7 | import { KeywordValue } from './keyword_value'; 8 | 9 | export function IsOverflowCropped( 10 | computed_style: ComputedStyleData):boolean { 11 | return computed_style.overflow == KeywordValue.GetAuto() || 12 | computed_style.overflow == KeywordValue.GetHidden() || 13 | computed_style.overflow == KeywordValue.GetScroll(); 14 | } 15 | -------------------------------------------------------------------------------- /packages/neditor/engine/cssom/declaration_data.ts: -------------------------------------------------------------------------------- 1 | import { PropertyValue } from './property_value'; 2 | import { PropertyKey } from './property_definitions'; 3 | 4 | export type PropertyValues = Map 5 | 6 | export abstract class DeclarationData { 7 | // abstract IsSupportedPropertyKey(key: PropertyKey): boolean 8 | abstract GetPropertyValue(key: PropertyKey): PropertyValue | null 9 | abstract SetPropertyValue(key: PropertyKey, property_value: PropertyValue|null): void 10 | } 11 | -------------------------------------------------------------------------------- /packages/neditor/engine/cssom/initial_computed_style.ts: -------------------------------------------------------------------------------- 1 | import type { Size } from '../math/size'; 2 | import { ComputedStyleData, MutableComputedStyleData } from './computed_style_data'; 3 | import { RGBAColorValue } from './rgba_color_value'; 4 | import { KeywordValue } from './keyword_value'; 5 | import { LengthUnit, LengthValue } from './length_value'; 6 | import { PropertyKey } from './property_definitions'; 7 | 8 | export function CreateInitialComputedStyle( 9 | viewport_size: Size 10 | ): ComputedStyleData { 11 | let initial_containing_block_computed_style = new MutableComputedStyleData(); 12 | initial_containing_block_computed_style.set_background_color(new RGBAColorValue(0x00000000)); 13 | initial_containing_block_computed_style.set_display(KeywordValue.GetBlock()); 14 | initial_containing_block_computed_style.set_is_inline_before_blockification(false); 15 | initial_containing_block_computed_style.set_width(new LengthValue(viewport_size.width(), LengthUnit.kPixelsUnit)); 16 | initial_containing_block_computed_style.set_height(new LengthValue(viewport_size.height(), LengthUnit.kPixelsUnit)); 17 | return initial_containing_block_computed_style; 18 | } 19 | -------------------------------------------------------------------------------- /packages/neditor/engine/cssom/integer_value.ts: -------------------------------------------------------------------------------- 1 | // Represents a dimensionless value. 2 | // https://www.w3.org/TR/css3-values/#integers 3 | 4 | import { PropertyValue } from './property_value'; 5 | import { PropertyValueVisitor } from './property_value_visitor'; 6 | import { baseGetTypeId } from '../base/type_id'; 7 | 8 | export class IntegerValue extends PropertyValue { 9 | value_: number; 10 | constructor(value: number) { 11 | super(); 12 | this.value_ = value; 13 | } 14 | 15 | Accept(visitor: PropertyValueVisitor) { 16 | visitor.VisitInteger(this); 17 | } 18 | 19 | value() { return this.value_; } 20 | 21 | ToString() { 22 | return this.value_.toString(); 23 | } 24 | GetTypeId(): number { 25 | return baseGetTypeId(IntegerValue); 26 | } 27 | EQ(other: PropertyValue): boolean { 28 | if (!(other instanceof IntegerValue)) return false; 29 | return this.value_ === other.value_; 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /packages/neditor/engine/cssom/mutation_observer.ts: -------------------------------------------------------------------------------- 1 | export interface StyleMutationObserver { 2 | OnCSSMutation(): void; 3 | } 4 | -------------------------------------------------------------------------------- /packages/neditor/engine/cssom/number_value.ts: -------------------------------------------------------------------------------- 1 | // Represents a dimensionless value. 2 | // https://www.w3.org/TR/css3-values/#numeric-types 3 | import { PropertyValue } from './property_value'; 4 | import type { PropertyValueVisitor } from './property_value_visitor'; 5 | import { baseGetTypeId } from '../base/type_id'; 6 | 7 | export class NumberValue extends PropertyValue { 8 | private value_: number; 9 | constructor(value: number) { 10 | super(); 11 | this.value_ = value; 12 | } 13 | 14 | Accept(visitor: PropertyValueVisitor) { 15 | visitor.VisitNumber(this); 16 | } 17 | 18 | value(): number { return this.value_; } 19 | 20 | ToString(): string { 21 | return this.value_.toString(); 22 | } 23 | 24 | EQ(other: NumberValue): boolean { 25 | return this.value_ === other.value_; 26 | } 27 | GetTypeId(): number { 28 | return baseGetTypeId(NumberValue) 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /packages/neditor/engine/cssom/percentage_value.ts: -------------------------------------------------------------------------------- 1 | // Percentage values are always relative to another value, for example a length. 2 | // Each property that allows percentages also defines the value to which 3 | // the percentage refers. 4 | // https://www.w3.org/TR/css3-values/#percentages 5 | import { PropertyValue } from './property_value'; 6 | import type { PropertyValueVisitor } from './property_value_visitor'; 7 | import { toPercentage } from '@neditor/core/base/common/number'; 8 | import { baseGetTypeId } from '../base/type_id'; 9 | 10 | export class PercentageValue extends PropertyValue { 11 | value_: number; 12 | 13 | // A |value| is a normalized factor, where 1 means 100%. 14 | constructor(value: number) { 15 | super(); 16 | this.value_ = value; 17 | } 18 | 19 | Accept(visitor: PropertyValueVisitor) { 20 | visitor.VisitPercentage(this); 21 | } 22 | 23 | // Returns a normalized factor, where 1 means 100%. 24 | value(): number { return this.value_; } 25 | 26 | ToString(): string { 27 | return toPercentage(this.value_); 28 | } 29 | 30 | EQ(other: PercentageValue) { 31 | return this.value_ === other.value_; 32 | } 33 | GetTypeId(): number { 34 | return baseGetTypeId(PercentageValue) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/neditor/engine/cssom/property_list_value.ts: -------------------------------------------------------------------------------- 1 | import { ScopedRefListValue } from './scoped_ref_list_value'; 2 | import { PropertyValue } from './property_value'; 3 | import { PropertyValueVisitor } from './property_value_visitor'; 4 | import { baseGetTypeId, TypeId } from '../base/type_id'; 5 | 6 | export type PropertyListValueBuilder = PropertyValue[] 7 | 8 | export class PropertyListValue extends ScopedRefListValue { 9 | constructor(value: PropertyValue[]) {super(value);} 10 | 11 | Accept(visitor: PropertyValueVisitor) { 12 | visitor.VisitPropertyList(this); 13 | } 14 | 15 | ToString(): string { 16 | return this.value().map(value => value.ToString()).join(', '); 17 | } 18 | GetTypeId(): TypeId { 19 | return baseGetTypeId(PropertyListValue) 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /packages/neditor/engine/cssom/property_value.ts: -------------------------------------------------------------------------------- 1 | import type { PropertyValueVisitor } from './property_value_visitor'; 2 | import { TypeId } from '../base/type_id'; 3 | 4 | export abstract class PropertyValue { 5 | abstract Accept(visitor: PropertyValueVisitor): void; 6 | abstract ToString(): string; 7 | abstract GetTypeId(): TypeId 8 | abstract EQ(other: PropertyValue): boolean 9 | } 10 | -------------------------------------------------------------------------------- /packages/neditor/engine/cssom/rotate_function.ts: -------------------------------------------------------------------------------- 1 | // The rotate function specifies a 2D rotation operation by the the specified 2 | // angle. 3 | // https://www.w3.org/TR/css-transforms-1/#funcdef-rotate 4 | import { TransformFunction } from './transform_function'; 5 | import { TransformFunctionVisitor } from './transform_function_visitor'; 6 | import { SizeF } from '../math/size_f'; 7 | import { RotateMatrix } from '../math/transform_2d'; 8 | 9 | export class RotateFunction extends TransformFunction { 10 | private clockwise_angle_in_radians_: number; 11 | 12 | constructor( 13 | clockwise_angle_in_radians: number 14 | ) { 15 | super(); 16 | this.clockwise_angle_in_radians_ = clockwise_angle_in_radians; 17 | } 18 | 19 | Accept(visitor: TransformFunctionVisitor) { 20 | visitor.VisitRotate(this); 21 | } 22 | 23 | clockwise_angle_in_radians() { 24 | return this.clockwise_angle_in_radians_; 25 | } 26 | 27 | ToString() { 28 | return `rotate(${this.clockwise_angle_in_radians_}grad)`; 29 | } 30 | 31 | ToMatrix(used_size: SizeF) { 32 | return RotateMatrix(-this.clockwise_angle_in_radians_); 33 | } 34 | 35 | EQ(other: RotateFunction) { 36 | return this.clockwise_angle_in_radians_ === other.clockwise_angle_in_radians_; 37 | } 38 | 39 | }; 40 | 41 | -------------------------------------------------------------------------------- /packages/neditor/engine/cssom/string_value.ts: -------------------------------------------------------------------------------- 1 | // Represents a sequence of characters delimited by single or double quotes. 2 | // Applies to properties like font-family. 3 | // See https://www.w3.org/TR/css3-values/#strings for details. 4 | import { PropertyValue } from './property_value'; 5 | import { PropertyValueVisitor } from './property_value_visitor'; 6 | import { baseGetTypeId } from '../base/type_id'; 7 | 8 | export class StringValue extends PropertyValue { 9 | private value_: string; 10 | 11 | constructor(value: string) { 12 | super(); 13 | this.value_ = value; 14 | } 15 | 16 | Accept(visitor: PropertyValueVisitor) { 17 | visitor.VisitString(this); 18 | } 19 | 20 | value() { return this.value_; } 21 | 22 | ToString() { return '\'' + this.value_ + '\''; } 23 | 24 | EQ(other: StringValue) { 25 | return this.value_ === other.value_; 26 | } 27 | GetTypeId(): number { 28 | return baseGetTypeId(StringValue); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /packages/neditor/engine/cssom/transform_function_visitor.ts: -------------------------------------------------------------------------------- 1 | // Type-safe branching on a class hierarchy of transform functions, 2 | // implemented after a classical GoF pattern (see 3 | // http://en.wikipedia.org/wiki/Visitor_pattern#Java_example). 4 | import type { MatrixFunction } from './matrix_function'; 5 | import type { RotateFunction } from './rotate_function'; 6 | import type { ScaleFunction } from './scale_function'; 7 | import type { TranslateFunction } from './translate_function'; 8 | 9 | export abstract class TransformFunctionVisitor { 10 | abstract VisitMatrix(matrix_function: MatrixFunction): void 11 | abstract VisitRotate(rotate_function: RotateFunction): void 12 | abstract VisitScale(scale_function: ScaleFunction): void 13 | abstract VisitTranslate(translate_function: TranslateFunction): void 14 | }; 15 | -------------------------------------------------------------------------------- /packages/neditor/engine/cssom/transform_property_value.ts: -------------------------------------------------------------------------------- 1 | // A base class for all CSS transform property values. 2 | import { PropertyValue } from './property_value'; 3 | import { PropertyValueVisitor } from './property_value_visitor'; 4 | import { SizeF } from '../math/size_f'; 5 | import { Matrix3F } from '../math/matrix3_f'; 6 | import { Trait } from './transform_function'; 7 | 8 | export abstract class TransformPropertyValue extends PropertyValue { 9 | 10 | Accept(visitor: PropertyValueVisitor) { 11 | visitor.VisitTransformPropertyValue(this); 12 | } 13 | 14 | // Returns whether the transform has any functions with the specified trait. 15 | abstract HasTrait(trait: Trait): boolean 16 | 17 | abstract ToMatrix(used_size: SizeF): Matrix3F 18 | }; 19 | -------------------------------------------------------------------------------- /packages/neditor/engine/cssom/unicode_range_value.ts: -------------------------------------------------------------------------------- 1 | // See https://www.w3.org/TR/css3-fonts/#unicode-range-desc for details. 2 | import { PropertyValue } from './property_value'; 3 | import { PropertyValueVisitor } from './property_value_visitor'; 4 | import { baseGetTypeId, TypeId } from '../base/type_id'; 5 | 6 | export class UnicodeRangeValue extends PropertyValue { 7 | private start_: number; 8 | private end_: number; 9 | constructor( 10 | start: number, 11 | end: number, 12 | ) { 13 | super(); 14 | this.start_ = start; 15 | this.end_ = end; 16 | } 17 | 18 | Accept(visitor: PropertyValueVisitor) { 19 | visitor.VisitUnicodeRange(this); 20 | } 21 | 22 | start() { return this.start_; } 23 | end() { return this.end_; } 24 | 25 | ToString() { 26 | if (this.start_ == this.end_) { 27 | return `U+${this.start_.toString(16)}`; 28 | } else { 29 | return `U+${this.start_.toString(16)}-${this.end_.toString(16)}`; 30 | } 31 | } 32 | 33 | EQ(other: UnicodeRangeValue): boolean { 34 | if (!(other instanceof UnicodeRangeValue)) return false; 35 | return this.start_ == other.start_ && this.end_ == other.end_; 36 | } 37 | 38 | IsValid() { 39 | return this.start_ <= this.end_; 40 | } 41 | GetTypeId(): TypeId { 42 | return baseGetTypeId(UnicodeRangeValue); 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /packages/neditor/engine/cssom/url_value.ts: -------------------------------------------------------------------------------- 1 | // A URL is a pointer to a resource and corresponds to the URI token in the 2 | // grammar. It is generated by CSS parser when parsing URL values. 3 | // https://www.w3.org/TR/css3-values/#urls 4 | import { PropertyValue } from "./property_value"; 5 | import { PropertyValueVisitor } from "./property_value_visitor"; 6 | import { NOTIMPLEMENTED } from "@neditor/core/base/common/notreached"; 7 | import { baseGetTypeId, TypeId } from "../base/type_id"; 8 | 9 | export class URLValue extends PropertyValue { 10 | private url_: string; 11 | // private is_absolute_: boolean; 12 | 13 | constructor(url: string) { 14 | super(); 15 | this.url_ = url 16 | } 17 | Accept(visitor: PropertyValueVisitor): void { 18 | NOTIMPLEMENTED() 19 | } 20 | EQ(other: URLValue): boolean { 21 | return this.url_ === other.url_ 22 | } 23 | GetTypeId(): TypeId { 24 | return baseGetTypeId(URLValue) 25 | } 26 | ToString(): string { 27 | return "url(" + this.url_ + ")" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/neditor/engine/dom/abstract_range.ts: -------------------------------------------------------------------------------- 1 | import { Node } from './node'; 2 | 3 | export abstract class AbstractRange { 4 | abstract startContainer: Node 5 | abstract startOffset: number 6 | abstract endContainer: Node 7 | abstract endOffset: number 8 | abstract collapsed: boolean 9 | 10 | // static bool HasDifferentRootContainer(Node* start_root_container, 11 | // Node* end_root_container); 12 | // static unsigned LengthOfContents(const Node*); 13 | // virtual bool IsStaticRange() const = 0; 14 | // virtual Document& OwnerDocument() const = 0; 15 | } 16 | -------------------------------------------------------------------------------- /packages/neditor/engine/dom/attach_context.ts: -------------------------------------------------------------------------------- 1 | import { keys } from '@neditor/core/base/common/objects'; 2 | 3 | export class AttachContext { 4 | // Keep track of previously attached in-flow box during attachment so that 5 | // we don't need to backtrack past display:none/contents and out of flow 6 | // objects when we need to do whitespace re-attachment. 7 | // previous_in_flow: Ptr; 8 | // The parent LayoutObject to use when inserting a new child into the layout 9 | // tree in LayoutTreeBuilder::CreateLayoutObject. 10 | // parent: Ptr; 11 | // LayoutObject to be used as the next pointer when inserting a LayoutObject 12 | // into the tree. 13 | // next_sibling: Ptr; 14 | // Set to true if the AttachLayoutTree is done as part of the 15 | // RebuildLayoutTree pass. 16 | performing_reattach = false; 17 | // True if the previous_in_flow member is up-to-date, even if it is nullptr. 18 | use_previous_in_flow = false; 19 | // True if the next_sibling member is up-to-date, even if it is nullptr. 20 | next_sibling_valid = false; 21 | // True if we need to force legacy layout objects for the entire subtree. 22 | force_legacy_layout = false; 23 | constructor(other?: AttachContext) { 24 | if (other) { 25 | keys(other).forEach((k) => { 26 | Reflect.set(this, k, other[k]); 27 | }); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/neditor/engine/dom/benchmark_stat_names.ts: -------------------------------------------------------------------------------- 1 | export const kBenchmarkStatUpdateComputedStyles = 'UpdateComputedStyle'; 2 | export const kBenchmarkStatUpdateSelectorTree = 'UpdateSelectorTree'; 3 | -------------------------------------------------------------------------------- /packages/neditor/engine/dom/comment.ts: -------------------------------------------------------------------------------- 1 | // The Comment interface represents textual notations within markup; although 2 | // it is generally not visually shown, such comments can be still retrieved 3 | // from the document. 4 | // https://www.w3.org/TR/2014/WD-dom-20140710/#interface-comment 5 | import { CharacterData } from './character_data'; 6 | import type { NodeVisitor } from './node'; 7 | import { ConstructionType, NodeType } from './node'; 8 | import { Document } from './document'; 9 | import { isString } from '@neditor/core/base/common/type'; 10 | 11 | export class Comment extends CharacterData { 12 | constructor(data: string) 13 | constructor(document: Document, data: string) 14 | constructor(a1: Document | string, a2?: string) { 15 | if (isString(a1)) { 16 | super(ConstructionType.kCreateOther, a1); 17 | } else { 18 | super(ConstructionType.kCreateOther, a1 as Document, a2!); 19 | } 20 | } 21 | // Web API: Node 22 | get nodeName() {return '#comment';} 23 | getNodeType() { return NodeType.kCommentNode; } 24 | 25 | // Custom, not in any spec: Node. 26 | // 27 | AsComment() { return this; } 28 | 29 | Accept(visitor: NodeVisitor) { 30 | visitor.VisitComment(this); 31 | } 32 | 33 | Duplicate() { 34 | let c = new Comment(this.data); 35 | c.node_document_ = this.node_document_; 36 | return c; 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /packages/neditor/engine/dom/container_action.ts: -------------------------------------------------------------------------------- 1 | export enum SubtreeModificationAction { 2 | kDispatchSubtreeModifiedEvent, 3 | kOmitSubtreeModifiedEvent 4 | }; 5 | -------------------------------------------------------------------------------- /packages/neditor/engine/dom/directionality.ts: -------------------------------------------------------------------------------- 1 | export enum Directionality { 2 | kLeftToRightDirectionality, 3 | kRightToLeftDirectionality, 4 | }; 5 | -------------------------------------------------------------------------------- /packages/neditor/engine/dom/document_timeline.ts: -------------------------------------------------------------------------------- 1 | // Implements the DocumentTimeline IDL interface. 2 | // https://www.w3.org/TR/2015/WD-web-animations-1-20150707/#the-documenttimeline-interface 3 | import type { Document } from './document'; 4 | import { BasicClock, OffsetClock } from '../base/clock'; 5 | import { TimeDelta } from '@neditor/core/base/time/time'; 6 | import { AnimationTimeline } from '../web_animations/animation_timeline'; 7 | 8 | export class DocumentTimeline extends AnimationTimeline { 9 | private document_: Document; 10 | 11 | constructor(document: Document, origin_time: number) { 12 | super(CreateOffsetClock(document, origin_time)); 13 | this.document_ = document; 14 | } 15 | }; 16 | 17 | function CreateOffsetClock(document: Document, 18 | origin_time: number): BasicClock { 19 | return new OffsetClock(document.navigation_start_clock(), TimeDelta.FromMilliseconds(origin_time)); 20 | } 21 | -------------------------------------------------------------------------------- /packages/neditor/engine/dom/html_anchor_element.ts: -------------------------------------------------------------------------------- 1 | import type { Document } from './document'; 2 | import { HTMLElement } from './html_element'; 3 | 4 | export class HTMLAnchorElement extends HTMLElement { 5 | static kTagName = 'a'; 6 | constructor(document: Document) {super(document, HTMLAnchorElement.kTagName);} 7 | } 8 | -------------------------------------------------------------------------------- /packages/neditor/engine/dom/html_body_element.ts: -------------------------------------------------------------------------------- 1 | import type { Document } from './document'; 2 | import { HTMLElement } from './html_element'; 3 | 4 | export class HTMLBodyElement extends HTMLElement { 5 | static kTagName = 'body'; 6 | constructor(document: Document) {super(document, HTMLBodyElement.kTagName);} 7 | 8 | AsHTMLBodyElement(): HTMLBodyElement | null { 9 | return this; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/neditor/engine/dom/html_br_element.ts: -------------------------------------------------------------------------------- 1 | // The br element represents a line break. 2 | // https://www.w3.org/TR/html50/text-level-semantics.html#the-br-element 3 | import { Document } from './document'; 4 | import { HTMLElement } from './html_element'; 5 | 6 | export class HTMLBRElement extends HTMLElement { 7 | static kTagName = 'br'; 8 | constructor(document: Document) {super(document, HTMLBRElement.kTagName);} 9 | 10 | // Custom, not in any spec. 11 | AsHTMLBRElement() { return this; } 12 | }; 13 | -------------------------------------------------------------------------------- /packages/neditor/engine/dom/html_div_element.ts: -------------------------------------------------------------------------------- 1 | import type { Document } from './document'; 2 | import { HTMLElement } from './html_element'; 3 | 4 | export class HTMLDivElement extends HTMLElement { 5 | static kTagName = 'div'; 6 | constructor() 7 | constructor(document: Document) 8 | constructor(document?: Document) { 9 | if (document) { 10 | super(document, HTMLDivElement.kTagName); 11 | } else { 12 | super(HTMLDivElement.kTagName); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/neditor/engine/dom/html_element_context.ts: -------------------------------------------------------------------------------- 1 | // This class contains references to several objects that are required by HTML 2 | // elements, including HTML element factory, which is used to create new 3 | // HTML elements. 4 | import { ResourceProvider } from '../render_tree/resource_provider'; 5 | import { ImageCache } from '../loader/image/image_cache'; 6 | import { RemoteTypefaceCache } from '../loader/font/remote_typeface_cache'; 7 | 8 | export class HTMLElementContext { 9 | private resource_provider_: ResourceProvider; 10 | private image_cache_: ImageCache; 11 | private font_language_script_: string; 12 | private remote_typeface_cache_: RemoteTypefaceCache; 13 | 14 | constructor( 15 | resource_provider: ResourceProvider, 16 | image_cache: ImageCache, 17 | remote_typeface_cache: RemoteTypefaceCache, 18 | font_language_script: string 19 | ) { 20 | this.resource_provider_ = resource_provider; 21 | this.image_cache_ = image_cache; 22 | this.remote_typeface_cache_ = remote_typeface_cache; 23 | this.font_language_script_ = font_language_script; 24 | } 25 | 26 | font_language_script() { 27 | return this.font_language_script_; 28 | } 29 | resource_provider(): ResourceProvider { 30 | return this.resource_provider_; 31 | } 32 | image_cache() { 33 | return this.image_cache_; 34 | } 35 | remote_typeface_cache() { 36 | return this.remote_typeface_cache_; 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /packages/neditor/engine/dom/html_em_element.ts: -------------------------------------------------------------------------------- 1 | import type { Document } from './document'; 2 | import { HTMLElement } from './html_element'; 3 | 4 | export class HTMLEmElement extends HTMLElement { 5 | static kTagName = 'em'; 6 | constructor(document: Document) {super(document, HTMLEmElement.kTagName);} 7 | } 8 | -------------------------------------------------------------------------------- /packages/neditor/engine/dom/html_heading_element.ts: -------------------------------------------------------------------------------- 1 | // These elements represent headings for their sections. 2 | // https://www.w3.org/TR/html50/sections.html#the-h1,-h2,-h3,-h4,-h5,-and-h6-elements 3 | import { HTMLElement } from './html_element'; 4 | 5 | export class HTMLHeadingElement extends HTMLElement { 6 | static kTagNames = ['h1', 'h2', 'h3', 7 | 'h4', 'h5', 'h6']; 8 | } 9 | -------------------------------------------------------------------------------- /packages/neditor/engine/dom/html_html_element.ts: -------------------------------------------------------------------------------- 1 | import type { Document } from './document'; 2 | import { HTMLElement } from './html_element'; 3 | 4 | export class HTMLHtmlElement extends HTMLElement { 5 | static kTagName = 'html'; 6 | constructor(document: Document) {super(document, HTMLHtmlElement.kTagName);} 7 | } 8 | -------------------------------------------------------------------------------- /packages/neditor/engine/dom/html_paragraph_element.ts: -------------------------------------------------------------------------------- 1 | import type { Document } from './document'; 2 | import { HTMLElement } from './html_element'; 3 | 4 | export class HTMLParagraphElement extends HTMLElement { 5 | static kTagName = 'p'; 6 | constructor(document: Document) { 7 | super(document, HTMLParagraphElement.kTagName); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/neditor/engine/dom/html_span_element.ts: -------------------------------------------------------------------------------- 1 | import type { Document } from './document'; 2 | import { HTMLElement } from './html_element'; 3 | 4 | export class HTMLSpanElement extends HTMLElement { 5 | static kTagName = 'span'; 6 | constructor(document?: Document) { 7 | if (document) { 8 | super(document, HTMLSpanElement.kTagName); 9 | } else { 10 | super(HTMLSpanElement.kTagName); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/neditor/engine/dom/html_strong_element.ts: -------------------------------------------------------------------------------- 1 | import type { Document } from './document'; 2 | import { HTMLElement } from './html_element'; 3 | 4 | export class HTMLStrongElement extends HTMLElement { 5 | static kTagName = 'strong'; 6 | constructor(document: Document) {super(document, HTMLStrongElement.kTagName);} 7 | } 8 | -------------------------------------------------------------------------------- /packages/neditor/engine/dom/node_children_iterator.ts: -------------------------------------------------------------------------------- 1 | // Iterates over the first-level children of the given node. 2 | import type { Node } from './node'; 3 | import { Optional } from '@neditor/core/base/common/typescript'; 4 | 5 | export class NodeChildrenIterator { 6 | parent_: Node; 7 | current_: Optional; 8 | constructor(parent: Node) { 9 | this.parent_ = parent; 10 | } 11 | 12 | First(): Optional { 13 | this.current_ = this.parent_.firstChild 14 | return this.current_; 15 | } 16 | 17 | Next(): Optional { 18 | if (this.current_) { 19 | this.current_ = this.current_.next_sibling(); 20 | } 21 | return this.current_; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/neditor/engine/dom/node_descendants_iterator.ts: -------------------------------------------------------------------------------- 1 | import type { Node } from './node'; 2 | import { Optional } from '@neditor/core/base/common/typescript'; 3 | 4 | export class NodeDescendantsIterator { 5 | base_: Node; 6 | current_: Optional; 7 | constructor(base: Node) { 8 | this.base_ = base; 9 | } 10 | 11 | First(): Optional { 12 | this.current_ = this.base_.firstChild 13 | return this.current_; 14 | } 15 | 16 | Next(): Optional { 17 | if (!this.current_) { 18 | return undefined; 19 | } 20 | if (this.current_.firstChild) { 21 | // Walk down and use the first child. 22 | this.current_ = this.current_.firstChild 23 | } else { 24 | // Walk towards the next sibling if one exists. 25 | // If one doesn't exist, walk up and look for a node (that is not a root) 26 | // with a sibling and continue the iteration from there. 27 | while (!this.current_!.next_sibling()) { 28 | this.current_ = this.current_!.parentNode; 29 | if (this.current_ === this.base_) { 30 | this.current_ = undefined; 31 | return this.current_; 32 | } 33 | } 34 | this.current_ = this.current_!.next_sibling(); 35 | } 36 | return this.current_; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/neditor/engine/dom/node_list.ts: -------------------------------------------------------------------------------- 1 | // A NodeList object is a collection of nodes. 2 | // https://www.w3.org/TR/2015/WD-dom-20150428/#interface-nodelist 3 | import type { Node } from './node'; 4 | 5 | export class NodeList implements Record { 6 | [x: number]: Node | null 7 | protected collection_: Node[] = []; 8 | Length(): number {return this.collection_.length;} 9 | get length(): number {return this.Length();} 10 | 11 | Item(item: number): Node | null { 12 | return this.collection_[item]; 13 | } 14 | AppendNode(node: Node) { 15 | this.collection_.push(node); 16 | } 17 | Clear() { 18 | this.collection_.length = 0; 19 | } 20 | // custom 21 | indexOf(item: Node): number { 22 | return this.collection_.indexOf(item); 23 | } 24 | [Symbol.iterator]() { 25 | let index = 0; 26 | return { 27 | next: () => { 28 | if (index < this.Length()) { 29 | let value = this.Item(index); 30 | index++; 31 | return { 32 | value, 33 | done: false, 34 | }; 35 | } else { 36 | return { 37 | value: undefined, 38 | done: true 39 | }; 40 | } 41 | } 42 | }; 43 | 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/neditor/engine/dom/node_traversal.ts: -------------------------------------------------------------------------------- 1 | import { Optional } from '@neditor/core/base/common/typescript'; 2 | import { Node } from './node'; 3 | import { ContainerNode } from './container_node'; 4 | import { TraversalAncestorRange, TraversalSiblingRange } from './traversal_range'; 5 | 6 | export class NodeTraversal { 7 | static FirstChild(parent: Node): Optional { return parent.firstChild; } 8 | static LastChild(parent: Node) { return parent.lastChild; } 9 | static NextSibling(node: Node): Optional { return node.nextSibling; } 10 | static PreviousSibling(node: Node) { 11 | return node.previousSibling; 12 | } 13 | static Parent(node: Node): Optional { return node.parentNode?.AsContainerNode(); } 14 | static ChildrenOf(parent: Node) { 15 | return TraversalSiblingRange(NodeTraversal.FirstChild(parent)); 16 | } 17 | static AncestorsOf(node: Node) { 18 | return TraversalAncestorRange(NodeTraversal.Parent(node)); 19 | } 20 | 21 | static CommonAncestor(node_a: Node, node_b: Node): Optional { 22 | if (!node_a || !node_b) 23 | return undefined; 24 | return node_a.CommonAncestor(node_b); 25 | } 26 | 27 | static ChildAt(parent: Node, index: number): Optional { 28 | let child = parent.firstChild; 29 | while (child && index--) 30 | child = child.nextSibling; 31 | return child; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/neditor/engine/dom/text.ts: -------------------------------------------------------------------------------- 1 | import { ConstructionType, NodeType, NodeVisitor } from './node'; 2 | import type { Document } from './document'; 3 | import { CharacterData } from './character_data'; 4 | import { AttachContext } from './attach_context'; 5 | import { NOTIMPLEMENTED } from '@neditor/core/base/common/notreached'; 6 | import { isString } from '@neditor/core/base/common/type'; 7 | 8 | // The Text interface represents the textual content of Element or Attr. 9 | // https://www.w3.org/TR/2014/WD-dom-20140710/#interface-text 10 | export class Text extends CharacterData { 11 | constructor(data: string) 12 | constructor(document: Document, data: string) 13 | constructor(a1: Document | string, a2?: string) { 14 | if (isString(a1)) { 15 | super(ConstructionType.kCreateText, a1); 16 | } else { 17 | super(ConstructionType.kCreateText, a1 as Document, a2!); 18 | } 19 | } 20 | 21 | // Web API: Node 22 | 23 | get nodeName() {return '#text';} 24 | getNodeType() { return NodeType.kTextNode; } 25 | 26 | // Custom, not in any spec: Node. 27 | // 28 | AsText() { return this; } 29 | 30 | Accept(visitor: NodeVisitor) { 31 | visitor.VisitText(this); 32 | } 33 | 34 | Duplicate() { 35 | let t = new Text(this.data); 36 | t.node_document_ = this.node_document_; 37 | return t; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/neditor/engine/dom/traversal_range.ts: -------------------------------------------------------------------------------- 1 | import { Optional } from '../../base/common/typescript'; 2 | import { Node } from './node'; 3 | import { assertIsDefined } from "../../base/common/type"; 4 | 5 | export function TraversalSiblingRange(node: Optional) { 6 | return { 7 | [Symbol.iterator]() { 8 | let n: Optional = node; 9 | return { 10 | next() { 11 | if (n) { 12 | let value = n; 13 | n = n.nextSibling; 14 | return { 15 | value: assertIsDefined(value), 16 | done: false, 17 | }; 18 | } else { 19 | return { 20 | done: true 21 | }; 22 | } 23 | } 24 | }; 25 | 26 | } 27 | }; 28 | } 29 | 30 | export function TraversalAncestorRange(node: Optional) { 31 | return { 32 | [Symbol.iterator]() { 33 | let n: Optional = node; 34 | return { 35 | next() { 36 | if (n) { 37 | let value = n; 38 | n = n.parentNode; 39 | return { 40 | value, 41 | done: false, 42 | }; 43 | } else { 44 | return { 45 | value: undefined, 46 | done: true, 47 | }; 48 | } 49 | } 50 | }; 51 | } 52 | }; 53 | } 54 | -------------------------------------------------------------------------------- /packages/neditor/engine/geometry/dom_rect.ts: -------------------------------------------------------------------------------- 1 | import { DOMRectReadOnly } from './dom_rect_read_only'; 2 | 3 | export class DOMRect extends DOMRectReadOnly { 4 | }; 5 | -------------------------------------------------------------------------------- /packages/neditor/engine/geometry/dom_rect_read_only.ts: -------------------------------------------------------------------------------- 1 | import { DCHECK } from '../../base/check'; 2 | 3 | export class DOMRectReadOnly { 4 | protected x_: number; 5 | protected y_: number; 6 | protected width_: number; 7 | protected height_: number; 8 | constructor(x: number, y: number, width: number, height: number) { 9 | DCHECK(width > 0); 10 | DCHECK(height > 0); 11 | this.x_ = x; 12 | this.y_ = y; 13 | this.width_ = width; 14 | this.height_ = height; 15 | } 16 | 17 | x() { return this.x_; } 18 | y() { return this.y_; } 19 | width() { return this.width_; } 20 | height() { return this.height_; } 21 | 22 | top() { return Math.min(this.y_, this.y_ + this.height_); } 23 | right() { return Math.max(this.x_, this.x_ + this.width_); } 24 | bottom() { return Math.max(this.y_, this.y_ + this.height_); } 25 | left() { return Math.min(this.x_, this.x_ + this.width_); } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /packages/neditor/engine/layout/base_direction.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | // BaseDirection is used to indicate the inline direction in which content is 4 | // ordered on a line and defines on which sides "start" and "end" of a line are, 5 | // and also to indicate the zero bidirectional orientation of text in a 6 | // paragraph. 7 | // https://www.w3.org/TR/css-writing-modes-3/#inline-base-direction 8 | // http://unicode.org/reports/tr9/#BD5 9 | export enum BaseDirection { 10 | kRightToLeftBaseDirection, 11 | kLeftToRightBaseDirection, 12 | }; 13 | -------------------------------------------------------------------------------- /packages/neditor/engine/layout/benchmark_stat_names.ts: -------------------------------------------------------------------------------- 1 | export const kBenchmarkStatBoxGeneration = 'BoxGeneration'; 2 | export const kBenchmarkStatLayout = 'Layout'; 3 | export const kBenchmarkStatRenderAndAnimate = 'RenderAndAnimate'; 4 | export const kBenchmarkStatUpdateCrossReferences = 'UpdateCrossReferences'; 5 | export const kBenchmarkStatUpdateUsedSizes = 'UpdateUsedSizes'; 6 | export const kBenchmarkStatNonMeasuredLayout = 'NonMeasuredLayout'; 7 | -------------------------------------------------------------------------------- /packages/neditor/engine/layout/block_level_replaced_box.ts: -------------------------------------------------------------------------------- 1 | import { ReplacedBox } from './replaced_box'; 2 | import { LayoutUnit } from './layout_unit'; 3 | import { Optional } from '@neditor/core/base/common/typescript'; 4 | import { Level } from './box'; 5 | import { BaseDirection } from './base_direction'; 6 | 7 | export class BlockLevelReplacedBox extends ReplacedBox { 8 | // From |Box|. 9 | GetLevel(): Level {return Level.kBlockLevel;} 10 | 11 | // From |Box|. 12 | DumpClassName(stream: string) { 13 | stream += 'BlockLevelReplacedBox '; 14 | return stream; 15 | } 16 | 17 | // From |ReplacedBox|. 18 | UpdateHorizontalMargins( 19 | containing_block_direction: BaseDirection, 20 | containing_block_width: LayoutUnit, 21 | border_box_width: LayoutUnit, 22 | maybe_margin_left: Optional, 23 | maybe_margin_right: Optional) { 24 | // Calculate the horizonal margins for block-level, replaced elements in 25 | // normal flow. 26 | // https://www.w3.org/TR/CSS21/visudet.html#block-replaced-width 27 | this.UpdateHorizontalMarginsAssumingBlockLevelInFlowBox( 28 | containing_block_direction, containing_block_width, 29 | border_box_width, maybe_margin_left, maybe_margin_right); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /packages/neditor/engine/layout/layout_object.ts: -------------------------------------------------------------------------------- 1 | import { Box } from "./box"; 2 | import { Node } from "../dom/node"; 3 | import { Optional } from "../../base/common/typescript"; 4 | import { LayoutTextes } from "./layout_text"; 5 | 6 | export class LayoutObject { 7 | constructor( 8 | public node: Node, 9 | public box: Box 10 | ) { 11 | } 12 | 13 | AsLayoutTexes(): Optional { 14 | return undefined 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/neditor/engine/layout/layout_text.ts: -------------------------------------------------------------------------------- 1 | import { LayoutObject } from "./layout_object"; 2 | import { TextBox } from "./text_box"; 3 | import { Box } from "./box"; 4 | import { Node } from "../dom/node"; 5 | 6 | export class LayoutTextes extends LayoutObject { 7 | children: TextBox[] = [] 8 | constructor(node: Node, box: TextBox) { 9 | super(node, box); 10 | this.children.push(box) 11 | } 12 | AsLayoutTexes() { 13 | return this; 14 | } 15 | Append(box: TextBox) { 16 | this.children.push(box) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/neditor/engine/layout/line_wrapping.ts: -------------------------------------------------------------------------------- 1 | export enum WrapAtPolicy { 2 | kWrapAtPolicyBefore, 3 | kWrapAtPolicyLastOpportunityWithinWidth, 4 | kWrapAtPolicyLastOpportunity, 5 | kWrapAtPolicyFirstOpportunity, 6 | } 7 | 8 | // Wrapping is only performed at an allowed break point, called a soft wrap 9 | // opportunity. 10 | // https://www.w3.org/TR/css-text-3/#line-breaking 11 | // 'normal': Lines may break only at allowed break points. 12 | // 'break-word': An unbreakable 'word' may be broken at an an arbitrary point... 13 | // https://www.w3.org/TR/css-text-3/#overflow-wrap 14 | export enum WrapOpportunityPolicy { 15 | kWrapOpportunityPolicyNormal, 16 | kWrapOpportunityPolicyBreakWord, 17 | kWrapOpportunityPolicyBreakWordOrNormal, 18 | } 19 | 20 | export enum WrapResult { 21 | kWrapResultNoWrap, 22 | kWrapResultWrapBefore, 23 | kWrapResultSplitWrap, 24 | } 25 | 26 | export function ShouldProcessWrapOpportunityPolicy( 27 | wrap_opportunity_policy: WrapOpportunityPolicy, 28 | does_style_allow_break_word: boolean): boolean { 29 | if (does_style_allow_break_word) { 30 | return wrap_opportunity_policy !== WrapOpportunityPolicy.kWrapOpportunityPolicyNormal; 31 | } else { 32 | return wrap_opportunity_policy !== WrapOpportunityPolicy.kWrapOpportunityPolicyBreakWordOrNormal; 33 | } 34 | // return wrap_opportunity_policy != WrapOpportunityPolicy.kWrapOpportunityPolicyBreakWord 35 | // || does_style_allow_break_word; 36 | } 37 | -------------------------------------------------------------------------------- /packages/neditor/engine/layout/map_coordinates_flags.ts: -------------------------------------------------------------------------------- 1 | export enum MapCoordinatesMode { 2 | // Only needed in some special cases to intentionally ignore transforms. 3 | kIgnoreTransforms = 1 << 2, 4 | 5 | kTraverseDocumentBoundaries = 1 << 3, 6 | 7 | // Ignore offset adjustments caused by position:sticky calculations when 8 | // walking the chain. 9 | kIgnoreStickyOffset = 1 << 4, 10 | 11 | // Ignore scroll offset from container, i.e. scrolling has no effect on mapped 12 | // position. 13 | kIgnoreScrollOffset = 1 << 5, 14 | 15 | // If the local root frame has a remote frame parent, apply the transformation 16 | // from the local root frame to the remote main frame. 17 | kApplyRemoteMainFrameTransform = 1 << 6, 18 | } 19 | export type MapCoordinatesFlags = number 20 | -------------------------------------------------------------------------------- /packages/neditor/engine/layout/r_tree.ts: -------------------------------------------------------------------------------- 1 | import RBush from 'rbush'; 2 | import type { TextBox } from "./text_box"; 3 | 4 | export interface IRTreeItem { 5 | minX: number, 6 | minY: number, 7 | maxX: number, 8 | maxY: number, 9 | } 10 | 11 | export type RTree = RBush 12 | 13 | export interface ITextBoxRTreeItem extends IRTreeItem { 14 | box: TextBox 15 | } 16 | 17 | export function textBoxToRTreeItem(box: TextBox) { 18 | const rect = box.GetClientRect(); 19 | return { 20 | minX: rect.left().toFloat(), 21 | minY: rect.top().toFloat(), 22 | maxX: rect.right().toFloat(), 23 | maxY: rect.bottom().toFloat(), 24 | box, 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/neditor/engine/loader/354.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WaiSiuKei/neditor/07fb259149d9d74e0b7a65c8ad60841d86e3b95e/packages/neditor/engine/loader/354.jpg -------------------------------------------------------------------------------- /packages/neditor/engine/loader/loader_types.ts: -------------------------------------------------------------------------------- 1 | export enum LoadResponseType { 2 | kLoadResponseContinue, 3 | kLoadResponseAbort, 4 | }; 5 | 6 | -------------------------------------------------------------------------------- /packages/neditor/engine/loader/net_fetcher.ts: -------------------------------------------------------------------------------- 1 | // NetFetcher is for fetching data from the network. 2 | import { Fetcher, Handler } from './fetcher'; 3 | // @ts-ignore 4 | import img from './354.jpg'; 5 | 6 | export class NetFetcher extends Fetcher { 7 | constructor( 8 | url: string, 9 | handler: Handler 10 | ) { 11 | super(handler); 12 | this.Start(); 13 | fetch(img).then((resp: Response) => resp.arrayBuffer()).then((buf: ArrayBuffer) => { 14 | let data = buf; 15 | this.handler().OnReceivedPassed(this, data); 16 | 17 | this.handler().OnDone(this); 18 | }); 19 | // this.OnURLFetchComplete(); 20 | } 21 | 22 | private Start() { 23 | // const GURL& original_url = url_fetcher_.GetOriginalURL(); 24 | // security_callback_.Run(original_url, false /* did not redirect */)) { 25 | // url_fetcher_.Start(); 26 | } 27 | 28 | // private OnURLFetchComplete() { 29 | // let data = ''; 30 | // this.handler().OnReceivedPassed(this, data); 31 | // 32 | // this.handler().OnDone(this); 33 | // } 34 | }; 35 | -------------------------------------------------------------------------------- /packages/neditor/engine/math/insets_f.ts: -------------------------------------------------------------------------------- 1 | import { InsetsBase } from './insets'; 2 | 3 | export class InsetsF extends InsetsBase {} 4 | -------------------------------------------------------------------------------- /packages/neditor/engine/math/point_f.ts: -------------------------------------------------------------------------------- 1 | import { PointBase } from './point_base'; 2 | import { Vector2dF } from './vector2d_f'; 3 | 4 | export class PointF extends PointBase {} 5 | 6 | export function PointAtOffsetFromOrigin( offset_from_origin : Vector2dF) { 7 | return new PointF(offset_from_origin.x(), offset_from_origin.y()); 8 | } 9 | -------------------------------------------------------------------------------- /packages/neditor/engine/math/rect.ts: -------------------------------------------------------------------------------- 1 | import { RectBase } from './rect_base'; 2 | 3 | export class Rect extends RectBase {} 4 | -------------------------------------------------------------------------------- /packages/neditor/engine/math/rect_f.ts: -------------------------------------------------------------------------------- 1 | import { RectBase } from './rect_base'; 2 | import type { RectLayoutUnit } from "../layout/rect_layout_unit"; 3 | 4 | export class RectF extends RectBase { 5 | static fromRectLayoutUnit(u: RectLayoutUnit): RectF { 6 | const origin = u.origin() 7 | const size = u.size() 8 | return new RectF(origin.x().toFloat(), origin.y().toFloat(), size.width().toFloat(), size.height().toFloat()) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/neditor/engine/math/size_f.ts: -------------------------------------------------------------------------------- 1 | // A floating-point version of Size. 2 | import { SizeBase } from './size'; 3 | 4 | export class SizeF extends SizeBase { 5 | // SizeF() : SizeBase(0, 0) {} 6 | // SizeF(float width, float height) : SizeBase(width, height) {} 7 | // ~SizeF() {} 8 | 9 | // void Scale(float scale) { Scale(scale, scale); } 10 | // 11 | // void Scale(float x_scale, float y_scale) { 12 | // SetSize(width() * x_scale, height() * y_scale); 13 | // } 14 | // 15 | // std::string ToString() const; 16 | } 17 | 18 | // inline bool operator==(const SizeF& lhs, const SizeF& rhs) { 19 | // return lhs.width() == rhs.width() && lhs.height() == rhs.height(); 20 | // } 21 | // 22 | // inline bool operator!=(const SizeF& lhs, const SizeF& rhs) { 23 | // return !(lhs == rhs); 24 | // } 25 | // 26 | // SizeF ScaleSize(const SizeF& p, float x_scale, float y_scale); 27 | // 28 | // inline SizeF ScaleSize(const SizeF& p, float scale) { 29 | // return ScaleSize(p, scale, scale); 30 | // } 31 | // 32 | // inline std::ostream& operator<<(std::ostream& stream, const SizeF& size) { 33 | // stream << "{width=" << size.width() << " height=" << size.height() << "}"; 34 | // return stream; 35 | // } 36 | -------------------------------------------------------------------------------- /packages/neditor/engine/render_tree/background.ts: -------------------------------------------------------------------------------- 1 | import { RectF } from '../math/rect_f'; 2 | import { Brush } from './brush'; 3 | 4 | export class Background { 5 | constructor( 6 | public colorBrush: Brush, 7 | public rect: RectF, 8 | ) {} 9 | } 10 | -------------------------------------------------------------------------------- /packages/neditor/engine/render_tree/clear_rect_node.ts: -------------------------------------------------------------------------------- 1 | // A simple rectangle, filled (without blending) with a specified color. 2 | import { Node } from './node'; 3 | import { RectF } from '../math/rect_f'; 4 | import { ColorRGBA } from './color_rgba'; 5 | import { baseGetTypeId as _GetTypeId } from '../base/type_id'; 6 | import { NodeVisitor } from './node_visitor'; 7 | 8 | class Builder { 9 | // The destination rectangle. 10 | rect: RectF; 11 | 12 | // The clear color. 13 | color: ColorRGBA; 14 | constructor(rect: RectF, color: ColorRGBA) { 15 | this.rect = rect; 16 | this.color = color; 17 | } 18 | } 19 | 20 | export class ClearRectNode extends Node { 21 | private data_: Builder; 22 | 23 | constructor(rect: RectF, color: ColorRGBA) { 24 | super(); 25 | this.data_ = new Builder(rect, color); 26 | } 27 | 28 | Accept(visitor: NodeVisitor) { 29 | visitor.VisitClearRectNode(this); 30 | } 31 | GetBounds() { return this.data_.rect; } 32 | 33 | GetTypeId(): number { 34 | return _GetTypeId(ClearRectNode); 35 | } 36 | data() { return this.data_; } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /packages/neditor/engine/render_tree/font_provider.ts: -------------------------------------------------------------------------------- 1 | // The FontProvider class is an abstract base class representing a collection of 2 | // fonts with a matching style and size, which provides fonts for any given 3 | // character based upon what it considers to be the best match. 4 | import type { Font, FontStyle } from './font'; 5 | import { GlyphIndex } from './glyph'; 6 | 7 | export abstract class FontProvider { 8 | // The style of the fonts contained within the collection. 9 | abstract style(): FontStyle 10 | 11 | // The size of the fonts contained within the collection. 12 | abstract size(): number 13 | 14 | // Returns the font-glyph combination that the FontProvider considers to be 15 | // the best match for the passed in character. The returned font is guaranteed 16 | // to be non-NULL. However, the glyph index may be set to |kInvalidGlyphIndex| 17 | // if the returned font does not provide a glyph for the character. 18 | abstract GetCharacterFont(utf32_character: number): { font: Font, glyph_index: GlyphIndex } 19 | } 20 | 21 | 22 | -------------------------------------------------------------------------------- /packages/neditor/engine/render_tree/freehand_node.ts: -------------------------------------------------------------------------------- 1 | import { Node } from './node'; 2 | import { NodeVisitor } from './node_visitor'; 3 | import { RectF } from '../math/rect_f'; 4 | import { baseGetTypeId } from '../base/type_id'; 5 | import { Path } from "./path"; 6 | 7 | export class FreehandNode extends Node { 8 | constructor(path: Path) { 9 | super(); 10 | // @ts-ignore 11 | this.data_ = path; 12 | } 13 | 14 | Accept(visitor: NodeVisitor) { 15 | visitor.VisitFreehandNode(this); 16 | } 17 | 18 | GetBounds(): RectF { 19 | return new RectF(); 20 | } 21 | 22 | GetTypeId() { 23 | return baseGetTypeId(FreehandNode); 24 | } 25 | 26 | data() { 27 | return this.data_; 28 | } 29 | 30 | private data_: Path; 31 | }; 32 | -------------------------------------------------------------------------------- /packages/neditor/engine/render_tree/glyph.ts: -------------------------------------------------------------------------------- 1 | export type GlyphIndex = number 2 | export const kInvalidGlyphIndex: GlyphIndex = 0; 3 | export const kUnknownGlyphIndex: GlyphIndex = 0xffff; 4 | -------------------------------------------------------------------------------- /packages/neditor/engine/render_tree/glyph_buffer.ts: -------------------------------------------------------------------------------- 1 | // The GlyphBuffer class is a base class, which contains the data needed to 2 | // render shaped text. It is intended to be immutable and thread-safe. Since 3 | // GlyphBuffer objects may be created in the front-end, but must be accessed 4 | // by the rasterizer, it is expected that they will be downcast again to a 5 | // rasterizer-specific type through base::polymorphic_downcast(). 6 | import { RectF } from '../math/rect_f'; 7 | import { Disposable } from "../../base/common/lifecycle"; 8 | 9 | export class GlyphBuffer extends Disposable { 10 | private bounds_: RectF; 11 | constructor(bounds: RectF) { 12 | super() 13 | this.bounds_ = bounds; 14 | } 15 | 16 | GetBounds() { 17 | return this.bounds_; 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /packages/neditor/engine/render_tree/node.ts: -------------------------------------------------------------------------------- 1 | // A base class of all objects that form a render tree. 2 | import type { NodeVisitor } from './node_visitor'; 3 | import type { RectF } from '../math/rect_f'; 4 | import type { TypeId } from '../base/type_id'; 5 | import { Disposable } from "../../base/common/lifecycle"; 6 | 7 | let id_counter_ = 0; 8 | 9 | export abstract class Node extends Disposable { 10 | node_id_: number; 11 | constructor() { 12 | super() 13 | this.node_id_ = id_counter_++; 14 | } 15 | 16 | abstract Accept(visitor: NodeVisitor): void 17 | // Returns an axis-aligned bounding rectangle for this render tree node in 18 | // units of pixels. 19 | abstract GetBounds(): RectF 20 | 21 | // Returns an ID that is unique to the node type. This can be used to 22 | // polymorphically identify what type a node is. 23 | abstract GetTypeId(): TypeId 24 | 25 | // Number to help differentiate nodes. This is specific to the local process 26 | // and is not deterministic. Node identifiers from different processes may 27 | // overlap. This is intended to be used as a key when, for example, caching 28 | // render results of nodes. 29 | GetId(): number { 30 | return this.node_id_; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/neditor/engine/render_tree/path.ts: -------------------------------------------------------------------------------- 1 | import { ColorRGBA } from "./color_rgba"; 2 | import { Optional } from "../../base/common/typescript"; 3 | 4 | export enum PathCommandVerb { 5 | M = 'M', 6 | Q = 'Q', 7 | Z = 'Z' 8 | } 9 | 10 | export class Path { 11 | constructor( 12 | public d: string, 13 | public fill: Optional, 14 | public stroke: Optional, 15 | ) { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/neditor/engine/render_tree/shadow.ts: -------------------------------------------------------------------------------- 1 | // Describes a shadow effect that can be applied as a filter or to a shape. 2 | // The |blur_sigma| value is given in units of Gaussian standard deviations for 3 | // the Gaussian kernel that will be used to blur them. 4 | import { Vector2dF } from '../math/vector2d_f'; 5 | import { ColorRGBA } from './color_rgba'; 6 | import { RectF } from '../math/rect_f'; 7 | 8 | export class Shadow { 9 | offset: Vector2dF; 10 | blur_sigma: number; 11 | color: ColorRGBA; 12 | constructor( 13 | offset: Vector2dF, 14 | blur_sigma: number, 15 | color: ColorRGBA, 16 | ) { 17 | this.offset = offset; 18 | this.blur_sigma = blur_sigma; 19 | this.color = color; 20 | } 21 | // Since the blur parameters represent standard deviations, most of the 22 | // blur is contained within 3 of them, so report that as the extent of the 23 | // blur. 24 | BlurExtent(): Vector2dF { 25 | return new Vector2dF(this.blur_sigma * 3, this.blur_sigma * 3); 26 | } 27 | 28 | // Returns a bounding rectangle for the shadow if it were to be applied to 29 | // the input bounding rectangle. 30 | ToShadowBounds(source_bound: RectF): RectF { 31 | let ret = new RectF(source_bound); 32 | ret.Offset(this.offset); 33 | 34 | let blur_extent: Vector2dF = this.BlurExtent(); 35 | ret.Outset(blur_extent.x(), blur_extent.y()); 36 | return ret; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/neditor/engine/renderer/backend/render_target.ts: -------------------------------------------------------------------------------- 1 | import { Size } from '../../math/size'; 2 | import { Surface } from 'canvaskit-wasm'; 3 | import {CanvasKit} from '@neditor/skia' 4 | 5 | export class RenderTarget { 6 | private size_: Size; 7 | private surface_?: Surface; 8 | 9 | constructor( 10 | canvas: HTMLCanvasElement, 11 | ) { 12 | this.size_ = new Size(canvas.width, canvas.height); 13 | this.surface_ = CanvasKit.MakeCanvasSurface(canvas)!; 14 | } 15 | 16 | get surface() { 17 | return this.surface_!; 18 | } 19 | 20 | GetSize() { 21 | return this.size_; 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /packages/neditor/engine/renderer/rasterizer/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WaiSiuKei/neditor/07fb259149d9d74e0b7a65c8ad60841d86e3b95e/packages/neditor/engine/renderer/rasterizer/Roboto-Regular.ttf -------------------------------------------------------------------------------- /packages/neditor/engine/renderer/rasterizer/SourceHanSansCN-Normal.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WaiSiuKei/neditor/07fb259149d9d74e0b7a65c8ad60841d86e3b95e/packages/neditor/engine/renderer/rasterizer/SourceHanSansCN-Normal.ttf -------------------------------------------------------------------------------- /packages/neditor/engine/renderer/rasterizer/common/utils.ts: -------------------------------------------------------------------------------- 1 | export function IsOpaque(opacity: number): boolean { 2 | return opacity >= 254.5 / 255.0; 3 | } 4 | -------------------------------------------------------------------------------- /packages/neditor/engine/renderer/rasterizer/render_tree_node_visitor_draw_state.ts: -------------------------------------------------------------------------------- 1 | import { Canvas } from 'canvaskit-wasm'; 2 | 3 | export class RenderTreeNodeVisitorDrawState { 4 | render_target: Canvas; 5 | opacity: number = 1; 6 | // True if the current clip is a rectangle or not. If it is not, we need 7 | // to enable blending when rendering clipped rectangles. 8 | clip_is_rect = true; 9 | constructor(render_target: Canvas) { 10 | this.render_target = render_target; 11 | } 12 | 13 | // glm::mat4 transform_3d; 14 | // transform_3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) {} 15 | 16 | }; 17 | -------------------------------------------------------------------------------- /packages/neditor/engine/renderer/rasterizer/sk_glyph_buffer.ts: -------------------------------------------------------------------------------- 1 | // Describes a render_tree::GlyphBuffer using Skia. This object contain all of 2 | // the information needed by Skia to render glyphs and is both immutable and 3 | // thread-safe. 4 | import { GlyphBuffer as BaseGlyphBuffer } from '../../render_tree/glyph_buffer'; 5 | import { TextBlob } from 'canvaskit-wasm'; 6 | import { RectF } from '../../math/rect_f'; 7 | import { Optional } from '@neditor/core/base/common/typescript'; 8 | import { IDisposable } from "../../../base/common/lifecycle"; 9 | import { isNil } from "../../../base/common/type"; 10 | 11 | // text_blob 是 optional,因为不可见字符不需要渲染 12 | export class GlyphBuffer extends BaseGlyphBuffer { 13 | constructor( 14 | bounds: RectF, 15 | private chars_: string, 16 | text_blob: Optional, 17 | ) { 18 | super(bounds); 19 | this.text_blob_ = text_blob; 20 | } 21 | 22 | GetTextBlob() { 23 | return this.text_blob_; 24 | } 25 | dispose() { 26 | // FIXME:text_blob 是引用,不能 delete 27 | // if (this.text_blob_) { 28 | // this.text_blob_.deleteLater(); 29 | // } 30 | } 31 | 32 | private text_blob_: Optional; 33 | }; 34 | -------------------------------------------------------------------------------- /packages/neditor/engine/renderer/rasterizer/skia/effects/SkGradientShader.ts: -------------------------------------------------------------------------------- 1 | export class SkGradientShader { 2 | static Flags = { 3 | /** By default gradients will interpolate their colors in unpremul space 4 | * and then premultiply each of the results. By setting this flag, the 5 | * gradients will premultiply their colors first, and then interpolate 6 | * between them. 7 | * example: https://fiddle.skia.org/c/@GradientShader_MakeLinear 8 | */ 9 | kInterpolateColorsInPremul_Flag: 1 << 0, 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /packages/neditor/engine/renderer/rasterizer/skia/sk_color.ts: -------------------------------------------------------------------------------- 1 | export type SkColor = number[] 2 | -------------------------------------------------------------------------------- /packages/neditor/engine/renderer/rasterizer/skia/sk_font.ts: -------------------------------------------------------------------------------- 1 | import { Font } from 'canvaskit-wasm'; 2 | 3 | export type SkFont = Font 4 | -------------------------------------------------------------------------------- /packages/neditor/engine/renderer/rasterizer/skia/sk_image.ts: -------------------------------------------------------------------------------- 1 | import { Image } from 'canvaskit-wasm'; 2 | 3 | export type SkImage = Image 4 | -------------------------------------------------------------------------------- /packages/neditor/engine/renderer/rasterizer/skia/sk_matrix.ts: -------------------------------------------------------------------------------- 1 | export type SkMatrix = number[] 2 | -------------------------------------------------------------------------------- /packages/neditor/engine/renderer/rasterizer/skia/sk_path.ts: -------------------------------------------------------------------------------- 1 | import { Path } from 'canvaskit-wasm'; 2 | 3 | export type SkPath = Path; 4 | -------------------------------------------------------------------------------- /packages/neditor/engine/renderer/rasterizer/skia/sk_point.ts: -------------------------------------------------------------------------------- 1 | export type SkPoint = [number, number] 2 | -------------------------------------------------------------------------------- /packages/neditor/engine/renderer/rasterizer/skia/sk_rect.ts: -------------------------------------------------------------------------------- 1 | export type SkRect = number[] | Float32Array 2 | // left, top, right, bottom 3 | export function SkRect_MakeXYWH(x: number, y: number, width: number, height: number): SkRect { 4 | return [x, y, x + width, y + height]; 5 | } 6 | export function SkRect_MakeLeftTopRightBottom(left: number, top: number, right: number, bottom: number): SkRect { 7 | return [left, top, right, bottom]; 8 | } 9 | -------------------------------------------------------------------------------- /packages/neditor/engine/renderer/rasterizer/skia/sk_scalar.ts: -------------------------------------------------------------------------------- 1 | export type SkScalar = number 2 | -------------------------------------------------------------------------------- /packages/neditor/engine/renderer/rasterizer/skia/sk_typeface.ts: -------------------------------------------------------------------------------- 1 | import { Typeface } from 'canvaskit-wasm'; 2 | 3 | export type SkTypeface = Typeface 4 | -------------------------------------------------------------------------------- /packages/neditor/engine/renderer/rasterizer/type_conversions.ts: -------------------------------------------------------------------------------- 1 | import { Matrix3F } from '../../math/matrix3_f'; 2 | import { SkMatrix } from './skia/sk_matrix'; 3 | import { RectBase } from '../../math/rect_base'; 4 | import { SkRect, SkRect_MakeXYWH } from './skia/sk_rect'; 5 | 6 | export function CobaltMatrixToSkia(cobalt_matrix: Matrix3F): SkMatrix { 7 | // Shorten the variable name. 8 | let cm = cobalt_matrix; 9 | 10 | return [cm.Get(0, 0), cm.Get(0, 1), cm.Get(0, 2), cm.Get(1, 0), 11 | cm.Get(1, 1), cm.Get(1, 2), cm.Get(2, 0), cm.Get(2, 1), 12 | cm.Get(2, 2)]; 13 | } 14 | 15 | export function CobaltRectFToSkiaRect(rect: RectBase): SkRect { 16 | return SkRect_MakeXYWH(rect.x(), rect.y(), rect.width(), rect.height()); 17 | } 18 | -------------------------------------------------------------------------------- /packages/neditor/env.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | const SFC: Vue.Component; 3 | export default SFC; 4 | } 5 | -------------------------------------------------------------------------------- /packages/neditor/global.d.ts: -------------------------------------------------------------------------------- 1 | interface CanvasRenderingContext2D { 2 | // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/roundRect 3 | roundRect?: ( 4 | x: number, 5 | y: number, 6 | width: number, 7 | height: number, 8 | radii: 9 | | number // [all-corners] 10 | | [number] // [all-corners] 11 | | [number, number] // [top-left-and-bottom-right, top-right-and-bottom-left] 12 | | [number, number, number] // [top-left, top-right-and-bottom-left, bottom-right] 13 | | [number, number, number, number], // [top-left, top-right, bottom-right, bottom-left] 14 | ) => void; 15 | } 16 | -------------------------------------------------------------------------------- /packages/neditor/index.ts: -------------------------------------------------------------------------------- 1 | import 'normalize.css'; 2 | import '@neditor/core/platform/tool/common/toolRegistry'; 3 | import '@neditor/core/platform/tool/browser/toolService'; 4 | import '@neditor/core/platform/input/browser/inputService'; 5 | import '@neditor/core/platform/canvas/common/canvasService'; 6 | import '@neditor/core/platform/model/common/modelService'; 7 | import '@neditor/core/platform/keybinding/browser/keybindingService'; 8 | import '@neditor/core/platform/contextkey/browser/contextKeyService'; 9 | import '@neditor/core/platform/undoRedo/common/undoRedoService'; 10 | import '@neditor/core/platform/lifecycle/browser/lifecycleService'; 11 | import '@neditor/core/platform/commands/common/commandService'; 12 | import '@neditor/core/workbench/browser/parts/editor/editorPart'; 13 | import '@neditor/core/workbench/contrib/action/action.contributions'; 14 | import '@neditor/core/workbench/contrib/tool/tool.contributions'; 15 | import { create } from './workbench/browser/web.factory'; 16 | // import './editor'; 17 | 18 | create(document.body, {}); 19 | 20 | -------------------------------------------------------------------------------- /packages/neditor/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@neditor/core", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "vite", 7 | "build": "vite build" 8 | }, 9 | "dependencies": { 10 | "@types/big.js": "6.1.6", 11 | "@types/react": "^18.2.20", 12 | "@vue/reactivity": "^3.3.4", 13 | "@vue/shared": "3.2.47", 14 | "big.js": "6.2.1", 15 | "immer": "^10.0.2", 16 | "is-plain-object": "^5.0.0", 17 | "jpeg-js": "0.4.4", 18 | "lodash-es": "4.17.21", 19 | "normalize.css": "8.0.1", 20 | "orderedmap": "2.1.0", 21 | "perfect-freehand": "1.2.0", 22 | "randomcolor": "0.6.2", 23 | "rbush": "3.0.1", 24 | "react": "^18.2.0", 25 | "react-dom": "^18.2.0", 26 | "slate": "^0.94.1", 27 | "slate-react": "^0.98.1", 28 | "tailwindcss": "3.2.7", 29 | "vue": "3.2.39", 30 | "w3c-keyname": "2.2.6", 31 | "yjs": "13.6.6" 32 | }, 33 | "devDependencies": { 34 | "@esbuild-plugins/node-globals-polyfill": "0.1.1", 35 | "@esbuild-plugins/node-modules-polyfill": "0.1.4", 36 | "@originjs/vite-plugin-commonjs": "1.0.3", 37 | "@types/lodash-es": "4.17.6", 38 | "@types/node": "18.15.3", 39 | "@types/randomcolor": "0.5.7", 40 | "@types/rbush": "3.0.0", 41 | "@vitejs/plugin-vue": "3.2.0", 42 | "autoprefixer": "10.4.14", 43 | "postcss": "8.4.21", 44 | "typescript": "5.0.4", 45 | "vite": "3.2.4" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/neditor/platform/canvas/browser/canvasViews.ts: -------------------------------------------------------------------------------- 1 | import { createDecorator } from '../../instantiation/common/instantiation'; 2 | 3 | export const ICanvasViewsService = createDecorator('ICanvasViewsService'); 4 | 5 | export interface ICanvasViewsService { 6 | readonly _serviceBrand: undefined; 7 | 8 | readonly isReady: boolean; 9 | readonly whenReady: Promise; 10 | readonly whenRestored: Promise; 11 | 12 | restore(): void | Promise; 13 | create(parent: HTMLElement): void; 14 | } 15 | -------------------------------------------------------------------------------- /packages/neditor/platform/canvas/common/canvas.ts: -------------------------------------------------------------------------------- 1 | /* --------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *-------------------------------------------------------------------------------------------- */ 5 | 6 | import { Event } from '@neditor/core/base/common/event'; 7 | import { createDecorator } from '@neditor/core/platform/instantiation/common/instantiation'; 8 | import { ICanvas } from '@neditor/core/canvas/canvas/canvas'; 9 | import { Optional } from '@neditor/core/base/common/typescript'; 10 | 11 | export const ICanvasService = createDecorator('ICanvasService'); 12 | 13 | export interface ICanvasService { 14 | readonly _serviceBrand: undefined; 15 | 16 | readonly onCanvasAdded: Event; 17 | readonly onCanvasRemoved: Event; 18 | readonly onDidActiveCanvasChange: Event; 19 | 20 | addCanvas(canvas: ICanvas): void; 21 | removeCanvas(canvas: ICanvas): void; 22 | listCanvases(): readonly ICanvas[]; 23 | 24 | getFocusedCanvas(): Optional; 25 | getActiveCanvas(): Optional; 26 | } 27 | -------------------------------------------------------------------------------- /packages/neditor/platform/input/common/input.ts: -------------------------------------------------------------------------------- 1 | export enum HitTestLevel { 2 | None, 3 | BlockBox, 4 | InlineBox, 5 | InlineContent, 6 | } 7 | -------------------------------------------------------------------------------- /packages/neditor/platform/input/common/inputService.ts: -------------------------------------------------------------------------------- 1 | import { createDecorator } from '../../instantiation/common/instantiation'; 2 | import { ICanvas } from '@neditor/core/canvas/canvas/canvas'; 3 | import { IEventFilter } from '../browser/event'; 4 | 5 | export const IInputService = createDecorator('IInputManager'); 6 | 7 | export interface IInputService extends IEventFilter { 8 | _serviceBrand: undefined; 9 | 10 | addTrackedCanvas(canvas: ICanvas): void; 11 | removeTrackedCanvas(canvas: ICanvas): void; 12 | 13 | // handleOnWillChangeTool(canvas: ICanvas): void; 14 | // handleOnDidChangeTool(canvas: ICanvas): void; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /packages/neditor/platform/instantiation/common/descriptors.ts: -------------------------------------------------------------------------------- 1 | /* --------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *-------------------------------------------------------------------------------------------- */ 5 | 6 | export class SyncDescriptor { 7 | static __SyncDescriptorBrand: undefined; 8 | readonly ctor: any; 9 | readonly staticArguments: any[]; 10 | readonly supportsDelayedInstantiation: boolean; 11 | 12 | constructor( 13 | ctor: new (...args: any[]) => T, 14 | staticArguments: any[] = [], 15 | supportsDelayedInstantiation = false, 16 | ) { 17 | this.ctor = ctor; 18 | this.staticArguments = staticArguments; 19 | this.supportsDelayedInstantiation = supportsDelayedInstantiation; 20 | } 21 | } 22 | 23 | export interface SyncDescriptor0 { 24 | readonly ctor: new () => T; 25 | } 26 | -------------------------------------------------------------------------------- /packages/neditor/platform/layout/browser/layoutService.ts: -------------------------------------------------------------------------------- 1 | import { createDecorator } from '@neditor/core/platform/instantiation/common/instantiation'; 2 | 3 | export const ILayoutService = createDecorator('layoutService'); 4 | 5 | export interface ILayoutService { 6 | readonly _serviceBrand: undefined; 7 | } 8 | -------------------------------------------------------------------------------- /packages/neditor/platform/model/common/helper.ts: -------------------------------------------------------------------------------- 1 | import { TextNodeModelProxy } from './model'; 2 | 3 | export class PrelimTextNodeModelProxy extends TextNodeModelProxy { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /packages/neditor/platform/model/common/location.ts: -------------------------------------------------------------------------------- 1 | import { IIdentifier } from "../../../common/common"; 2 | import { Scope } from "../../../canvas/canvasCommon/scope"; 3 | 4 | export enum DirectionType { 5 | backward, 6 | forward, 7 | inward, 8 | self, 9 | ancestors, 10 | descendants, 11 | } 12 | 13 | export interface ILocation { 14 | ref: IIdentifier; 15 | direction?: DirectionType; 16 | } 17 | 18 | export interface IScopedLocation extends ILocation { 19 | scope: Scope; 20 | } 21 | -------------------------------------------------------------------------------- /packages/neditor/platform/model/common/modelChangeParticipant.ts: -------------------------------------------------------------------------------- 1 | import { Disposable, IDisposable, toDisposable } from "../../../base/common/lifecycle"; 2 | import { ICanvasModel, IModelChangeParticipant } from "./model"; 3 | import { IModelContentChangedEvent } from "./modelEvents"; 4 | import { NOTIMPLEMENTED } from "../../../base/common/notreached"; 5 | import { insert } from "../../../base/common/array"; 6 | 7 | export class ModelChangeParticipant extends Disposable { 8 | private readonly saveParticipants: IModelChangeParticipant[] = []; 9 | 10 | constructor() { 11 | super(); 12 | } 13 | 14 | addSaveParticipant(participant: IModelChangeParticipant): IDisposable { 15 | const remove = insert(this.saveParticipants, participant); 16 | 17 | return toDisposable(() => remove()); 18 | } 19 | 20 | participate(model: ICanvasModel, changes: IModelContentChangedEvent) { 21 | for (const saveParticipant of this.saveParticipants) { 22 | try { 23 | saveParticipant.participate(model, changes); 24 | } catch (err) { 25 | NOTIMPLEMENTED(); 26 | } 27 | } 28 | } 29 | 30 | dispose(): void { 31 | this.saveParticipants.splice(0, this.saveParticipants.length); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/neditor/platform/model/common/modelImpl.ts: -------------------------------------------------------------------------------- 1 | import { Optional } from '../../../base/common/typescript'; 2 | import { URI } from '../../../base/common/uri'; 3 | import { ModelOperator } from './modelOperator'; 4 | import { IDocumentModel } from '../../../common/model'; 5 | import { ICanvasModel, IModelService } from './model'; 6 | import { NOTIMPLEMENTED } from "../../../base/common/notreached"; 7 | import * as Y from 'yjs'; 8 | 9 | export class CanvasModel extends ModelOperator implements ICanvasModel { 10 | constructor(rawModel: IDocumentModel, doc: Y.Doc, associatedResource: Optional, @IModelService modelService: IModelService) { 11 | super(rawModel, doc, associatedResource, modelService); 12 | } 13 | replaceModel(rawModel: IDocumentModel): void { 14 | NOTIMPLEMENTED() 15 | // this._doc = this._processInput(rawModel); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/neditor/platform/runtime_enabled_features.ts: -------------------------------------------------------------------------------- 1 | export class RuntimeEnabledFeatures { 2 | static CompositeAfterPaintEnabled() { 3 | return true; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/neditor/platform/storage/common/storageIpc.ts: -------------------------------------------------------------------------------- 1 | /* --------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *-------------------------------------------------------------------------------------------- */ 5 | 6 | import { Emitter, Event } from '@neditor/core/base/common/event'; 7 | import { Disposable } from '@neditor/core/base/common/lifecycle'; 8 | import { 9 | IStorageDatabase, 10 | IStorageItemsChangeEvent, 11 | IUpdateRequest, 12 | } from '@neditor/core/base/parts/storage/common/storage'; 13 | 14 | export type Key = string; 15 | export type Value = string; 16 | export type Item = [Key, Value]; 17 | 18 | export interface IBaseSerializableStorageRequest { 19 | readonly workspace: undefined; 20 | } 21 | 22 | export interface ISerializableUpdateRequest extends IBaseSerializableStorageRequest { 23 | insert?: Item[]; 24 | delete?: Key[]; 25 | } 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /packages/neditor/platform/tool/common/toolRegistry.ts: -------------------------------------------------------------------------------- 1 | import { GenericRegistry } from '../../registry/common/genericRegistry'; 2 | import { IToolRegistry, IToolFactory, Tool } from './tool'; 3 | import { Registry } from '../../registry/common/platform'; 4 | import { Optional } from '../../../base/common/typescript'; 5 | 6 | export class ToolRegistry extends GenericRegistry implements IToolRegistry { 7 | private _defaultToolID: Optional; 8 | registerWithShortcut(factory: IToolFactory, asDefaultTool?: boolean): void { 9 | this.add(factory); 10 | if (asDefaultTool) { 11 | this._defaultToolID = factory.id; 12 | } 13 | } 14 | getDefaultOne() { 15 | return this._defaultToolID; 16 | } 17 | } 18 | 19 | Registry.add(Tool, new ToolRegistry()); 20 | -------------------------------------------------------------------------------- /packages/neditor/platform/undoRedo/common/undoRedo.ts: -------------------------------------------------------------------------------- 1 | import { URI } from "../../../base/common/uri"; 2 | import { IDisposable } from "../../../base/common/lifecycle"; 3 | import { createDecorator } from "../../instantiation/common/instantiation"; 4 | 5 | export const IUndoRedoService = createDecorator('undoRedoService'); 6 | 7 | export interface IUndoRedoService { 8 | readonly _serviceBrand: undefined; 9 | canUndo(resource: URI | UndoRedoSource): boolean; 10 | undo(resource: URI | UndoRedoSource): Promise | void; 11 | 12 | canRedo(resource: URI | UndoRedoSource): boolean; 13 | redo(resource: URI | UndoRedoSource): Promise | void; 14 | } 15 | 16 | export class UndoRedoSource { 17 | private static _ID = 0; 18 | 19 | static isInstance(other: any): other is UndoRedoSource { 20 | return Reflect.get(other, '__classBland__') === 'UndoRedoSource'; 21 | } 22 | 23 | public readonly __classBland__ = 'UndoRedoSource'; 24 | public readonly id: number; 25 | private order: number; 26 | 27 | constructor() { 28 | this.id = UndoRedoSource._ID++; 29 | this.order = 1; 30 | } 31 | 32 | public nextOrder(): number { 33 | if (this.id === 0) { 34 | return 0; 35 | } 36 | return this.order++; 37 | } 38 | 39 | public static None = new UndoRedoSource(); 40 | } 41 | -------------------------------------------------------------------------------- /packages/neditor/platform/workbenchLayout/common/workbenchLayout.ts: -------------------------------------------------------------------------------- 1 | import { Part } from '../../../workbench/browser/part'; 2 | import { createDecorator } from '../../instantiation/common/instantiation'; 3 | 4 | export const IWorkbenchLayoutService = createDecorator('workbenchLayoutService'); 5 | 6 | export const enum Parts { 7 | EDITOR_PART = 'workbench.parts.editor', 8 | TOOLBAT_PART = 'workbench.parts.toolbat', 9 | } 10 | 11 | export interface IWorkbenchLayoutService { 12 | readonly _serviceBrand: undefined; 13 | 14 | readonly container: HTMLElement; 15 | /** 16 | * A promise for to await the `isRestored()` condition to be `true`. 17 | */ 18 | readonly whenRestored: Promise; 19 | 20 | /** 21 | * Register a part to participate in the layout. 22 | */ 23 | registerPart(part: Part): void; 24 | } 25 | -------------------------------------------------------------------------------- /packages/neditor/platform/workspace/common/workspace.ts: -------------------------------------------------------------------------------- 1 | export interface IBaseWorkspaceIdentifier { 2 | /** 3 | * Every workspace (multi-root, single folder or empty) 4 | * has a unique identifier. It is not possible to open 5 | * a workspace with the same `id` in multiple windows 6 | */ 7 | readonly id: string; 8 | } 9 | 10 | export interface IEmptyWorkspaceIdentifier extends IBaseWorkspaceIdentifier { 11 | } 12 | 13 | export type IAnyWorkspaceIdentifier = IEmptyWorkspaceIdentifier; 14 | -------------------------------------------------------------------------------- /packages/neditor/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /packages/neditor/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | './index.html', 5 | './**/*.{js,jsx,ts,tsx,vue}', 6 | ], 7 | theme: { 8 | extend: {}, 9 | }, 10 | plugins: [], 11 | } 12 | -------------------------------------------------------------------------------- /packages/neditor/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "ES2015", 4 | "moduleResolution": "node", 5 | "target": "ESNext", 6 | "lib": [ 7 | "DOM", 8 | "DOM.Iterable", 9 | "ES6", 10 | "ES2015", 11 | "ESNext" 12 | ], 13 | "sourceMap": false, 14 | "declaration": false, 15 | "newLine": "LF", 16 | "allowJs": true, 17 | "experimentalDecorators": true, 18 | "skipLibCheck": true, 19 | "esModuleInterop": true, 20 | "noImplicitAny": true, 21 | "resolveJsonModule": true, 22 | "downlevelIteration": true, 23 | "isolatedModules": true, 24 | "types": [ 25 | "@types/big.js" 26 | ], 27 | "strict": true, 28 | "jsx": "react", 29 | }, 30 | "exclude": [ 31 | "postcss.config.js", 32 | "tailwind.config.js", 33 | "**/slate-vue/**/*.ts", 34 | "**/slate-vue/**/*.tsx" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /packages/neditor/workbench/browser/part.ts: -------------------------------------------------------------------------------- 1 | /* --------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *-------------------------------------------------------------------------------------------- */ 5 | export abstract class Part { 6 | protected parent: HTMLElement | undefined; 7 | // private contentArea: HTMLElement | undefined; 8 | 9 | constructor(public id: string) { 10 | } 11 | 12 | // create(parent: HTMLElement, options?: object): void { 13 | // this.parent = parent; 14 | // this.contentArea = this.createContentArea(parent, options); 15 | // } 16 | // 17 | // 18 | // /** 19 | // * Subclasses override to provide a content area implementation. 20 | // */ 21 | // protected createContentArea(parent: HTMLElement, options?: object): HTMLElement | undefined { 22 | // return undefined; 23 | // } 24 | // 25 | // element!: HTMLElement; 26 | // // #endregion 27 | } 28 | -------------------------------------------------------------------------------- /packages/neditor/workbench/browser/parts/components/IconButton.vue: -------------------------------------------------------------------------------- 1 | 10 | 18 | 19 | 52 | 53 | -------------------------------------------------------------------------------- /packages/neditor/workbench/browser/parts/editor/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 26 | 35 | -------------------------------------------------------------------------------- /packages/neditor/workbench/browser/parts/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | body { 7 | @apply bg-white overflow-hidden; 8 | overscroll-behavior-x: none; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/neditor/workbench/browser/parts/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --color-surface-canvas: #eee; 3 | --color-surface-panel: #fff; 4 | --color-surface-subtle: #f3f3f3; 5 | --color-divider: #eee; 6 | --color-foreground-600: #333; 7 | --color-foreground-500: #444; 8 | --color-foreground-400: #555; 9 | --color-foreground-300: #666; 10 | --color-foreground-200: #888; 11 | --color-foreground-100: #999; 12 | --color-primary: #0044ff; 13 | --color-white: #fff; 14 | --shadow-popup: 0px 2px 4px rgba(0, 0, 0, 0.1), 15 | 0px 0px 0px 1px rgba(0, 0, 0, 0.05); 16 | 17 | --colors-hover: #ececec; 18 | --colors-selected: rgba(66, 133, 244, 1); 19 | } 20 | -------------------------------------------------------------------------------- /packages/neditor/workbench/browser/parts/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 13 | -------------------------------------------------------------------------------- /packages/neditor/workbench/browser/parts/injects.ts: -------------------------------------------------------------------------------- 1 | export const Injects = { 2 | 'instantiationService': 'instantiationService', 3 | } 4 | -------------------------------------------------------------------------------- /packages/neditor/workbench/browser/parts/toolbar/EraserButton.vue: -------------------------------------------------------------------------------- 1 | 12 | 22 | -------------------------------------------------------------------------------- /packages/neditor/workbench/browser/parts/toolbar/PanButton.vue: -------------------------------------------------------------------------------- 1 | 9 | 19 | -------------------------------------------------------------------------------- /packages/neditor/workbench/browser/parts/toolbar/PencilButton.vue: -------------------------------------------------------------------------------- 1 | 6 | 16 | -------------------------------------------------------------------------------- /packages/neditor/workbench/browser/parts/toolbar/RectangleButton.vue: -------------------------------------------------------------------------------- 1 | 6 | 16 | -------------------------------------------------------------------------------- /packages/neditor/workbench/browser/parts/toolbar/RedoButton.vue: -------------------------------------------------------------------------------- 1 | 25 | 34 | 40 | -------------------------------------------------------------------------------- /packages/neditor/workbench/browser/parts/toolbar/SelectionButton.vue: -------------------------------------------------------------------------------- 1 | 9 | 19 | -------------------------------------------------------------------------------- /packages/neditor/workbench/browser/parts/toolbar/TextButton.vue: -------------------------------------------------------------------------------- 1 | 10 | 20 | -------------------------------------------------------------------------------- /packages/neditor/workbench/browser/parts/toolbar/UndoButton.vue: -------------------------------------------------------------------------------- 1 | 18 | 27 | 33 | -------------------------------------------------------------------------------- /packages/neditor/workbench/browser/web.api.ts: -------------------------------------------------------------------------------- 1 | import { IProductConfiguration } from "@neditor/core/base/common/product"; 2 | 3 | export interface IWorkbench { 4 | 5 | /** 6 | * Triggers shutdown of the workbench programmatically. After this method is 7 | * called, the workbench is not usable anymore and the page needs to reload 8 | * or closed. 9 | * 10 | * This will also remove any `beforeUnload` handlers that would bring up a 11 | * confirmation dialog. 12 | * 13 | * The returned promise should be awaited on to ensure any data to persist 14 | * has been persisted. 15 | */ 16 | shutdown: () => Promise; 17 | } 18 | 19 | export interface IWorkbenchConstructionOptions { 20 | readonly productConfiguration?: Partial; 21 | } 22 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/action/action.contributions.ts: -------------------------------------------------------------------------------- 1 | import { KeyCode } from '@neditor/core/base/common/keyCodes'; 2 | import { KeybindingsRegistry, KeybindingWeight } from "../../../platform/keybinding/common/keybindingsRegistry"; 3 | import { 4 | CancelToolAction, 5 | IToolService, 6 | ToolContextKeys, 7 | ToolInvocationPhase 8 | } from "../../../platform/tool/common/tool"; 9 | 10 | KeybindingsRegistry.registerCommandAndKeybindingRule({ 11 | id: CancelToolAction, 12 | primary: KeyCode.Escape, 13 | when: ToolContextKeys.currentToolState.notEqualsTo(ToolInvocationPhase.noop), 14 | weight: KeybindingWeight.EditorCore, 15 | handler(accessor, args) { 16 | const toolService = accessor.get(IToolService) 17 | toolService.switchDefault() 18 | } 19 | }) 20 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/brush/utils.ts: -------------------------------------------------------------------------------- 1 | // Turn the points returned from perfect-freehand into SVG path data. 2 | 3 | export function getSvgPathFromStroke(stroke: number[][]): string { 4 | if (!stroke.length) return "" 5 | 6 | const d = stroke.reduce( 7 | (acc, [x0, y0], i, arr) => { 8 | const [x1, y1] = arr[(i + 1) % arr.length] 9 | acc.push(Math.round(x0), Math.round(y0), Math.round((x0 + x1) / 2), Math.round((y0 + y1) / 2)) 10 | return acc 11 | }, 12 | ["M", ...stroke[0], "Q"] 13 | ) 14 | 15 | d.push("Z") 16 | return d.join(" ") 17 | } 18 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/common.ts: -------------------------------------------------------------------------------- 1 | export interface IPhysicalCursorPosition { 2 | blockStart: number; 3 | inlineStart: number; 4 | inlineSize: number; 5 | } 6 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/core/get-fragment.ts: -------------------------------------------------------------------------------- 1 | import { Editor, Node } from '../interfaces' 2 | import { WithEditorFirstArg } from '../utils' 3 | 4 | export const getFragment: WithEditorFirstArg = editor => { 5 | const { selection } = editor 6 | 7 | if (selection) { 8 | return Node.fragment(editor, selection) 9 | } 10 | return [] 11 | } 12 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/core/index.ts: -------------------------------------------------------------------------------- 1 | export * from './apply' 2 | export * from './get-dirty-paths' 3 | export * from './get-fragment' 4 | export * from './normalize-node' 5 | export * from './should-normalize' 6 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/core/should-normalize.ts: -------------------------------------------------------------------------------- 1 | import { WithEditorFirstArg } from '../utils/types' 2 | import { Editor } from '../interfaces/editor' 3 | 4 | export const shouldNormalize: WithEditorFirstArg = ( 5 | editor, 6 | { iteration, initialDirtyPathsLength } 7 | ) => { 8 | const maxIterations = initialDirtyPathsLength * 42 // HACK: better way? 9 | 10 | if (iteration > maxIterations) { 11 | throw new Error( 12 | `Could not completely normalize the editor after ${maxIterations} iterations! This is usually due to incorrect normalization logic that leaves a node in an invalid state.` 13 | ) 14 | } 15 | 16 | return true 17 | } 18 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/above.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorInterface } from '../interfaces/editor' 2 | import { Text } from '../interfaces/text' 3 | import { Range } from '../interfaces/range' 4 | import { Path } from '../interfaces/path' 5 | 6 | export const above: EditorInterface['above'] = (editor, options = {}) => { 7 | const { 8 | voids = false, 9 | mode = 'lowest', 10 | at = editor.selection, 11 | match, 12 | } = options 13 | 14 | if (!at) { 15 | return 16 | } 17 | 18 | const path = Editor.path(editor, at) 19 | const reverse = mode === 'lowest' 20 | 21 | for (const [n, p] of Editor.levels(editor, { 22 | at: path, 23 | voids, 24 | match, 25 | reverse, 26 | })) { 27 | if (Text.isText(n)) continue 28 | if (Range.isRange(at)) { 29 | if ( 30 | Path.isAncestor(p, at.anchor.path) && 31 | Path.isAncestor(p, at.focus.path) 32 | ) { 33 | return [n, p] 34 | } 35 | } else { 36 | if (!Path.equals(path, p)) { 37 | return [n, p] 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/after.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorInterface } from '../interfaces/editor' 2 | 3 | export const after: EditorInterface['after'] = (editor, at, options = {}) => { 4 | const anchor = Editor.point(editor, at, { edge: 'end' }) 5 | const focus = Editor.end(editor, []) 6 | const range = { anchor, focus } 7 | const { distance = 1 } = options 8 | let d = 0 9 | let target 10 | 11 | for (const p of Editor.positions(editor, { 12 | ...options, 13 | at: range, 14 | })) { 15 | if (d > distance) { 16 | break 17 | } 18 | 19 | if (d !== 0) { 20 | target = p 21 | } 22 | 23 | d++ 24 | } 25 | 26 | return target 27 | } 28 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/before.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorInterface } from '../interfaces/editor' 2 | 3 | export const before: EditorInterface['before'] = (editor, at, options = {}) => { 4 | const anchor = Editor.start(editor, []) 5 | const focus = Editor.point(editor, at, { edge: 'start' }) 6 | const range = { anchor, focus } 7 | const { distance = 1 } = options 8 | let d = 0 9 | let target 10 | 11 | for (const p of Editor.positions(editor, { 12 | ...options, 13 | at: range, 14 | reverse: true, 15 | })) { 16 | if (d > distance) { 17 | break 18 | } 19 | 20 | if (d !== 0) { 21 | target = p 22 | } 23 | 24 | d++ 25 | } 26 | 27 | return target 28 | } 29 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/delete-backward.ts: -------------------------------------------------------------------------------- 1 | import { Editor } from '../interfaces/editor' 2 | import { Transforms } from '../interfaces/transforms' 3 | import { Range } from '../interfaces/range' 4 | import { WithEditorFirstArg } from '../utils/types' 5 | 6 | export const deleteBackward: WithEditorFirstArg = ( 7 | editor, 8 | unit 9 | ) => { 10 | const { selection } = editor 11 | 12 | if (selection && Range.isCollapsed(selection)) { 13 | Transforms.delete(editor, { unit, reverse: true }) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/delete-forward.ts: -------------------------------------------------------------------------------- 1 | import { Editor } from '../interfaces/editor' 2 | import { Transforms } from '../interfaces/transforms' 3 | import { Range } from '../interfaces/range' 4 | import { WithEditorFirstArg } from '../utils/types' 5 | 6 | export const deleteForward: WithEditorFirstArg = ( 7 | editor, 8 | unit 9 | ) => { 10 | const { selection } = editor 11 | 12 | if (selection && Range.isCollapsed(selection)) { 13 | Transforms.delete(editor, { unit }) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/delete-fragment.ts: -------------------------------------------------------------------------------- 1 | import { Range } from '../interfaces/range' 2 | import { Transforms } from '../interfaces/transforms' 3 | import { EditorInterface } from '../interfaces/editor' 4 | 5 | export const deleteFragment: EditorInterface['deleteFragment'] = ( 6 | editor, 7 | { direction = 'forward' } = {} 8 | ) => { 9 | const { selection } = editor 10 | 11 | if (selection && Range.isExpanded(selection)) { 12 | Transforms.delete(editor, { reverse: direction === 'backward' }) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/edges.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorInterface } from '../interfaces/editor' 2 | 3 | export const edges: EditorInterface['edges'] = (editor, at) => { 4 | return [Editor.start(editor, at), Editor.end(editor, at)] 5 | } 6 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/element-read-only.ts: -------------------------------------------------------------------------------- 1 | import { Element } from '../interfaces/element' 2 | import { Editor, EditorInterface } from '../interfaces/editor' 3 | 4 | export const elementReadOnly: EditorInterface['elementReadOnly'] = ( 5 | editor, 6 | options = {} 7 | ) => { 8 | return Editor.above(editor, { 9 | ...options, 10 | match: n => Element.isElement(n) && Editor.isElementReadOnly(editor, n), 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/end.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorInterface } from '../interfaces/editor' 2 | 3 | export const end: EditorInterface['end'] = (editor, at) => { 4 | return Editor.point(editor, at, { edge: 'end' }) 5 | } 6 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/first.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorInterface } from '../interfaces/editor' 2 | 3 | export const first: EditorInterface['first'] = (editor, at) => { 4 | const path = Editor.path(editor, at, { edge: 'start' }) 5 | return Editor.node(editor, path) 6 | } 7 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/fragment.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorInterface } from '../interfaces/editor' 2 | import { Node } from '../interfaces/node' 3 | 4 | export const fragment: EditorInterface['fragment'] = (editor, at) => { 5 | const range = Editor.range(editor, at) 6 | return Node.fragment(editor, range) 7 | } 8 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/get-void.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorInterface } from '../interfaces/editor' 2 | import { Element } from '../interfaces/element' 3 | 4 | export const getVoid: EditorInterface['void'] = (editor, options = {}) => { 5 | return Editor.above(editor, { 6 | ...options, 7 | match: n => Element.isElement(n) && Editor.isVoid(editor, n), 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/has-blocks.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorInterface } from '../interfaces/editor' 2 | import { Element } from '../interfaces/element' 3 | 4 | export const hasBlocks: EditorInterface['hasBlocks'] = (editor, element) => { 5 | return element.children.some( 6 | n => Element.isElement(n) && Editor.isBlock(editor, n) 7 | ) 8 | } 9 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/has-inlines.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorInterface } from '../interfaces/editor' 2 | import { Text } from '../interfaces/text' 3 | 4 | export const hasInlines: EditorInterface['hasInlines'] = (editor, element) => { 5 | return element.children.some( 6 | n => Text.isText(n) || Editor.isInline(editor, n) 7 | ) 8 | } 9 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/has-path.ts: -------------------------------------------------------------------------------- 1 | import { EditorInterface } from '../interfaces/editor' 2 | import { Node } from '../interfaces/node' 3 | 4 | export const hasPath: EditorInterface['hasPath'] = (editor, path) => { 5 | return Node.has(editor, path) 6 | } 7 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/has-texts.ts: -------------------------------------------------------------------------------- 1 | import { EditorInterface } from '../interfaces/editor' 2 | import { Text } from '../interfaces/text' 3 | 4 | export const hasTexts: EditorInterface['hasTexts'] = (editor, element) => { 5 | return element.children.every(n => Text.isText(n)) 6 | } 7 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/insert-break.ts: -------------------------------------------------------------------------------- 1 | import { Transforms } from '../interfaces/transforms' 2 | import { EditorInterface } from '../interfaces/editor' 3 | 4 | export const insertBreak: EditorInterface['insertBreak'] = editor => { 5 | Transforms.splitNodes(editor, { always: true }) 6 | } 7 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/insert-node.ts: -------------------------------------------------------------------------------- 1 | import { Transforms } from '../interfaces/transforms' 2 | import { EditorInterface } from '../interfaces/editor' 3 | 4 | export const insertNode: EditorInterface['insertNode'] = ( 5 | editor, 6 | node, 7 | options 8 | ) => { 9 | Transforms.insertNodes(editor, node, options) 10 | } 11 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/insert-soft-break.ts: -------------------------------------------------------------------------------- 1 | import { Transforms } from '../interfaces/transforms' 2 | import { EditorInterface } from '../interfaces/editor' 3 | 4 | export const insertSoftBreak: EditorInterface['insertSoftBreak'] = editor => { 5 | Transforms.splitNodes(editor, { always: true }) 6 | } 7 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/insert-text.ts: -------------------------------------------------------------------------------- 1 | import { Transforms } from '../interfaces/transforms'; 2 | import { EditorInterface } from '../interfaces/editor'; 3 | 4 | export const insertText: EditorInterface['insertText'] = ( 5 | editor, 6 | text, 7 | options = {} 8 | ) => { 9 | const { selection, marks } = editor; 10 | 11 | if (selection) { 12 | if (marks) { 13 | const node = { content: text, type: 'text' as const, ...marks }; 14 | Transforms.insertNodes(editor, node, { 15 | at: options.at, 16 | voids: options.voids, 17 | }); 18 | } else { 19 | Transforms.insertText(editor, text, options); 20 | } 21 | 22 | editor.marks = null; 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/is-block.ts: -------------------------------------------------------------------------------- 1 | import { EditorInterface } from '../interfaces/editor' 2 | 3 | export const isBlock: EditorInterface['isBlock'] = (editor, value) => { 4 | return !editor.isInline(value) 5 | } 6 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/is-edge.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorInterface } from '../interfaces/editor' 2 | 3 | export const isEdge: EditorInterface['isEdge'] = (editor, point, at) => { 4 | return Editor.isStart(editor, point, at) || Editor.isEnd(editor, point, at) 5 | } 6 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/is-editor.ts: -------------------------------------------------------------------------------- 1 | import { isFunction } from '../../../../../../base/common/type'; 2 | import { Editor, EditorInterface } from '../interfaces/editor'; 3 | 4 | export const isEditor: EditorInterface['isEditor'] = ( 5 | value: any 6 | ): value is Editor => { 7 | const func = Reflect.get(value, 'isEditor'); 8 | return func && isFunction(func) && func.call(value); 9 | }; 10 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/is-empty.ts: -------------------------------------------------------------------------------- 1 | import { EditorInterface } from '../interfaces/editor'; 2 | import { Text } from '../interfaces/text'; 3 | 4 | export const isEmpty: EditorInterface['isEmpty'] = (editor, element) => { 5 | const { children } = element; 6 | const [first] = children; 7 | return ( 8 | children.length === 0 || 9 | (children.length === 1 && 10 | Text.isText(first) && 11 | first.content === '' && 12 | !editor.isVoid(element)) 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/is-end.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorInterface } from '../interfaces/editor' 2 | import { Point } from '../interfaces/point' 3 | 4 | export const isEnd: EditorInterface['isEnd'] = (editor, point, at) => { 5 | const end = Editor.end(editor, at) 6 | return Point.equals(point, end) 7 | } 8 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/is-normalizing.ts: -------------------------------------------------------------------------------- 1 | import { EditorInterface } from '../interfaces/editor' 2 | import { NORMALIZING } from '../utils/weak-maps' 3 | 4 | export const isNormalizing: EditorInterface['isNormalizing'] = editor => { 5 | const isNormalizing = NORMALIZING.get(editor) 6 | return isNormalizing === undefined ? true : isNormalizing 7 | } 8 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/is-start.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorInterface } from '../interfaces/editor' 2 | import { Point } from '../interfaces/point' 3 | 4 | export const isStart: EditorInterface['isStart'] = (editor, point, at) => { 5 | // PERF: If the offset isn't `0` we know it's not the start. 6 | if (point.offset !== 0) { 7 | return false 8 | } 9 | 10 | const start = Editor.start(editor, at) 11 | return Point.equals(point, start) 12 | } 13 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/last.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorInterface } from '../interfaces/editor' 2 | 3 | export const last: EditorInterface['last'] = (editor, at) => { 4 | const path = Editor.path(editor, at, { edge: 'end' }) 5 | return Editor.node(editor, path) 6 | } 7 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/leaf.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorInterface } from '../interfaces/editor' 2 | import { Node } from '../interfaces/node' 3 | 4 | export const leaf: EditorInterface['leaf'] = (editor, at, options = {}) => { 5 | const path = Editor.path(editor, at, options) 6 | const node = Node.leaf(editor, path) 7 | return [node, path] 8 | } 9 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/levels.ts: -------------------------------------------------------------------------------- 1 | import { Node, NodeEntry } from '../interfaces/node' 2 | import { Editor, EditorLevelsOptions } from '../interfaces/editor' 3 | import { Element } from '../interfaces/element' 4 | 5 | export function* levels( 6 | editor: Editor, 7 | options: EditorLevelsOptions = {} 8 | ): Generator, void, undefined> { 9 | const { at = editor.selection, reverse = false, voids = false } = options 10 | let { match } = options 11 | 12 | if (match == null) { 13 | match = () => true 14 | } 15 | 16 | if (!at) { 17 | return 18 | } 19 | 20 | const levels: NodeEntry[] = [] 21 | const path = Editor.path(editor, at) 22 | 23 | for (const [n, p] of Node.levels(editor, path)) { 24 | if (!match(n, p)) { 25 | continue 26 | } 27 | 28 | levels.push([n, p]) 29 | 30 | if (!voids && Element.isElement(n) && Editor.isVoid(editor, n)) { 31 | break 32 | } 33 | } 34 | 35 | if (reverse) { 36 | levels.reverse() 37 | } 38 | 39 | yield* levels 40 | } 41 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/next.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorInterface } from '../interfaces/editor' 2 | import { Span } from '../interfaces/location' 3 | import { Path } from '../interfaces/path' 4 | 5 | export const next: EditorInterface['next'] = (editor, options = {}) => { 6 | const { mode = 'lowest', voids = false } = options 7 | let { match, at = editor.selection } = options 8 | 9 | if (!at) { 10 | return 11 | } 12 | 13 | const pointAfterLocation = Editor.after(editor, at, { voids }) 14 | 15 | if (!pointAfterLocation) return 16 | 17 | const [, to] = Editor.last(editor, []) 18 | 19 | const span: Span = [pointAfterLocation.path, to] 20 | 21 | if (Path.isPath(at) && at.length === 0) { 22 | throw new Error(`Cannot get the next node from the root node!`) 23 | } 24 | 25 | if (match == null) { 26 | if (Path.isPath(at)) { 27 | const [parent] = Editor.parent(editor, at) 28 | match = n => parent.children.includes(n) 29 | } else { 30 | match = () => true 31 | } 32 | } 33 | 34 | const [next] = Editor.nodes(editor, { at: span, match, mode, voids }) 35 | return next 36 | } 37 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/node.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorInterface } from '../interfaces/editor' 2 | import { Node } from '../interfaces/node' 3 | 4 | export const node: EditorInterface['node'] = (editor, at, options = {}) => { 5 | const path = Editor.path(editor, at, options) 6 | const node = Node.get(editor, path) 7 | return [node, path] 8 | } 9 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/parent.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorInterface } from '../interfaces/editor' 2 | import { Path } from '../interfaces/path' 3 | import { Ancestor, NodeEntry } from '../interfaces/node' 4 | 5 | export const parent: EditorInterface['parent'] = (editor, at, options = {}) => { 6 | const path = Editor.path(editor, at, options) 7 | const parentPath = Path.parent(path) 8 | const entry = Editor.node(editor, parentPath) 9 | return entry as NodeEntry 10 | } 11 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/path-ref.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorInterface } from '../interfaces/editor' 2 | import { PathRef } from '../interfaces/path-ref' 3 | 4 | export const pathRef: EditorInterface['pathRef'] = ( 5 | editor, 6 | path, 7 | options = {} 8 | ) => { 9 | const { affinity = 'forward' } = options 10 | const ref: PathRef = { 11 | current: path, 12 | affinity, 13 | unref() { 14 | const { current } = ref 15 | const pathRefs = Editor.pathRefs(editor) 16 | pathRefs.delete(ref) 17 | ref.current = null 18 | return current 19 | }, 20 | } 21 | 22 | const refs = Editor.pathRefs(editor) 23 | refs.add(ref) 24 | return ref 25 | } 26 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/path-refs.ts: -------------------------------------------------------------------------------- 1 | import { EditorInterface } from '../interfaces/editor' 2 | import { PATH_REFS } from '../utils/weak-maps' 3 | 4 | export const pathRefs: EditorInterface['pathRefs'] = editor => { 5 | let refs = PATH_REFS.get(editor) 6 | 7 | if (!refs) { 8 | refs = new Set() 9 | PATH_REFS.set(editor, refs) 10 | } 11 | 12 | return refs 13 | } 14 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/path.ts: -------------------------------------------------------------------------------- 1 | import { EditorInterface, Node, Path, Point, Range } from '../interfaces' 2 | 3 | export const path: EditorInterface['path'] = (editor, at, options = {}) => { 4 | const { depth, edge } = options 5 | 6 | if (Path.isPath(at)) { 7 | if (edge === 'start') { 8 | const [, firstPath] = Node.first(editor, at) 9 | at = firstPath 10 | } else if (edge === 'end') { 11 | const [, lastPath] = Node.last(editor, at) 12 | at = lastPath 13 | } 14 | } 15 | 16 | if (Range.isRange(at)) { 17 | if (edge === 'start') { 18 | at = Range.start(at) 19 | } else if (edge === 'end') { 20 | at = Range.end(at) 21 | } else { 22 | at = Path.common(at.anchor.path, at.focus.path) 23 | } 24 | } 25 | 26 | if (Point.isPoint(at)) { 27 | at = at.path 28 | } 29 | 30 | if (depth != null) { 31 | at = at.slice(0, depth) 32 | } 33 | 34 | return at 35 | } 36 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/point-ref.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorInterface } from '../interfaces/editor' 2 | import { PointRef } from '../interfaces/point-ref' 3 | 4 | export const pointRef: EditorInterface['pointRef'] = ( 5 | editor, 6 | point, 7 | options = {} 8 | ) => { 9 | const { affinity = 'forward' } = options 10 | const ref: PointRef = { 11 | current: point, 12 | affinity, 13 | unref() { 14 | const { current } = ref 15 | const pointRefs = Editor.pointRefs(editor) 16 | pointRefs.delete(ref) 17 | ref.current = null 18 | return current 19 | }, 20 | } 21 | 22 | const refs = Editor.pointRefs(editor) 23 | refs.add(ref) 24 | return ref 25 | } 26 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/point-refs.ts: -------------------------------------------------------------------------------- 1 | import { EditorInterface } from '../interfaces/editor' 2 | import { POINT_REFS } from '../utils/weak-maps' 3 | 4 | export const pointRefs: EditorInterface['pointRefs'] = editor => { 5 | let refs = POINT_REFS.get(editor) 6 | 7 | if (!refs) { 8 | refs = new Set() 9 | POINT_REFS.set(editor, refs) 10 | } 11 | 12 | return refs 13 | } 14 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/point.ts: -------------------------------------------------------------------------------- 1 | import { EditorInterface } from '../interfaces/editor'; 2 | import { Path } from '../interfaces/path'; 3 | import { Node } from '../interfaces/node'; 4 | import { Text } from '../interfaces/text'; 5 | import { Range } from '../interfaces/range'; 6 | 7 | export const point: EditorInterface['point'] = (editor, at, options = {}) => { 8 | const { edge = 'start' } = options; 9 | 10 | if (Path.isPath(at)) { 11 | let path; 12 | 13 | if (edge === 'end') { 14 | const [, lastPath] = Node.last(editor, at); 15 | path = lastPath; 16 | } else { 17 | const [, firstPath] = Node.first(editor, at); 18 | path = firstPath; 19 | } 20 | 21 | const node = Node.get(editor, path); 22 | 23 | if (!Text.isText(node)) { 24 | throw new Error( 25 | `Cannot get the ${edge} point in the node at path [${at}] because it has no ${edge} text node.` 26 | ); 27 | } 28 | 29 | return { path, offset: edge === 'end' ? node.content.length : 0 }; 30 | } 31 | 32 | if (Range.isRange(at)) { 33 | const [start, end] = Range.edges(at); 34 | return edge === 'start' ? start : end; 35 | } 36 | 37 | return at; 38 | }; 39 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/previous.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorInterface } from '../interfaces/editor' 2 | import { Span } from '../interfaces/location' 3 | import { Path } from '../interfaces/path' 4 | 5 | export const previous: EditorInterface['previous'] = (editor, options = {}) => { 6 | const { mode = 'lowest', voids = false } = options 7 | let { match, at = editor.selection } = options 8 | 9 | if (!at) { 10 | return 11 | } 12 | 13 | const pointBeforeLocation = Editor.before(editor, at, { voids }) 14 | 15 | if (!pointBeforeLocation) { 16 | return 17 | } 18 | 19 | const [, to] = Editor.first(editor, []) 20 | 21 | // The search location is from the start of the document to the path of 22 | // the point before the location passed in 23 | const span: Span = [pointBeforeLocation.path, to] 24 | 25 | if (Path.isPath(at) && at.length === 0) { 26 | throw new Error(`Cannot get the previous node from the root node!`) 27 | } 28 | 29 | if (match == null) { 30 | if (Path.isPath(at)) { 31 | const [parent] = Editor.parent(editor, at) 32 | match = n => parent.children.includes(n) 33 | } else { 34 | match = () => true 35 | } 36 | } 37 | 38 | const [previous] = Editor.nodes(editor, { 39 | reverse: true, 40 | at: span, 41 | match, 42 | mode, 43 | voids, 44 | }) 45 | 46 | return previous 47 | } 48 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/range-ref.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorInterface } from '../interfaces/editor' 2 | import { RangeRef } from '../interfaces/range-ref' 3 | 4 | export const rangeRef: EditorInterface['rangeRef'] = ( 5 | editor, 6 | range, 7 | options = {} 8 | ) => { 9 | const { affinity = 'forward' } = options 10 | const ref: RangeRef = { 11 | current: range, 12 | affinity, 13 | unref() { 14 | const { current } = ref 15 | const rangeRefs = Editor.rangeRefs(editor) 16 | rangeRefs.delete(ref) 17 | ref.current = null 18 | return current 19 | }, 20 | } 21 | 22 | const refs = Editor.rangeRefs(editor) 23 | refs.add(ref) 24 | return ref 25 | } 26 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/range-refs.ts: -------------------------------------------------------------------------------- 1 | import { EditorInterface } from '../interfaces/editor' 2 | import { RANGE_REFS } from '../utils/weak-maps' 3 | 4 | export const rangeRefs: EditorInterface['rangeRefs'] = editor => { 5 | let refs = RANGE_REFS.get(editor) 6 | 7 | if (!refs) { 8 | refs = new Set() 9 | RANGE_REFS.set(editor, refs) 10 | } 11 | 12 | return refs 13 | } 14 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/range.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorInterface } from '../interfaces/editor' 2 | import { Range } from '../interfaces/range' 3 | 4 | export const range: EditorInterface['range'] = (editor, at, to) => { 5 | if (Range.isRange(at) && !to) { 6 | return at 7 | } 8 | 9 | const start = Editor.start(editor, at) 10 | const end = Editor.end(editor, to || at) 11 | return { anchor: start, focus: end } 12 | } 13 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/set-normalizing.ts: -------------------------------------------------------------------------------- 1 | import { EditorInterface } from '../interfaces/editor' 2 | import { NORMALIZING } from '../utils/weak-maps' 3 | 4 | export const setNormalizing: EditorInterface['setNormalizing'] = ( 5 | editor, 6 | isNormalizing 7 | ) => { 8 | NORMALIZING.set(editor, isNormalizing) 9 | } 10 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/start.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorInterface } from '../interfaces/editor' 2 | 3 | export const start: EditorInterface['start'] = (editor, at) => { 4 | return Editor.point(editor, at, { edge: 'start' }) 5 | } 6 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/string.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorInterface } from '../interfaces/editor' 2 | import { Range } from '../interfaces/range' 3 | import { Text } from '../interfaces/text' 4 | import { Path } from '../interfaces/path' 5 | 6 | export const string: EditorInterface['string'] = (editor, at, options = {}) => { 7 | const { voids = false } = options 8 | const range = Editor.range(editor, at) 9 | const [start, end] = Range.edges(range) 10 | let text = '' 11 | 12 | for (const [node, path] of Editor.nodes(editor, { 13 | at: range, 14 | match: Text.isText, 15 | voids, 16 | })) { 17 | let t = node.content 18 | 19 | if (Path.equals(path, end.path)) { 20 | t = t.slice(0, end.offset) 21 | } 22 | 23 | if (Path.equals(path, start.path)) { 24 | t = t.slice(start.offset) 25 | } 26 | 27 | text += t 28 | } 29 | 30 | return text 31 | } 32 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/editor/without-normalizing.ts: -------------------------------------------------------------------------------- 1 | import { Editor, EditorInterface } from '../interfaces/editor' 2 | 3 | export const withoutNormalizing: EditorInterface['withoutNormalizing'] = ( 4 | editor, 5 | fn 6 | ) => { 7 | const value = Editor.isNormalizing(editor) 8 | Editor.setNormalizing(editor, false) 9 | try { 10 | fn() 11 | } finally { 12 | Editor.setNormalizing(editor, value) 13 | } 14 | Editor.normalize(editor) 15 | } 16 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/index.ts: -------------------------------------------------------------------------------- 1 | export * from './core' 2 | export * from './create-editor' 3 | export * from './editor' 4 | export * from './interfaces' 5 | export * from './transforms-node' 6 | export * from './transforms-selection' 7 | export * from './transforms-text' 8 | export * from './types' 9 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './editor'; 2 | export * from './element'; 3 | export * from './location'; 4 | export * from './node'; 5 | export * from './operation'; 6 | export * from './path-ref'; 7 | export * from './path'; 8 | export * from './point-ref'; 9 | export * from './point'; 10 | export * from './range-ref'; 11 | export * from './range'; 12 | export * from './scrubber'; 13 | export * from './text'; 14 | export * from './transforms/index'; 15 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/interfaces/path-ref.ts: -------------------------------------------------------------------------------- 1 | import { Operation, Path } from '..' 2 | 3 | /** 4 | * `PathRef` objects keep a specific path in a document synced over time as new 5 | * operations are applied to the editor. You can access their `current` property 6 | * at any time for the up-to-date path value. 7 | */ 8 | 9 | export interface PathRef { 10 | current: Path | null 11 | affinity: 'forward' | 'backward' | null 12 | unref(): Path | null 13 | } 14 | 15 | export interface PathRefInterface { 16 | /** 17 | * Transform the path ref's current value by an operation. 18 | */ 19 | transform: (ref: PathRef, op: Operation) => void 20 | } 21 | 22 | // eslint-disable-next-line no-redeclare 23 | export const PathRef: PathRefInterface = { 24 | transform(ref: PathRef, op: Operation): void { 25 | const { current, affinity } = ref 26 | 27 | if (current == null) { 28 | return 29 | } 30 | 31 | const path = Path.transform(current, op, { affinity }) 32 | ref.current = path 33 | 34 | if (path == null) { 35 | ref.unref() 36 | } 37 | }, 38 | } 39 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/interfaces/point-ref.ts: -------------------------------------------------------------------------------- 1 | import { Operation, Point } from '..' 2 | import { TextDirection } from '../types/types' 3 | 4 | /** 5 | * `PointRef` objects keep a specific point in a document synced over time as new 6 | * operations are applied to the editor. You can access their `current` property 7 | * at any time for the up-to-date point value. 8 | */ 9 | 10 | export interface PointRef { 11 | current: Point | null 12 | affinity: TextDirection | null 13 | unref(): Point | null 14 | } 15 | 16 | export interface PointRefInterface { 17 | /** 18 | * Transform the point ref's current value by an operation. 19 | */ 20 | transform: (ref: PointRef, op: Operation) => void 21 | } 22 | 23 | // eslint-disable-next-line no-redeclare 24 | export const PointRef: PointRefInterface = { 25 | transform(ref: PointRef, op: Operation): void { 26 | const { current, affinity } = ref 27 | 28 | if (current == null) { 29 | return 30 | } 31 | 32 | const point = Point.transform(current, op, { affinity }) 33 | ref.current = point 34 | 35 | if (point == null) { 36 | ref.unref() 37 | } 38 | }, 39 | } 40 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/interfaces/range-ref.ts: -------------------------------------------------------------------------------- 1 | import { Operation, Range } from '..' 2 | 3 | /** 4 | * `RangeRef` objects keep a specific range in a document synced over time as new 5 | * operations are applied to the editor. You can access their `current` property 6 | * at any time for the up-to-date range value. 7 | */ 8 | 9 | export interface RangeRef { 10 | current: Range | null 11 | affinity: 'forward' | 'backward' | 'outward' | 'inward' | null 12 | unref(): Range | null 13 | } 14 | 15 | export interface RangeRefInterface { 16 | /** 17 | * Transform the range ref's current value by an operation. 18 | */ 19 | transform: (ref: RangeRef, op: Operation) => void 20 | } 21 | 22 | // eslint-disable-next-line no-redeclare 23 | export const RangeRef: RangeRefInterface = { 24 | transform(ref: RangeRef, op: Operation): void { 25 | const { current, affinity } = ref 26 | 27 | if (current == null) { 28 | return 29 | } 30 | 31 | const path = Range.transform(current, op, { affinity }) 32 | ref.current = path 33 | 34 | if (path == null) { 35 | ref.unref() 36 | } 37 | }, 38 | } 39 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/interfaces/scrubber.ts: -------------------------------------------------------------------------------- 1 | export type Scrubber = (key: string, value: unknown) => unknown 2 | 3 | export interface ScrubberInterface { 4 | setScrubber(scrubber: Scrubber | undefined): void 5 | stringify(value: any): string 6 | } 7 | 8 | let _scrubber: Scrubber | undefined = undefined 9 | 10 | /** 11 | * This interface implements a stringify() function, which is used by Slate 12 | * internally when generating exceptions containing end user data. Developers 13 | * using Slate may call Scrubber.setScrubber() to alter the behavior of this 14 | * stringify() function. 15 | * 16 | * For example, to prevent the cleartext logging of 'text' fields within Nodes: 17 | * 18 | * import { Scrubber } from 'slate'; 19 | * Scrubber.setScrubber((key, val) => { 20 | * if (key === 'text') return '...scrubbed...' 21 | * return val 22 | * }); 23 | * 24 | */ 25 | // eslint-disable-next-line no-redeclare 26 | export const Scrubber: ScrubberInterface = { 27 | setScrubber(scrubber: Scrubber | undefined): void { 28 | _scrubber = scrubber 29 | }, 30 | 31 | stringify(value: any): string { 32 | return JSON.stringify(value, _scrubber) 33 | }, 34 | } 35 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/interfaces/transforms/index.ts: -------------------------------------------------------------------------------- 1 | import { GeneralTransforms } from './general' 2 | import { NodeTransforms } from './node' 3 | import { SelectionTransforms } from './selection' 4 | import { TextTransforms } from './text' 5 | 6 | export const Transforms: GeneralTransforms & 7 | NodeTransforms & 8 | SelectionTransforms & 9 | TextTransforms = { 10 | ...GeneralTransforms, 11 | ...NodeTransforms, 12 | ...SelectionTransforms, 13 | ...TextTransforms, 14 | } 15 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/transforms-node/index.ts: -------------------------------------------------------------------------------- 1 | export * from './insert-nodes' 2 | export * from './lift-nodes' 3 | export * from './merge-nodes' 4 | export * from './move-nodes' 5 | export * from './remove-nodes' 6 | export * from './set-nodes' 7 | export * from './split-nodes' 8 | export * from './unset-nodes' 9 | export * from './unwrap-nodes' 10 | export * from './wrap-nodes' 11 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/transforms-node/remove-nodes.ts: -------------------------------------------------------------------------------- 1 | import { NodeTransforms } from '../interfaces/transforms/node' 2 | import { Editor } from '../interfaces/editor' 3 | import { Path } from '../interfaces/path' 4 | import { matchPath } from '../utils/match-path' 5 | import { Element } from '../interfaces/element' 6 | import { Range } from '../interfaces/range' 7 | 8 | export const removeNodes: NodeTransforms['removeNodes'] = ( 9 | editor, 10 | options = {} 11 | ) => { 12 | Editor.withoutNormalizing(editor, () => { 13 | const { hanging = false, voids = false, mode = 'lowest' } = options 14 | let { at = editor.selection, match } = options 15 | 16 | if (!at) { 17 | return 18 | } 19 | 20 | if (match == null) { 21 | match = Path.isPath(at) 22 | ? matchPath(editor, at) 23 | : n => Element.isElement(n) && Editor.isBlock(editor, n) 24 | } 25 | 26 | if (!hanging && Range.isRange(at)) { 27 | at = Editor.unhangRange(editor, at, { voids }) 28 | } 29 | 30 | const depths = Editor.nodes(editor, { at, match, mode, voids }) 31 | const pathRefs = Array.from(depths, ([, p]) => Editor.pathRef(editor, p)) 32 | 33 | for (const pathRef of pathRefs) { 34 | const path = pathRef.unref()! 35 | 36 | if (path) { 37 | const [node] = Editor.node(editor, path) 38 | editor.apply({ type: 'remove_node', path, node }) 39 | } 40 | } 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/transforms-node/unset-nodes.ts: -------------------------------------------------------------------------------- 1 | import { NodeTransforms } from '../interfaces/transforms/node' 2 | import { Transforms } from '../interfaces/transforms' 3 | 4 | export const unsetNodes: NodeTransforms['unsetNodes'] = ( 5 | editor, 6 | props, 7 | options = {} 8 | ) => { 9 | if (!Array.isArray(props)) { 10 | props = [props] 11 | } 12 | 13 | const obj = {} 14 | 15 | for (const key of props) { 16 | // @ts-ignore 17 | obj[key] = null 18 | } 19 | 20 | Transforms.setNodes(editor, obj, options) 21 | } 22 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/transforms-selection/collapse.ts: -------------------------------------------------------------------------------- 1 | import { SelectionTransforms } from '../interfaces/transforms/selection' 2 | import { Transforms } from '../interfaces/transforms' 3 | import { Range } from '../interfaces/range' 4 | 5 | export const collapse: SelectionTransforms['collapse'] = ( 6 | editor, 7 | options = {} 8 | ) => { 9 | const { edge = 'anchor' } = options 10 | const { selection } = editor 11 | 12 | if (!selection) { 13 | return 14 | } else if (edge === 'anchor') { 15 | Transforms.select(editor, selection.anchor) 16 | } else if (edge === 'focus') { 17 | Transforms.select(editor, selection.focus) 18 | } else if (edge === 'start') { 19 | const [start] = Range.edges(selection) 20 | Transforms.select(editor, start) 21 | } else if (edge === 'end') { 22 | const [, end] = Range.edges(selection) 23 | Transforms.select(editor, end) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/transforms-selection/deselect.ts: -------------------------------------------------------------------------------- 1 | import { SelectionTransforms } from '../interfaces/transforms/selection' 2 | 3 | export const deselect: SelectionTransforms['deselect'] = editor => { 4 | const { selection } = editor 5 | 6 | if (selection) { 7 | editor.apply({ 8 | type: 'set_selection', 9 | properties: selection, 10 | newProperties: null, 11 | }) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/transforms-selection/index.ts: -------------------------------------------------------------------------------- 1 | export * from './collapse' 2 | export * from './deselect' 3 | export * from './move' 4 | export * from './select' 5 | export * from './set-point' 6 | export * from './set-selection' 7 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/transforms-selection/select.ts: -------------------------------------------------------------------------------- 1 | import { SelectionTransforms } from '../interfaces/transforms/selection' 2 | import { Editor } from '../interfaces/editor' 3 | import { Transforms } from '../interfaces/transforms' 4 | import { Range } from '../interfaces/range' 5 | import { Scrubber } from '../interfaces/scrubber' 6 | 7 | export const select: SelectionTransforms['select'] = (editor, target) => { 8 | const { selection } = editor 9 | target = Editor.range(editor, target) 10 | 11 | if (selection) { 12 | Transforms.setSelection(editor, target) 13 | return 14 | } 15 | 16 | if (!Range.isRange(target)) { 17 | throw new Error( 18 | `When setting the selection and the current selection is \`null\` you must provide at least an \`anchor\` and \`focus\`, but you passed: ${Scrubber.stringify( 19 | target 20 | )}` 21 | ) 22 | } 23 | 24 | editor.apply({ 25 | type: 'set_selection', 26 | properties: selection, 27 | newProperties: target, 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/transforms-selection/set-point.ts: -------------------------------------------------------------------------------- 1 | import { SelectionTransforms } from '../interfaces/transforms/selection' 2 | import { Range } from '../interfaces/range' 3 | import { Transforms } from '../interfaces/transforms' 4 | 5 | export const setPoint: SelectionTransforms['setPoint'] = ( 6 | editor, 7 | props, 8 | options = {} 9 | ) => { 10 | const { selection } = editor 11 | let { edge = 'both' } = options 12 | 13 | if (!selection) { 14 | return 15 | } 16 | 17 | if (edge === 'start') { 18 | edge = Range.isBackward(selection) ? 'focus' : 'anchor' 19 | } 20 | 21 | if (edge === 'end') { 22 | edge = Range.isBackward(selection) ? 'anchor' : 'focus' 23 | } 24 | 25 | const { anchor, focus } = selection 26 | const point = edge === 'anchor' ? anchor : focus 27 | 28 | Transforms.setSelection(editor, { 29 | [edge === 'anchor' ? 'anchor' : 'focus']: { ...point, ...props }, 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/transforms-selection/set-selection.ts: -------------------------------------------------------------------------------- 1 | import { SelectionTransforms } from '../interfaces/transforms/selection' 2 | import { Range } from '../interfaces/range' 3 | import { Point } from '../interfaces/point' 4 | 5 | export const setSelection: SelectionTransforms['setSelection'] = ( 6 | editor, 7 | props 8 | ) => { 9 | const { selection } = editor 10 | const oldProps: Partial | null = {} 11 | const newProps: Partial = {} 12 | 13 | if (!selection) { 14 | return 15 | } 16 | 17 | for (const key in props) { 18 | const k = key as keyof Range; 19 | if ( 20 | (k === 'anchor' && 21 | props.anchor != null && 22 | !Point.equals(props.anchor, selection.anchor)) || 23 | (k === 'focus' && 24 | props.focus != null && 25 | !Point.equals(props.focus, selection.focus)) || 26 | (k !== 'anchor' && k !== 'focus' && props[k] !== selection[k]) 27 | ) { 28 | oldProps[k] = selection[k] 29 | newProps[k] = props[k] 30 | } 31 | } 32 | 33 | if (Object.keys(oldProps).length > 0) { 34 | editor.apply({ 35 | type: 'set_selection', 36 | properties: oldProps, 37 | newProperties: newProps, 38 | }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/transforms-text/index.ts: -------------------------------------------------------------------------------- 1 | export * from './delete-text' 2 | export * from './insert-fragment' 3 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/types/custom-types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Extendable Custom Types Interface 3 | */ 4 | 5 | type ExtendableTypes = 6 | | 'Editor' 7 | | 'Element' 8 | | 'Text' 9 | | 'Selection' 10 | | 'Range' 11 | | 'Point' 12 | | 'Operation' 13 | | 'InsertNodeOperation' 14 | | 'InsertTextOperation' 15 | | 'MergeNodeOperation' 16 | | 'MoveNodeOperation' 17 | | 'RemoveNodeOperation' 18 | | 'RemoveTextOperation' 19 | | 'SetNodeOperation' 20 | | 'SetSelectionOperation' 21 | | 'SplitNodeOperation' 22 | 23 | export interface CustomTypes { 24 | [key: string]: unknown 25 | } 26 | 27 | export type ExtendedType< 28 | K extends ExtendableTypes, 29 | B 30 | > = unknown extends CustomTypes[K] ? B : CustomTypes[K] 31 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from './custom-types' 2 | export * from './types' 3 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/types/types.ts: -------------------------------------------------------------------------------- 1 | export type LeafEdge = 'start' | 'end' 2 | 3 | export type MaximizeMode = RangeMode | 'all' 4 | 5 | export type MoveUnit = 'offset' | 'character' | 'word' | 'line' 6 | 7 | export type RangeDirection = TextDirection | 'outward' | 'inward' 8 | 9 | export type RangeMode = 'highest' | 'lowest' 10 | 11 | export type SelectionEdge = 'anchor' | 'focus' | 'start' | 'end' 12 | 13 | export type SelectionMode = 'all' | 'highest' | 'lowest' 14 | 15 | export type TextDirection = 'forward' | 'backward' 16 | 17 | export type TextUnit = 'character' | 'word' | 'line' | 'block' 18 | 19 | export type TextUnitAdjustment = TextUnit | 'offset' 20 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/utils/get-default-insert-location.ts: -------------------------------------------------------------------------------- 1 | import { Editor, Location } from '../interfaces' 2 | 3 | /** 4 | * Get the default location to insert content into the editor. 5 | * By default, use the selection as the target location. But if there is 6 | * no selection, insert at the end of the document since that is such a 7 | * common use case when inserting from a non-selected state. 8 | */ 9 | export const getDefaultInsertLocation = (editor: Editor): Location => { 10 | if (editor.selection) { 11 | return editor.selection 12 | } else if (editor.children.length > 0) { 13 | return Editor.end(editor, []) 14 | } else { 15 | return [0] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './deep-equal' 2 | export * from './get-default-insert-location' 3 | export * from './match-path' 4 | export * from './string' 5 | export * from './types' 6 | export * from './weak-maps' 7 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/utils/match-path.ts: -------------------------------------------------------------------------------- 1 | import { Editor } from '../interfaces/editor' 2 | import { Path } from '../interfaces/path' 3 | import { Node } from '../interfaces/node' 4 | 5 | export const matchPath = ( 6 | editor: Editor, 7 | path: Path 8 | ): ((node: Node) => boolean) => { 9 | const [node] = Editor.node(editor, path) 10 | return n => n === node 11 | } 12 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/utils/types.ts: -------------------------------------------------------------------------------- 1 | import { Editor } from '../interfaces/editor' 2 | 3 | export type OmitFirstArg = F extends (x: any, ...args: infer P) => infer R 4 | ? (...args: P) => R 5 | : never 6 | 7 | export type OmitFirstArgWithSpecificGeneric = F extends ( 8 | x: any, 9 | ...args: infer P 10 | ) => infer R 11 | ? (...args: P) => R 12 | : never 13 | 14 | export type WithEditorFirstArg any> = ( 15 | editor: Editor, 16 | ...args: Parameters 17 | ) => ReturnType 18 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/editor/utils/weak-maps.ts: -------------------------------------------------------------------------------- 1 | import { Editor, Path, PathRef, PointRef, RangeRef } from '..' 2 | 3 | export const DIRTY_PATHS: WeakMap = new WeakMap() 4 | export const DIRTY_PATH_KEYS: WeakMap> = new WeakMap() 5 | export const FLUSHING: WeakMap = new WeakMap() 6 | export const NORMALIZING: WeakMap = new WeakMap() 7 | export const PATH_REFS: WeakMap> = new WeakMap() 8 | export const POINT_REFS: WeakMap> = new WeakMap() 9 | export const RANGE_REFS: WeakMap> = new WeakMap() 10 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/utils/key.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * An auto-incrementing identifier for keys. 3 | */ 4 | 5 | let n = 0 6 | 7 | /** 8 | * A class that keeps track of a key string. We use a full class here because we 9 | * want to be able to use them as keys in `WeakMap` objects. 10 | */ 11 | 12 | export class Key { 13 | id: string 14 | 15 | constructor() { 16 | this.id = `${n++}` 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/view/common.ts: -------------------------------------------------------------------------------- 1 | export const Common = { 2 | LineFeed: 10 as const, 3 | }; 4 | export const LineFeed = '\n'; 5 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/view/cursor/textCursor.css: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | .cursor { 6 | position: absolute; 7 | overflow: hidden; 8 | background: black; 9 | pointer-events: none; 10 | } 11 | 12 | @keyframes monaco-cursor-smooth { 13 | 0%, 14 | 20% { 15 | opacity: 1; 16 | } 17 | 60%, 18 | 100% { 19 | opacity: 0; 20 | } 21 | } 22 | 23 | @keyframes monaco-cursor-phase { 24 | 0%, 25 | 20% { 26 | opacity: 1; 27 | } 28 | 90%, 29 | 100% { 30 | opacity: 0; 31 | } 32 | } 33 | 34 | .cursor-smooth { 35 | animation: monaco-cursor-smooth 0.5s ease-in-out 0s 20 alternate; 36 | } 37 | 38 | .cursor-phase { 39 | animation: monaco-cursor-phase 0.5s ease-in-out 0s 20 alternate; 40 | } 41 | -------------------------------------------------------------------------------- /packages/neditor/workbench/contrib/tool/text/view/type/textAreaHandler.css: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | .inputarea { 7 | min-width: 0; 8 | min-height: 0; 9 | 10 | margin: 0; 11 | padding: 0; 12 | position: absolute; 13 | outline: none !important; 14 | resize: none; 15 | border: none; 16 | overflow: hidden; 17 | color: transparent; 18 | background-color: transparent; 19 | 20 | cursor: text; 21 | } 22 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WaiSiuKei/neditor/07fb259149d9d74e0b7a65c8ad60841d86e3b95e/screenshot.png -------------------------------------------------------------------------------- /third_party/css-parser/gen/inline.tokens: -------------------------------------------------------------------------------- 1 | T__0=1 2 | T__1=2 3 | T__2=3 4 | T__3=4 5 | T__4=5 6 | T__5=6 7 | T__6=7 8 | T__7=8 9 | T__8=9 10 | Comment=10 11 | Space=11 12 | Includes=12 13 | DashMatch=13 14 | Hash=14 15 | Important=15 16 | Percentage=16 17 | Uri=17 18 | UnicodeRange=18 19 | Dimension=19 20 | UnknownDimension=20 21 | Plus=21 22 | Minus=22 23 | Greater=23 24 | Comma=24 25 | Tilde=25 26 | PseudoNot=26 27 | Number=27 28 | String_=28 29 | PrefixMatch=29 30 | SuffixMatch=30 31 | SubstringMatch=31 32 | Or=32 33 | Calc=33 34 | Variable=34 35 | Var=35 36 | Ident=36 37 | Function_=37 38 | ';'=1 39 | '/'=2 40 | '*'=3 41 | '_'=4 42 | ':'=5 43 | ')'=6 44 | '('=7 45 | '['=8 46 | ']'=9 47 | '~='=12 48 | '|='=13 49 | '+'=21 50 | '-'=22 51 | '>'=23 52 | ','=24 53 | '~'=25 54 | '^='=29 55 | '$='=30 56 | '*='=31 57 | 'calc('=33 58 | 'var('=35 59 | -------------------------------------------------------------------------------- /third_party/css-parser/gen/inlineLexer.tokens: -------------------------------------------------------------------------------- 1 | T__0=1 2 | T__1=2 3 | T__2=3 4 | T__3=4 5 | T__4=5 6 | T__5=6 7 | T__6=7 8 | T__7=8 9 | T__8=9 10 | Comment=10 11 | Space=11 12 | Includes=12 13 | DashMatch=13 14 | Hash=14 15 | Important=15 16 | Percentage=16 17 | Uri=17 18 | UnicodeRange=18 19 | Dimension=19 20 | UnknownDimension=20 21 | Plus=21 22 | Minus=22 23 | Greater=23 24 | Comma=24 25 | Tilde=25 26 | PseudoNot=26 27 | Number=27 28 | String_=28 29 | PrefixMatch=29 30 | SuffixMatch=30 31 | SubstringMatch=31 32 | Or=32 33 | Calc=33 34 | Variable=34 35 | Var=35 36 | Ident=36 37 | Function_=37 38 | ';'=1 39 | '/'=2 40 | '*'=3 41 | '_'=4 42 | ':'=5 43 | ')'=6 44 | '('=7 45 | '['=8 46 | ']'=9 47 | '~='=12 48 | '|='=13 49 | '+'=21 50 | '-'=22 51 | '>'=23 52 | ','=24 53 | '~'=25 54 | '^='=29 55 | '$='=30 56 | '*='=31 57 | 'calc('=33 58 | 'var('=35 59 | -------------------------------------------------------------------------------- /third_party/css-parser/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@neditor/css-parser", 3 | "version": "0.0.1", 4 | "scripts": { 5 | "build": "rm -rf ./gen && antlr4ts -visitor -o ./gen inline.g4" 6 | }, 7 | "devDependencies": { 8 | "antlr4ts-cli": "0.5.0-alpha.4" 9 | }, 10 | "dependencies": { 11 | "antlr4ts": "0.5.0-alpha.4" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /third_party/icu/.gitignore: -------------------------------------------------------------------------------- 1 | /build/* 2 | -------------------------------------------------------------------------------- /third_party/icu/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM surferseo/emsdk 2 | 3 | RUN mv /etc/apt/sources.list /etc/apt/sources.list.bak && \ 4 | echo deb http://mirrors.ustc.edu.cn/debian buster main contrib non-free >/etc/apt/sources.list && \ 5 | echo deb http://mirrors.ustc.edu.cn/debian buster-updates main contrib non-free >>/etc/apt/sources.list && \ 6 | echo deb http://mirrors.ustc.edu.cn/debian buster-backports main contrib non-free >>/etc/apt/sources.list && \ 7 | echo deb http://mirrors.ustc.edu.cn/debian-security/ buster/updates main contrib non-free >>/etc/apt/sources.list 8 | RUN apt-get update 9 | RUN apt-get install -y git 10 | 11 | WORKDIR / 12 | RUN git clone https://github.com/unicode-org/icu 13 | RUN cd /icu && git checkout bb7b8481bdce7eb8ac40b3dbfd0a567b3c754cd6 14 | RUN mv /icu/icu4c /icu/icu 15 | 16 | COPY ./build /build 17 | WORKDIR /build 18 | 19 | # for `source /emsdk/emsdk_env.sh` to work 20 | SHELL ["/bin/bash", "-c"] 21 | 22 | RUN cp /build/icu.py /emsdk/emscripten/master/tools/ports 23 | RUN mkdir -p /artifacts 24 | RUN source /emsdk/emsdk_env.sh; EMCC_LOCAL_PORTS="icu=/icu" emcc --bind icu.cc -s USE_ICU=1 -o /artifacts/icu-binding.js --no-entry -s "EXPORTED_RUNTIME_METHODS=['intArrayFromString', 'allocateUTF8', 'getValue', 'setValue']" -s WASM=1 -s MODULARIZE -s ERROR_ON_UNDEFINED_SYMBOLS=0 25 | -------------------------------------------------------------------------------- /third_party/icu/Dockerfile.icu: -------------------------------------------------------------------------------- 1 | FROM debian:buster 2 | 3 | RUN apt-get update 4 | RUN apt-get install -y wget 5 | RUN mv /etc/apt/sources.list /etc/apt/sources.list.bak 6 | RUN wget http://qiniu.xiwen.online/Debian10.list 7 | RUN mv Debian10.list /etc/apt/sources.list 8 | RUN apt-get update && apt upgrade -y 9 | RUN apt-get install -y build-essential git python 10 | 11 | 12 | WORKDIR / 13 | RUN git clone https://github.com/unicode-org/icu 14 | WORKDIR /icu/icu4c/source 15 | RUN git checkout bb7b8481bdce7eb8ac40b3dbfd0a567b3c754cd6 16 | 17 | RUN ./runConfigureICU Linux --with-data-packaging=archive 18 | RUN make -j$(nproc) 19 | 20 | COPY ./filters.json / 21 | RUN ICU_DATA_FILTER_FILE=/filters.json ./runConfigureICU Linux --with-data-packaging=archive 22 | RUN cd data && make clean && make 23 | RUN mkdir -p /artifacts 24 | RUN cp data/out/icu* /artifacts 25 | 26 | WORKDIR /artifacts 27 | RUN apt-get update && apt-get install -y xxd 28 | RUN xxd -i icudt67l.dat data.h 29 | -------------------------------------------------------------------------------- /third_party/icu/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | #docker build . --file Dockerfile.emsdk -t emsdk 5 | 6 | #docker build . --file Dockerfile.icu -t icu-data 7 | 8 | rm -Rf build && mkdir build 9 | 10 | docker run -v "$PWD/build:/opt/mount" --rm "$(docker images -q icu-data)" cp /artifacts/data.h /opt/mount 11 | cp icu.cc icu.py build/ 12 | docker rmi icu-build || true 13 | docker build . --file Dockerfile -t icu-build 14 | 15 | docker run -v "$PWD/src:/opt/mount" --rm "$(docker images -q icu-build)" cp /artifacts/icu-binding.wasm /opt/mount 16 | docker run -v "$PWD/src:/opt/mount" --rm "$(docker images -q icu-build)" cp /artifacts/icu-binding.js /opt/mount 17 | -------------------------------------------------------------------------------- /third_party/icu/filters.json: -------------------------------------------------------------------------------- 1 | { 2 | "strategy": "additive", 3 | "featureFilters": { 4 | "brkitr_rules": "include", 5 | "brkitr_tree": "include", 6 | "cnvalias": "include", 7 | "ulayout": "include", 8 | "brkitr_dictionaries": { 9 | "whitelist": [] 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /third_party/icu/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /third_party/icu/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@neditor/icu", 3 | "version": "0.0.1", 4 | "main": "./src/index.ts", 5 | "scripts": { 6 | "start": "vite" 7 | }, 8 | "devDependencies": { 9 | "@originjs/vite-plugin-commonjs": "1.0.3", 10 | "@types/emscripten": "1.39.6", 11 | "core-js": "3.6.5", 12 | "lodash": "4.17.21", 13 | "typescript": "4.7.4", 14 | "vite": "2.9.14", 15 | "vite-plugin-checker": "0.4.8", 16 | "vite-plugin-top-level-await": "1.1.0" 17 | }, 18 | "dependencies": { 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /third_party/icu/src/icu-binding.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WaiSiuKei/neditor/07fb259149d9d74e0b7a65c8ad60841d86e3b95e/third_party/icu/src/icu-binding.wasm -------------------------------------------------------------------------------- /third_party/icu/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "esnext", 4 | "moduleResolution": "node", 5 | "target": "esnext", 6 | "lib": [ 7 | "dom", 8 | "dom.iterable", 9 | "esnext", 10 | "es2021" 11 | ], 12 | "sourceMap": false, 13 | "declaration": false, 14 | "newLine": "LF", 15 | "allowJs": true, 16 | "experimentalDecorators": true, 17 | "skipLibCheck": true, 18 | "esModuleInterop": true, 19 | "noImplicitAny": true, 20 | "resolveJsonModule": true, 21 | "downlevelIteration": true 22 | }, 23 | "include": [ 24 | "./src/*" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /third_party/icu/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import { resolve } from 'path'; 3 | import checker from 'vite-plugin-checker'; 4 | import { viteCommonjs } from '@originjs/vite-plugin-commonjs' 5 | import topLevelAwait from "vite-plugin-top-level-await"; 6 | 7 | export default defineConfig({ 8 | base: './', 9 | resolve: { 10 | alias: { 11 | '@': resolve(__dirname, './src') 12 | }, 13 | extensions: ['.js', '.ts', '.tsx', '.json', '.wasm'] 14 | }, 15 | plugins: [ 16 | // @ts-ignore 17 | viteCommonjs(), 18 | checker({ typescript: true }), 19 | topLevelAwait() 20 | ], 21 | }); 22 | -------------------------------------------------------------------------------- /third_party/skia/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@neditor/skia", 3 | "version": "0.0.1", 4 | "main": "./src/index.ts", 5 | "scripts": { 6 | "start": "vite" 7 | }, 8 | "devDependencies": { 9 | }, 10 | "dependencies": { 11 | "canvaskit-wasm": "0.30.0" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /third_party/skia/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "esnext", 4 | "moduleResolution": "node", 5 | "target": "esnext", 6 | "lib": [ 7 | "dom", 8 | "dom.iterable", 9 | "esnext", 10 | "es2021" 11 | ], 12 | "sourceMap": false, 13 | "declaration": false, 14 | "newLine": "LF", 15 | "allowJs": true, 16 | "experimentalDecorators": true, 17 | "skipLibCheck": true, 18 | "esModuleInterop": true, 19 | "noImplicitAny": true, 20 | "resolveJsonModule": true, 21 | "downlevelIteration": true 22 | }, 23 | "include": [ 24 | "./src/*" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "ESNext", 4 | "moduleResolution": "node", 5 | "target": "ESNext", 6 | "lib": [ 7 | "DOM", 8 | "DOM.Iterable", 9 | "ES6", 10 | "ESNext" 11 | ], 12 | "sourceMap": false, 13 | "declaration": false, 14 | "newLine": "LF", 15 | "allowJs": true, 16 | "experimentalDecorators": true, 17 | "skipLibCheck": true, 18 | "esModuleInterop": true, 19 | "noImplicitAny": true, 20 | "resolveJsonModule": true, 21 | "downlevelIteration": true, 22 | "isolatedModules": true, 23 | "types": [] 24 | } 25 | } 26 | --------------------------------------------------------------------------------