@liminis/editor 0.1.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +34 -0
- package/README.md +376 -0
- package/dist/annotations/anchor-align.d.ts +50 -0
- package/dist/annotations/anchor-align.js +74 -0
- package/dist/annotations/anchor-model.d.ts +81 -0
- package/dist/annotations/anchor-model.js +80 -0
- package/dist/annotations/anchor-resolver.d.ts +92 -0
- package/dist/annotations/anchor-resolver.js +231 -0
- package/dist/annotations/block-structure.d.ts +65 -0
- package/dist/annotations/block-structure.js +113 -0
- package/dist/annotations/types.d.ts +159 -0
- package/dist/annotations/types.js +50 -0
- package/dist/annotations.d.ts +25 -0
- package/dist/annotations.js +27 -0
- package/dist/app/App.d.ts +64 -0
- package/dist/app/App.js +180 -0
- package/dist/app/editor/AmbientCorrectionPlugin.d.ts +18 -0
- package/dist/app/editor/AmbientCorrectionPlugin.js +185 -0
- package/dist/app/editor/AnchorScrollPlugin.d.ts +16 -0
- package/dist/app/editor/AnchorScrollPlugin.js +134 -0
- package/dist/app/editor/AnnotationMarkerPlugin.d.ts +24 -0
- package/dist/app/editor/AnnotationMarkerPlugin.js +224 -0
- package/dist/app/editor/AnnotationPlugin.d.ts +42 -0
- package/dist/app/editor/AnnotationPlugin.js +135 -0
- package/dist/app/editor/AnnotationSurface.d.ts +34 -0
- package/dist/app/editor/AnnotationSurface.js +115 -0
- package/dist/app/editor/AssetContext.d.ts +20 -0
- package/dist/app/editor/AssetContext.js +81 -0
- package/dist/app/editor/BlockClickPlugin.d.ts +8 -0
- package/dist/app/editor/BlockClickPlugin.js +126 -0
- package/dist/app/editor/CodeBlockPlugin.d.ts +1 -0
- package/dist/app/editor/CodeBlockPlugin.js +219 -0
- package/dist/app/editor/CorrectionPanelPlugin.d.ts +14 -0
- package/dist/app/editor/CorrectionPanelPlugin.js +333 -0
- package/dist/app/editor/DragHandlePlugin.d.ts +1 -0
- package/dist/app/editor/DragHandlePlugin.js +300 -0
- package/dist/app/editor/Editor.d.ts +84 -0
- package/dist/app/editor/Editor.js +539 -0
- package/dist/app/editor/FrontmatterPlugin.d.ts +19 -0
- package/dist/app/editor/FrontmatterPlugin.js +301 -0
- package/dist/app/editor/ImageModal.d.ts +13 -0
- package/dist/app/editor/ImageModal.js +267 -0
- package/dist/app/editor/ImagePlugin.d.ts +3 -0
- package/dist/app/editor/ImagePlugin.js +187 -0
- package/dist/app/editor/LinkClickPlugin.d.ts +23 -0
- package/dist/app/editor/LinkClickPlugin.js +90 -0
- package/dist/app/editor/MarkdownShortcutsPlugin.d.ts +49 -0
- package/dist/app/editor/MarkdownShortcutsPlugin.js +373 -0
- package/dist/app/editor/OrderedTaskListPlugin.d.ts +21 -0
- package/dist/app/editor/OrderedTaskListPlugin.js +78 -0
- package/dist/app/editor/SearchPlugin.d.ts +1 -0
- package/dist/app/editor/SearchPlugin.js +379 -0
- package/dist/app/editor/SelectionContextMenuPlugin.d.ts +29 -0
- package/dist/app/editor/SelectionContextMenuPlugin.js +160 -0
- package/dist/app/editor/SlashMenu.d.ts +11 -0
- package/dist/app/editor/SlashMenu.js +494 -0
- package/dist/app/editor/SlashMenuPlugin.d.ts +1 -0
- package/dist/app/editor/SlashMenuPlugin.js +168 -0
- package/dist/app/editor/TableActionsPlugin.d.ts +1 -0
- package/dist/app/editor/TableActionsPlugin.js +398 -0
- package/dist/app/editor/TogglePlugin.d.ts +1 -0
- package/dist/app/editor/TogglePlugin.js +159 -0
- package/dist/app/editor/Toolbar.d.ts +10 -0
- package/dist/app/editor/Toolbar.js +263 -0
- package/dist/app/editor/WikiLinkExistencePlugin.d.ts +17 -0
- package/dist/app/editor/WikiLinkExistencePlugin.js +104 -0
- package/dist/app/editor/WikiLinkFormatPlugin.d.ts +17 -0
- package/dist/app/editor/WikiLinkFormatPlugin.js +203 -0
- package/dist/app/editor/annotation-marks.d.ts +177 -0
- package/dist/app/editor/annotation-marks.js +750 -0
- package/dist/app/editor/annotationCommands.d.ts +13 -0
- package/dist/app/editor/annotationCommands.js +11 -0
- package/dist/app/editor/c4/C4InteractiveRenderer.d.ts +35 -0
- package/dist/app/editor/c4/C4InteractiveRenderer.js +299 -0
- package/dist/app/editor/c4/edge-clipping.d.ts +24 -0
- package/dist/app/editor/c4/edge-clipping.js +139 -0
- package/dist/app/editor/c4/hooks/useC4DiagramDrag.d.ts +38 -0
- package/dist/app/editor/c4/hooks/useC4DiagramDrag.js +112 -0
- package/dist/app/editor/c4/layout.d.ts +25 -0
- package/dist/app/editor/c4/layout.js +839 -0
- package/dist/app/editor/c4/parser.d.ts +19 -0
- package/dist/app/editor/c4/parser.js +410 -0
- package/dist/app/editor/c4/render-to-string.d.ts +24 -0
- package/dist/app/editor/c4/render-to-string.js +34 -0
- package/dist/app/editor/c4/renderer.d.ts +64 -0
- package/dist/app/editor/c4/renderer.js +569 -0
- package/dist/app/editor/c4/types.d.ts +203 -0
- package/dist/app/editor/c4/types.js +43 -0
- package/dist/app/editor/correction-yaml.d.ts +10 -0
- package/dist/app/editor/correction-yaml.js +40 -0
- package/dist/app/editor/editorNodes.d.ts +9 -0
- package/dist/app/editor/editorNodes.js +36 -0
- package/dist/app/editor/index.d.ts +1 -0
- package/dist/app/editor/index.js +1 -0
- package/dist/app/editor/mark-overlap-resolver.d.ts +6 -0
- package/dist/app/editor/mark-overlap-resolver.js +45 -0
- package/dist/app/editor/nodes/C4Component.d.ts +14 -0
- package/dist/app/editor/nodes/C4Component.js +284 -0
- package/dist/app/editor/nodes/C4Node.d.ts +32 -0
- package/dist/app/editor/nodes/C4Node.js +112 -0
- package/dist/app/editor/nodes/CalloutNode.d.ts +23 -0
- package/dist/app/editor/nodes/CalloutNode.js +94 -0
- package/dist/app/editor/nodes/CustomLinkNode.d.ts +57 -0
- package/dist/app/editor/nodes/CustomLinkNode.js +209 -0
- package/dist/app/editor/nodes/CustomListItemNode.d.ts +46 -0
- package/dist/app/editor/nodes/CustomListItemNode.js +120 -0
- package/dist/app/editor/nodes/CustomListNode.d.ts +27 -0
- package/dist/app/editor/nodes/CustomListNode.js +54 -0
- package/dist/app/editor/nodes/DefinitionListNode.d.ts +44 -0
- package/dist/app/editor/nodes/DefinitionListNode.js +151 -0
- package/dist/app/editor/nodes/DiagramContextMenu.d.ts +45 -0
- package/dist/app/editor/nodes/DiagramContextMenu.js +116 -0
- package/dist/app/editor/nodes/EquationComponent.d.ts +17 -0
- package/dist/app/editor/nodes/EquationComponent.js +130 -0
- package/dist/app/editor/nodes/EquationNode.d.ts +43 -0
- package/dist/app/editor/nodes/EquationNode.js +179 -0
- package/dist/app/editor/nodes/FootnoteNode.d.ts +40 -0
- package/dist/app/editor/nodes/FootnoteNode.js +151 -0
- package/dist/app/editor/nodes/FrontmatterNode.d.ts +24 -0
- package/dist/app/editor/nodes/FrontmatterNode.js +88 -0
- package/dist/app/editor/nodes/HorizontalRuleNode.d.ts +17 -0
- package/dist/app/editor/nodes/HorizontalRuleNode.js +57 -0
- package/dist/app/editor/nodes/HtmlNode.d.ts +32 -0
- package/dist/app/editor/nodes/HtmlNode.js +121 -0
- package/dist/app/editor/nodes/ImageComponent.d.ts +11 -0
- package/dist/app/editor/nodes/ImageComponent.js +228 -0
- package/dist/app/editor/nodes/ImageNode.d.ts +53 -0
- package/dist/app/editor/nodes/ImageNode.js +183 -0
- package/dist/app/editor/nodes/ListItemParagraphBreakNode.d.ts +42 -0
- package/dist/app/editor/nodes/ListItemParagraphBreakNode.js +95 -0
- package/dist/app/editor/nodes/MermaidComponent.d.ts +11 -0
- package/dist/app/editor/nodes/MermaidComponent.js +216 -0
- package/dist/app/editor/nodes/MermaidNode.d.ts +27 -0
- package/dist/app/editor/nodes/MermaidNode.js +96 -0
- package/dist/app/editor/nodes/ToggleNode.d.ts +56 -0
- package/dist/app/editor/nodes/ToggleNode.js +237 -0
- package/dist/app/editor/nodes/diagram-context-menu.d.ts +11 -0
- package/dist/app/editor/nodes/diagram-context-menu.js +70 -0
- package/dist/app/editor/nodes/diagram-utils.d.ts +13 -0
- package/dist/app/editor/nodes/diagram-utils.js +29 -0
- package/dist/app/editor/nodes/index.d.ts +30 -0
- package/dist/app/editor/nodes/index.js +17 -0
- package/dist/app/editor/utils.d.ts +2 -0
- package/dist/app/editor/utils.js +17 -0
- package/dist/app/mapper/index.d.ts +3 -0
- package/dist/app/mapper/index.js +2 -0
- package/dist/app/mapper/lexicalToMdast.d.ts +62 -0
- package/dist/app/mapper/lexicalToMdast.js +1937 -0
- package/dist/app/mapper/mdastToLexical.d.ts +31 -0
- package/dist/app/mapper/mdastToLexical.js +1075 -0
- package/dist/contract.d.ts +8 -0
- package/dist/contract.js +8 -0
- package/dist/headless.d.ts +40 -0
- package/dist/headless.js +45 -0
- package/dist/host/context.d.ts +20 -0
- package/dist/host/context.js +24 -0
- package/dist/host/defaults.d.ts +21 -0
- package/dist/host/defaults.js +47 -0
- package/dist/host/messages.d.ts +26 -0
- package/dist/host/messages.js +47 -0
- package/dist/host/types.d.ts +75 -0
- package/dist/host/types.js +13 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +51 -0
- package/dist/markdown/annotate-sentinels.d.ts +42 -0
- package/dist/markdown/annotate-sentinels.js +86 -0
- package/dist/markdown/parse.d.ts +56 -0
- package/dist/markdown/parse.js +516 -0
- package/dist/markdown/stringify.d.ts +7 -0
- package/dist/markdown/stringify.js +439 -0
- package/dist/markdown/vendor/mdast-util-wiki-link/LICENSE +21 -0
- package/dist/markdown/vendor/mdast-util-wiki-link/README.md +41 -0
- package/dist/markdown/vendor/mdast-util-wiki-link/from-markdown.d.ts +72 -0
- package/dist/markdown/vendor/mdast-util-wiki-link/from-markdown.js +127 -0
- package/dist/markdown/vendor/mdast-util-wiki-link/index.d.ts +8 -0
- package/dist/markdown/vendor/mdast-util-wiki-link/index.js +6 -0
- package/dist/markdown/vendor/mdast-util-wiki-link/to-markdown.d.ts +17 -0
- package/dist/markdown/vendor/mdast-util-wiki-link/to-markdown.js +33 -0
- package/dist/markdown.d.ts +28 -0
- package/dist/markdown.js +44 -0
- package/dist/mathjax-config.d.ts +89 -0
- package/dist/mathjax-config.js +133 -0
- package/dist/nodes.d.ts +34 -0
- package/dist/nodes.js +34 -0
- package/dist/stores/correctionStore.d.ts +15 -0
- package/dist/stores/correctionStore.js +12 -0
- package/dist/stores/editorStore.d.ts +16 -0
- package/dist/stores/editorStore.js +27 -0
- package/dist/styles.css +2477 -0
- package/dist/types.d.ts +296 -0
- package/dist/types.js +91 -0
- package/dist/utils/file-types.d.ts +10 -0
- package/dist/utils/file-types.js +46 -0
- package/docs/annotations.md +134 -0
- package/docs/decisions/README.md +72 -0
- package/docs/decisions/adr-002.md +27 -0
- package/docs/decisions/adr-007.md +27 -0
- package/docs/decisions/adr-010.md +26 -0
- package/docs/decisions/adr-024.md +124 -0
- package/docs/decisions/adr-024b.md +0 -0
- package/docs/decisions/adr-025.md +103 -0
- package/docs/decisions/adr-027.md +66 -0
- package/docs/decisions/adr-057.md +67 -0
- package/docs/decisions/adr-070.md +131 -0
- package/docs/decisions/adr-075.md +410 -0
- package/docs/decisions/adr-076.md +183 -0
- package/docs/decisions/adr-077.md +410 -0
- package/docs/decisions/adr-078.md +243 -0
- package/docs/decisions/adr-079.md +167 -0
- package/docs/decisions/adr-080.md +325 -0
- package/docs/decisions/adr-081.md +108 -0
- package/docs/decisions/adr-082.md +123 -0
- package/docs/decisions/adr-083.md +204 -0
- package/docs/decisions/adr-084.md +179 -0
- package/docs/editor-api.md +106 -0
- package/docs/markdown-pipeline.md +282 -0
- package/docs/provenance.md +106 -0
- package/docs/zusammen-editor-capability-map.md +232 -0
- package/package.json +175 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@liminis/editor/contract` — the host-message contract, and nothing else.
|
|
3
|
+
*
|
|
4
|
+
* A DOM- and React-free entry point so a preload script (or any other boundary
|
|
5
|
+
* that must not pull renderer code into its bundle) can `import type` the
|
|
6
|
+
* message shapes without dragging in Lexical.
|
|
7
|
+
*/
|
|
8
|
+
export * from './types.js';
|
package/dist/contract.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@liminis/editor/contract` — the host-message contract, and nothing else.
|
|
3
|
+
*
|
|
4
|
+
* A DOM- and React-free entry point so a preload script (or any other boundary
|
|
5
|
+
* that must not pull renderer code into its bundle) can `import type` the
|
|
6
|
+
* message shapes without dragging in Lexical.
|
|
7
|
+
*/
|
|
8
|
+
export * from './types.js';
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@liminis/editor/headless` — the DOM-free surface.
|
|
3
|
+
*
|
|
4
|
+
* Consumed by the Electron **main** process, which declares `lib: ["ES2024"]`
|
|
5
|
+
* and no DOM libs. Nothing exported from here may reference a DOM global or
|
|
6
|
+
* transitively import Lexical, `react-dom` (client), or anything that touches
|
|
7
|
+
* `document`/`window`. `renderC4DiagramToSVG` uses `react-dom/server`, which is
|
|
8
|
+
* DOM-free; the MathJax *lite* adaptor is typed over `LiteElement`, not
|
|
9
|
+
* `HTMLElement`, which is why only the lite factory appears here.
|
|
10
|
+
*
|
|
11
|
+
* **Known exception, carried over unchanged: `./mathjax-config`.** Its
|
|
12
|
+
* `createLiteAdaptorDocument` export is DOM-free, but the module statically
|
|
13
|
+
* imports `browserAdaptor` alongside `liteAdaptor` at top level, so anything
|
|
14
|
+
* that loads this entry evaluates a DOM-adaptor module. That is *not* a
|
|
15
|
+
* regression introduced by the extraction — before it, `main/remote-session/
|
|
16
|
+
* routes.ts` imported `shared/mathjax-config` directly and pulled in the very
|
|
17
|
+
* same graph; main's runtime imports are byte-for-byte what they were. It is
|
|
18
|
+
* called out here because this entry's contract would otherwise read as
|
|
19
|
+
* stronger than it is. Splitting the module into lite and browser halves is the
|
|
20
|
+
* real fix and belongs with #940's build work, where the entry graphs get
|
|
21
|
+
* enforced rather than documented.
|
|
22
|
+
*
|
|
23
|
+
* **This contract is by convention, not by compiler enforcement.** Main's
|
|
24
|
+
* `lib: ["ES2024"]` does *not* currently make `Document`/`HTMLElement` unresolvable
|
|
25
|
+
* — some dependency already in `src/main`'s own graph pulls the DOM lib in (this
|
|
26
|
+
* predates the package extraction; `@types/node` alone does not do it). So a
|
|
27
|
+
* DOM-typed export added here would compile silently rather than failing main's
|
|
28
|
+
* typecheck. Widening this entry therefore requires reading the addition's import
|
|
29
|
+
* graph yourself. The fix for a violation is always to prune that graph, never to
|
|
30
|
+
* widen main's `lib`.
|
|
31
|
+
*/
|
|
32
|
+
export { renderC4DiagramToSVG } from './app/editor/c4/render-to-string.js';
|
|
33
|
+
export { parseC4, validateC4 } from './app/editor/c4/parser.js';
|
|
34
|
+
export { layoutC4Diagram } from './app/editor/c4/layout.js';
|
|
35
|
+
export { isSystem, isContainer, isComponent, isPerson, isExternal, isBoundary, } from './app/editor/c4/types.js';
|
|
36
|
+
export type { C4ElementType, C4Shape, C4Direction, C4Style, C4Properties, C4Element, C4Relationship, C4Diagram, Point, LayoutNode, LayoutEdge, LayoutResult, LayoutOptions, ParseError, ParseResult, ManualLayout, } from './app/editor/c4/types.js';
|
|
37
|
+
export { createLiteAdaptorDocument, TEX_PACKAGES } from './mathjax-config.js';
|
|
38
|
+
export type { TeXPackage, MathDocument, LiteMathJaxInstance } from './mathjax-config.js';
|
|
39
|
+
export { getFileType } from './utils/file-types.js';
|
|
40
|
+
export type { FileType } from './utils/file-types.js';
|
package/dist/headless.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@liminis/editor/headless` — the DOM-free surface.
|
|
3
|
+
*
|
|
4
|
+
* Consumed by the Electron **main** process, which declares `lib: ["ES2024"]`
|
|
5
|
+
* and no DOM libs. Nothing exported from here may reference a DOM global or
|
|
6
|
+
* transitively import Lexical, `react-dom` (client), or anything that touches
|
|
7
|
+
* `document`/`window`. `renderC4DiagramToSVG` uses `react-dom/server`, which is
|
|
8
|
+
* DOM-free; the MathJax *lite* adaptor is typed over `LiteElement`, not
|
|
9
|
+
* `HTMLElement`, which is why only the lite factory appears here.
|
|
10
|
+
*
|
|
11
|
+
* **Known exception, carried over unchanged: `./mathjax-config`.** Its
|
|
12
|
+
* `createLiteAdaptorDocument` export is DOM-free, but the module statically
|
|
13
|
+
* imports `browserAdaptor` alongside `liteAdaptor` at top level, so anything
|
|
14
|
+
* that loads this entry evaluates a DOM-adaptor module. That is *not* a
|
|
15
|
+
* regression introduced by the extraction — before it, `main/remote-session/
|
|
16
|
+
* routes.ts` imported `shared/mathjax-config` directly and pulled in the very
|
|
17
|
+
* same graph; main's runtime imports are byte-for-byte what they were. It is
|
|
18
|
+
* called out here because this entry's contract would otherwise read as
|
|
19
|
+
* stronger than it is. Splitting the module into lite and browser halves is the
|
|
20
|
+
* real fix and belongs with #940's build work, where the entry graphs get
|
|
21
|
+
* enforced rather than documented.
|
|
22
|
+
*
|
|
23
|
+
* **This contract is by convention, not by compiler enforcement.** Main's
|
|
24
|
+
* `lib: ["ES2024"]` does *not* currently make `Document`/`HTMLElement` unresolvable
|
|
25
|
+
* — some dependency already in `src/main`'s own graph pulls the DOM lib in (this
|
|
26
|
+
* predates the package extraction; `@types/node` alone does not do it). So a
|
|
27
|
+
* DOM-typed export added here would compile silently rather than failing main's
|
|
28
|
+
* typecheck. Widening this entry therefore requires reading the addition's import
|
|
29
|
+
* graph yourself. The fix for a violation is always to prune that graph, never to
|
|
30
|
+
* widen main's `lib`.
|
|
31
|
+
*/
|
|
32
|
+
// C4 diagram subsystem — parse, layout, and server-side SVG rendering
|
|
33
|
+
export { renderC4DiagramToSVG } from './app/editor/c4/render-to-string.js';
|
|
34
|
+
export { parseC4, validateC4 } from './app/editor/c4/parser.js';
|
|
35
|
+
export { layoutC4Diagram } from './app/editor/c4/layout.js';
|
|
36
|
+
export { isSystem, isContainer, isComponent, isPerson, isExternal, isBoundary, } from './app/editor/c4/types.js';
|
|
37
|
+
// MathJax lite-adaptor factory (equation rendering, server-side export).
|
|
38
|
+
//
|
|
39
|
+
// `createBrowserAdaptorDocument` / `BrowserMathJaxInstance` are deliberately NOT
|
|
40
|
+
// re-exported here: they are typed over `Document` and `HTMLElement`, which would
|
|
41
|
+
// contradict this entry's DOM-free contract. Their only consumer is
|
|
42
|
+
// `EquationComponent.tsx` inside the package, which imports them directly.
|
|
43
|
+
export { createLiteAdaptorDocument, TEX_PACKAGES } from './mathjax-config.js';
|
|
44
|
+
// File-type detection, used by both the mdast mapper and app-side callers
|
|
45
|
+
export { getFileType } from './utils/file-types.js';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React context carrying the injected host services.
|
|
3
|
+
*
|
|
4
|
+
* Context rather than prop drilling: five separate mount sites in liminis-app
|
|
5
|
+
* plus ~20 deeply nested plugins would otherwise have to thread a services
|
|
6
|
+
* object through `App` → `Editor` → every plugin (see ADR-075).
|
|
7
|
+
*/
|
|
8
|
+
import { type ReactNode } from 'react';
|
|
9
|
+
import type { EditorHostServices, ResolvedEditorHostServices } from './types.js';
|
|
10
|
+
export interface EditorHostProviderProps {
|
|
11
|
+
/** Host services. Any omitted member falls back to a safe default. */
|
|
12
|
+
services?: EditorHostServices;
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
}
|
|
15
|
+
export declare function EditorHostProvider({ services, children }: EditorHostProviderProps): import("react").JSX.Element;
|
|
16
|
+
/**
|
|
17
|
+
* Access the host services. Usable without a provider — an absent provider
|
|
18
|
+
* yields the same all-defaults object a provider with no services would.
|
|
19
|
+
*/
|
|
20
|
+
export declare function useEditorHost(): ResolvedEditorHostServices;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* React context carrying the injected host services.
|
|
4
|
+
*
|
|
5
|
+
* Context rather than prop drilling: five separate mount sites in liminis-app
|
|
6
|
+
* plus ~20 deeply nested plugins would otherwise have to thread a services
|
|
7
|
+
* object through `App` → `Editor` → every plugin (see ADR-075).
|
|
8
|
+
*/
|
|
9
|
+
import { createContext, useContext, useMemo } from 'react';
|
|
10
|
+
import { resolveHostServices } from './defaults.js';
|
|
11
|
+
const EditorHostContext = createContext(null);
|
|
12
|
+
export function EditorHostProvider({ services, children }) {
|
|
13
|
+
const resolved = useMemo(() => resolveHostServices(services), [services]);
|
|
14
|
+
return _jsx(EditorHostContext.Provider, { value: resolved, children: children });
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Access the host services. Usable without a provider — an absent provider
|
|
18
|
+
* yields the same all-defaults object a provider with no services would.
|
|
19
|
+
*/
|
|
20
|
+
export function useEditorHost() {
|
|
21
|
+
const value = useContext(EditorHostContext);
|
|
22
|
+
return value ?? FALLBACK_SERVICES;
|
|
23
|
+
}
|
|
24
|
+
const FALLBACK_SERVICES = resolveHostServices(undefined);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safe defaults for every host service.
|
|
3
|
+
*
|
|
4
|
+
* FR-003 / US2-AC2: a host that supplies no logger, no bridge and no resolvers
|
|
5
|
+
* must still get a working `<Editor>`. Nothing here throws, and nothing here
|
|
6
|
+
* touches a global that only exists inside Electron.
|
|
7
|
+
*/
|
|
8
|
+
import type { EditorHostBridge, EditorHostServices, EditorLoggerFactory, ResolvedEditorHostServices } from './types.js';
|
|
9
|
+
/** Console-backed logger namespaced the same way liminis-app's logger is. */
|
|
10
|
+
export declare const defaultLoggerFactory: EditorLoggerFactory;
|
|
11
|
+
/**
|
|
12
|
+
* Bridge that drops outbound messages and never delivers inbound ones.
|
|
13
|
+
*
|
|
14
|
+
* This is the correct standalone behaviour: with no host there is nothing to
|
|
15
|
+
* apply edits to, so the editor simply behaves as an uncontrolled component.
|
|
16
|
+
*/
|
|
17
|
+
export declare const noopBridge: EditorHostBridge;
|
|
18
|
+
/** Last-resort error surface when the host provides no toast mechanism. */
|
|
19
|
+
export declare const defaultNotifyError: (message: string, description?: string) => void;
|
|
20
|
+
/** Fill in every unsupplied service with its safe default. */
|
|
21
|
+
export declare function resolveHostServices(services: EditorHostServices | undefined): ResolvedEditorHostServices;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safe defaults for every host service.
|
|
3
|
+
*
|
|
4
|
+
* FR-003 / US2-AC2: a host that supplies no logger, no bridge and no resolvers
|
|
5
|
+
* must still get a working `<Editor>`. Nothing here throws, and nothing here
|
|
6
|
+
* touches a global that only exists inside Electron.
|
|
7
|
+
*/
|
|
8
|
+
/** Console-backed logger namespaced the same way liminis-app's logger is. */
|
|
9
|
+
export const defaultLoggerFactory = (namespace) => ({
|
|
10
|
+
debug: (...args) => console.debug(`[${namespace}]`, ...args),
|
|
11
|
+
info: (...args) => console.info(`[${namespace}]`, ...args),
|
|
12
|
+
warn: (...args) => console.warn(`[${namespace}]`, ...args),
|
|
13
|
+
error: (...args) => console.error(`[${namespace}]`, ...args),
|
|
14
|
+
});
|
|
15
|
+
/**
|
|
16
|
+
* Bridge that drops outbound messages and never delivers inbound ones.
|
|
17
|
+
*
|
|
18
|
+
* This is the correct standalone behaviour: with no host there is nothing to
|
|
19
|
+
* apply edits to, so the editor simply behaves as an uncontrolled component.
|
|
20
|
+
*/
|
|
21
|
+
export const noopBridge = {
|
|
22
|
+
postMessage: () => { },
|
|
23
|
+
addMessageHandler: () => () => { },
|
|
24
|
+
};
|
|
25
|
+
/** Last-resort error surface when the host provides no toast mechanism. */
|
|
26
|
+
export const defaultNotifyError = (message, description) => {
|
|
27
|
+
if (description) {
|
|
28
|
+
console.error(message, description);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
console.error(message);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
/** Fill in every unsupplied service with its safe default. */
|
|
35
|
+
export function resolveHostServices(services) {
|
|
36
|
+
return {
|
|
37
|
+
bridge: services?.bridge ?? noopBridge,
|
|
38
|
+
logger: services?.logger ?? defaultLoggerFactory,
|
|
39
|
+
notifyError: services?.notifyError ?? defaultNotifyError,
|
|
40
|
+
// Left undefined on purpose when absent: the plugins that use these treat
|
|
41
|
+
// "service missing" as "feature unavailable, do nothing" — exactly what the
|
|
42
|
+
// pre-extraction host-API presence guards did.
|
|
43
|
+
resolveWikiLinks: services?.resolveWikiLinks,
|
|
44
|
+
onScrollToAnchor: services?.onScrollToAnchor,
|
|
45
|
+
corrections: services?.corrections,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host message helpers, rebuilt on top of `EditorHostBridge.postMessage`.
|
|
3
|
+
*
|
|
4
|
+
* These used to live in `liminis-app/src/editor/messaging-electron.ts`. Keeping
|
|
5
|
+
* them package-side means every host adapter only has to implement
|
|
6
|
+
* `postMessage` + `addMessageHandler`, and the wire payloads are identical by
|
|
7
|
+
* construction rather than by convention.
|
|
8
|
+
*/
|
|
9
|
+
import type { TextEdit, UIToHostMessage } from '../types.js';
|
|
10
|
+
import type { EditorHostBridge, EditorLogger } from './types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Members are declared as function-typed properties rather than methods on
|
|
13
|
+
* purpose: callers destructure them (`const { openLink } = useHostMessages()`),
|
|
14
|
+
* which the `unbound-method` lint rule rightly flags for real methods.
|
|
15
|
+
*/
|
|
16
|
+
export interface HostMessageApi {
|
|
17
|
+
postMessage: (message: UIToHostMessage) => void;
|
|
18
|
+
requestInit: () => void;
|
|
19
|
+
requestSettings: () => void;
|
|
20
|
+
applyTextEdits: (edits: TextEdit[], reason: 'typing' | 'drag' | 'paste' | 'format') => void;
|
|
21
|
+
writeAsset: (dataUri: string, suggestedName?: string) => void;
|
|
22
|
+
openLink: (url: string) => void;
|
|
23
|
+
}
|
|
24
|
+
export declare function createHostMessageApi(bridge: EditorHostBridge, log: EditorLogger): HostMessageApi;
|
|
25
|
+
/** Hook form of {@link createHostMessageApi}, bound to the ambient host services. */
|
|
26
|
+
export declare function useHostMessages(): HostMessageApi;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host message helpers, rebuilt on top of `EditorHostBridge.postMessage`.
|
|
3
|
+
*
|
|
4
|
+
* These used to live in `liminis-app/src/editor/messaging-electron.ts`. Keeping
|
|
5
|
+
* them package-side means every host adapter only has to implement
|
|
6
|
+
* `postMessage` + `addMessageHandler`, and the wire payloads are identical by
|
|
7
|
+
* construction rather than by convention.
|
|
8
|
+
*/
|
|
9
|
+
import { useMemo } from 'react';
|
|
10
|
+
import { useEditorHost } from './context.js';
|
|
11
|
+
export function createHostMessageApi(bridge, log) {
|
|
12
|
+
const postMessage = (message) => {
|
|
13
|
+
bridge.postMessage(message);
|
|
14
|
+
};
|
|
15
|
+
return {
|
|
16
|
+
postMessage,
|
|
17
|
+
requestInit: () => {
|
|
18
|
+
log.info('Requesting init from host');
|
|
19
|
+
postMessage({ type: 'REQUEST_INIT' });
|
|
20
|
+
},
|
|
21
|
+
requestSettings: () => {
|
|
22
|
+
postMessage({ type: 'REQUEST_SETTINGS' });
|
|
23
|
+
},
|
|
24
|
+
applyTextEdits: (edits, reason) => {
|
|
25
|
+
log.debug('applyTextEdits', {
|
|
26
|
+
reason,
|
|
27
|
+
edits: edits.map((e) => ({ start: e.start, end: e.end, newTextLen: e.newText.length })),
|
|
28
|
+
});
|
|
29
|
+
postMessage({ type: 'APPLY_TEXT_EDITS', edits, reason });
|
|
30
|
+
},
|
|
31
|
+
writeAsset: (dataUri, suggestedName) => {
|
|
32
|
+
postMessage({ type: 'WRITE_ASSET', dataUri, suggestedName });
|
|
33
|
+
},
|
|
34
|
+
openLink: (url) => {
|
|
35
|
+
log.debug('openLink', { url });
|
|
36
|
+
postMessage({ type: 'OPEN_LINK', url });
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** Hook form of {@link createHostMessageApi}, bound to the ambient host services. */
|
|
41
|
+
export function useHostMessages() {
|
|
42
|
+
const { bridge, logger } = useEditorHost();
|
|
43
|
+
return useMemo(() => createHostMessageApi(bridge, makeLog(logger)), [bridge, logger]);
|
|
44
|
+
}
|
|
45
|
+
function makeLog(logger) {
|
|
46
|
+
return logger('slashmd/messaging');
|
|
47
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The host seam for `@liminis/editor`.
|
|
3
|
+
*
|
|
4
|
+
* The package boundary is drawn at **persistence**: anything about text ranges,
|
|
5
|
+
* marks, rendering and in-document UX lives in this package; storage, lifecycle,
|
|
6
|
+
* identity and higher-level panels live in the embedding application. Everything
|
|
7
|
+
* the package needs from its host crosses that seam through the interfaces below
|
|
8
|
+
* (see ADR-075).
|
|
9
|
+
*
|
|
10
|
+
* Every member is optional and has a safe default (see `./defaults.ts`), so
|
|
11
|
+
* `<Editor>` renders in a host that supplies nothing at all.
|
|
12
|
+
*/
|
|
13
|
+
import type { HostToUIMessage, UIToHostMessage } from '../types.js';
|
|
14
|
+
/** Minimal logging surface. Structurally compatible with liminis-app's `Logger`. */
|
|
15
|
+
export interface EditorLogger {
|
|
16
|
+
debug: (...args: unknown[]) => void;
|
|
17
|
+
info: (...args: unknown[]) => void;
|
|
18
|
+
warn: (...args: unknown[]) => void;
|
|
19
|
+
error: (...args: unknown[]) => void;
|
|
20
|
+
}
|
|
21
|
+
/** Factory producing a namespaced logger, e.g. `createLogger('slashmd/App')`. */
|
|
22
|
+
export type EditorLoggerFactory = (namespace: string) => EditorLogger;
|
|
23
|
+
/**
|
|
24
|
+
* The channel through which the package talks to its embedding environment.
|
|
25
|
+
*
|
|
26
|
+
* Deliberately two methods: all higher-level helpers (`requestInit`,
|
|
27
|
+
* `applyTextEdits`, `writeAsset`, `openLink`) are built on `postMessage`
|
|
28
|
+
* inside the package, so every host adapter emits byte-identical payloads.
|
|
29
|
+
*/
|
|
30
|
+
export interface EditorHostBridge {
|
|
31
|
+
postMessage(message: UIToHostMessage): void;
|
|
32
|
+
/** Subscribe to host → UI messages. Returns an unsubscribe function. */
|
|
33
|
+
addMessageHandler(handler: (message: HostToUIMessage) => void): () => void;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Persistence and knowledge-graph services backing the correction feature.
|
|
37
|
+
*
|
|
38
|
+
* The in-editor correction UI is package-side; reading and writing the
|
|
39
|
+
* corrections document and driving the knowledge tools stays host-side. That
|
|
40
|
+
* split is the package boundary drawn at persistence (`docs/decisions/adr-075.md`):
|
|
41
|
+
* this package renders and edits, and never decides where bytes live or which
|
|
42
|
+
* knowledge pipeline consumes them.
|
|
43
|
+
*/
|
|
44
|
+
export interface CorrectionHostServices {
|
|
45
|
+
/** Raw YAML text of the corrections file, or `null` when it does not exist. */
|
|
46
|
+
readCorrections(): Promise<string | null>;
|
|
47
|
+
/** Persist the corrections file. The host owns mkdir and atomic-write semantics. */
|
|
48
|
+
writeCorrections(yaml: string): Promise<void>;
|
|
49
|
+
/** Entity-name suggestions for an autocomplete query. */
|
|
50
|
+
suggestEntities(query: string, numResults: number): Promise<string[]>;
|
|
51
|
+
/** Passage/source-name suggestions for an autocomplete query. */
|
|
52
|
+
suggestPassages(query: string, numResults: number, minScore: number): Promise<string[]>;
|
|
53
|
+
/** Apply pending corrections to the knowledge graph. Resolves true on success. */
|
|
54
|
+
applyCorrections(): Promise<boolean>;
|
|
55
|
+
}
|
|
56
|
+
/** Everything the package may ask of its host. */
|
|
57
|
+
export interface EditorHostServices {
|
|
58
|
+
/** Host message channel (Electron IPC in liminis-app). */
|
|
59
|
+
bridge?: EditorHostBridge;
|
|
60
|
+
/** Namespaced logger factory. */
|
|
61
|
+
logger?: EditorLoggerFactory;
|
|
62
|
+
/**
|
|
63
|
+
* Resolve wiki-link targets to existing paths. Returns a map of target →
|
|
64
|
+
* resolved path, or `null` for targets that do not resolve.
|
|
65
|
+
*/
|
|
66
|
+
resolveWikiLinks?: (targets: string[]) => Promise<Record<string, string | null>>;
|
|
67
|
+
/** Subscribe to host-driven "scroll to this anchor" requests. */
|
|
68
|
+
onScrollToAnchor?: (callback: (anchor: string) => void) => () => void;
|
|
69
|
+
/** Surface a user-visible error (a toast in liminis-app). */
|
|
70
|
+
notifyError?: (message: string, description?: string) => void;
|
|
71
|
+
/** Correction persistence + knowledge-graph services. */
|
|
72
|
+
corrections?: CorrectionHostServices;
|
|
73
|
+
}
|
|
74
|
+
/** `EditorHostServices` with every member resolved to a concrete implementation. */
|
|
75
|
+
export type ResolvedEditorHostServices = Required<Pick<EditorHostServices, 'bridge' | 'logger' | 'notifyError'>> & Pick<EditorHostServices, 'resolveWikiLinks' | 'onScrollToAnchor' | 'corrections'>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The host seam for `@liminis/editor`.
|
|
3
|
+
*
|
|
4
|
+
* The package boundary is drawn at **persistence**: anything about text ranges,
|
|
5
|
+
* marks, rendering and in-document UX lives in this package; storage, lifecycle,
|
|
6
|
+
* identity and higher-level panels live in the embedding application. Everything
|
|
7
|
+
* the package needs from its host crosses that seam through the interfaces below
|
|
8
|
+
* (see ADR-075).
|
|
9
|
+
*
|
|
10
|
+
* Every member is optional and has a safe default (see `./defaults.ts`), so
|
|
11
|
+
* `<Editor>` renders in a host that supplies nothing at all.
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@liminis/editor` — the curated public entry point.
|
|
3
|
+
*
|
|
4
|
+
* Consumers import from here (or from one of the other three declared
|
|
5
|
+
* subpaths: `./headless`, `./contract`, `./styles.css`) and never from a deep
|
|
6
|
+
* path into package internals (FR-002 / SC-002). If a consumer needs something
|
|
7
|
+
* that is not exported here, the answer is to add a considered export — never a
|
|
8
|
+
* deep import, and never a re-exported internals barrel.
|
|
9
|
+
*/
|
|
10
|
+
export { EditorHostProvider, useEditorHost } from './host/context.js';
|
|
11
|
+
export type { EditorHostProviderProps } from './host/context.js';
|
|
12
|
+
export { resolveHostServices, defaultLoggerFactory, defaultNotifyError, noopBridge, } from './host/defaults.js';
|
|
13
|
+
export { createHostMessageApi, useHostMessages } from './host/messages.js';
|
|
14
|
+
export type { HostMessageApi } from './host/messages.js';
|
|
15
|
+
export type { CorrectionHostServices, EditorHostBridge, EditorHostServices, EditorLogger, EditorLoggerFactory, ResolvedEditorHostServices, } from './host/types.js';
|
|
16
|
+
export { App } from './app/App.js';
|
|
17
|
+
export type { CursorState } from './app/App.js';
|
|
18
|
+
export { Editor } from './app/editor/index.js';
|
|
19
|
+
export type { SweepFn } from './app/editor/AmbientCorrectionPlugin.js';
|
|
20
|
+
export type { SelectionContextMenuEvent } from './app/editor/SelectionContextMenuPlugin.js';
|
|
21
|
+
export { OPEN_ANNOTATION_COMPOSER_COMMAND } from './app/editor/annotationCommands.js';
|
|
22
|
+
export type { AnnotationCreateEvent } from './app/editor/AnnotationPlugin.js';
|
|
23
|
+
export type { Annotation, AnnotationKind, AnnotationKindConfig, AnnotationKindConfigs, AnnotationCreateAffordance, AnnotationMarkerStyle, AnnotationPresentation, AnnotationEditorHandle, MarkerTarget, } from './annotations/types.js';
|
|
24
|
+
export { parseMarkdown } from './markdown/parse.js';
|
|
25
|
+
export type { ParseOptions, ParseResult } from './markdown/parse.js';
|
|
26
|
+
export { isParagraph, isHeading, isList, isListItem, isBlockquote, isCode, isThematicBreak, isTable, isImage, isLink, isHtml, isText, isStrong, isEmphasis, isInlineCode, isDelete, } from './markdown/parse.js';
|
|
27
|
+
export { stringifyMarkdown } from './markdown/stringify.js';
|
|
28
|
+
export type { StringifyOptions } from './markdown/stringify.js';
|
|
29
|
+
export { importMarkdownToLexical, exportLexicalToMdast } from './app/mapper/index.js';
|
|
30
|
+
export type { ExportOptions, WikiLinkPromotionMode } from './app/mapper/index.js';
|
|
31
|
+
export * from './types.js';
|
|
32
|
+
export { useEditorStore } from './stores/editorStore.js';
|
|
33
|
+
export { findSvgElement } from './app/editor/nodes/diagram-utils.js';
|
|
34
|
+
export * from './headless.js';
|
|
35
|
+
export type { ParseResult as C4ParseResult, ParseError as C4ParseError, } from './app/editor/c4/types.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@liminis/editor` — the curated public entry point.
|
|
3
|
+
*
|
|
4
|
+
* Consumers import from here (or from one of the other three declared
|
|
5
|
+
* subpaths: `./headless`, `./contract`, `./styles.css`) and never from a deep
|
|
6
|
+
* path into package internals (FR-002 / SC-002). If a consumer needs something
|
|
7
|
+
* that is not exported here, the answer is to add a considered export — never a
|
|
8
|
+
* deep import, and never a re-exported internals barrel.
|
|
9
|
+
*/
|
|
10
|
+
// --- Host seam -------------------------------------------------------------
|
|
11
|
+
// Injected services are how the package reaches its embedding environment.
|
|
12
|
+
export { EditorHostProvider, useEditorHost } from './host/context.js';
|
|
13
|
+
export { resolveHostServices, defaultLoggerFactory, defaultNotifyError, noopBridge, } from './host/defaults.js';
|
|
14
|
+
export { createHostMessageApi, useHostMessages } from './host/messages.js';
|
|
15
|
+
// --- Components ------------------------------------------------------------
|
|
16
|
+
export { App } from './app/App.js';
|
|
17
|
+
export { Editor } from './app/editor/index.js';
|
|
18
|
+
// --- Annotations, React surface (ADR-077) ---------------------------------
|
|
19
|
+
// The kind-configuration types a host needs to turn the mechanism on, plus the
|
|
20
|
+
// create-event shape. The DOM-free anchor model, resolver and marker-target
|
|
21
|
+
// helpers live on the `./annotations` subpath instead, so they stay callable
|
|
22
|
+
// outside a rendered editor.
|
|
23
|
+
export { OPEN_ANNOTATION_COMPOSER_COMMAND } from './app/editor/annotationCommands.js';
|
|
24
|
+
// --- Markdown pipeline -----------------------------------------------------
|
|
25
|
+
export { parseMarkdown } from './markdown/parse.js';
|
|
26
|
+
export { isParagraph, isHeading, isList, isListItem, isBlockquote, isCode, isThematicBreak, isTable, isImage, isLink, isHtml, isText, isStrong, isEmphasis, isInlineCode, isDelete, } from './markdown/parse.js';
|
|
27
|
+
export { stringifyMarkdown } from './markdown/stringify.js';
|
|
28
|
+
export { importMarkdownToLexical, exportLexicalToMdast } from './app/mapper/index.js';
|
|
29
|
+
// --- Host message contract -------------------------------------------------
|
|
30
|
+
export * from './types.js';
|
|
31
|
+
// --- Editor-owned stores ---------------------------------------------------
|
|
32
|
+
// `useEditorStore` is public: `EditorColumn` drives the frontmatter tray from
|
|
33
|
+
// the app toolbar.
|
|
34
|
+
//
|
|
35
|
+
// `useCorrectionStore` is deliberately NOT exported. ADR-075 justifies keeping
|
|
36
|
+
// it package-side on the grounds that it has no consumer outside the two
|
|
37
|
+
// editor plugins; publishing it would invite exactly the app-side consumer that
|
|
38
|
+
// invalidates that reasoning, with nothing at the boundary to catch it.
|
|
39
|
+
export { useEditorStore } from './stores/editorStore.js';
|
|
40
|
+
// --- Utilities the host also needs ----------------------------------------
|
|
41
|
+
export { findSvgElement } from './app/editor/nodes/diagram-utils.js';
|
|
42
|
+
// --- DOM-free surface, re-exported for renderer-side convenience ----------
|
|
43
|
+
// Renderer consumers may import these from here; the Electron main process must
|
|
44
|
+
// use '@liminis/editor/headless' instead (it has no DOM libs).
|
|
45
|
+
//
|
|
46
|
+
// NOTE: `./headless` also exports a `ParseResult` (the C4 parser's `{diagram,
|
|
47
|
+
// errors}`), which this entry's explicit markdown `ParseResult` above shadows —
|
|
48
|
+
// explicit exports win over `export *` in both ES and TypeScript. The C4 shape is
|
|
49
|
+
// therefore reachable from here only under the alias below. `./headless` itself
|
|
50
|
+
// exports it unaliased, where there is no collision.
|
|
51
|
+
export * from './headless.js';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Unicode Private-Use-Area sentinel tokens annotated-serialize mode
|
|
3
|
+
* brackets a live annotation mark's content with, plus the one helper that
|
|
4
|
+
* removes them again.
|
|
5
|
+
*
|
|
6
|
+
* They live here — below both the mapper and the stringifier — because the
|
|
7
|
+
* *invariant* they exist to serve reaches into both. Annotate mode must differ
|
|
8
|
+
* from a plain export by the tokens and nothing else (see
|
|
9
|
+
* `lexicalToMdast.ts`'s annotated-serialize header): the whole recovered-range
|
|
10
|
+
* calculation in `annotation-marks.ts` is offset arithmetic against a plain
|
|
11
|
+
* export of the same state. So every *decision* either module makes from a
|
|
12
|
+
* piece of text — "does this list item already carry an explicit `[ ]`
|
|
13
|
+
* marker?", "does this paragraph end in a colon?" — has to be made against the
|
|
14
|
+
* sentinel-free form, or enabling annotate mode changes output somewhere the
|
|
15
|
+
* caller never looks (Liminis #970).
|
|
16
|
+
*/
|
|
17
|
+
export declare const SENTINEL_OPEN_START = "\uE000";
|
|
18
|
+
export declare const SENTINEL_OPEN_END = "\uE001";
|
|
19
|
+
export declare const SENTINEL_CLOSE_START = "\uE002";
|
|
20
|
+
export declare const SENTINEL_CLOSE_END = "\uE003";
|
|
21
|
+
/**
|
|
22
|
+
* `text` with every annotate-mode sentinel token removed. A no-op — and cheap
|
|
23
|
+
* — for the overwhelmingly common case of a string that contains none, which
|
|
24
|
+
* is every string on the disk-write path.
|
|
25
|
+
*/
|
|
26
|
+
export declare function stripAnnotateSentinels(text: string): string;
|
|
27
|
+
export interface SentinelSplitPart {
|
|
28
|
+
text: string;
|
|
29
|
+
isSentinel: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Split `text` into alternating sentinel-token and plain-text parts.
|
|
33
|
+
*
|
|
34
|
+
* Used by `stringify.ts`'s force-escape handling (#17): a force-escaped
|
|
35
|
+
* text node's `getTextContent()` can have a sentinel token spliced onto
|
|
36
|
+
* either end by `sentinelAugmentedText` (annotate-serialize mode, see the
|
|
37
|
+
* module doc comment above) before the force-escape data is read, so
|
|
38
|
+
* treating the whole string as "one force-escaped run" would wrap the
|
|
39
|
+
* token's own characters in escape placeholders too. Splitting first lets
|
|
40
|
+
* the caller escape only the real content and leave token parts verbatim.
|
|
41
|
+
*/
|
|
42
|
+
export declare function splitOnSentinelTokens(text: string): SentinelSplitPart[];
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Unicode Private-Use-Area sentinel tokens annotated-serialize mode
|
|
3
|
+
* brackets a live annotation mark's content with, plus the one helper that
|
|
4
|
+
* removes them again.
|
|
5
|
+
*
|
|
6
|
+
* They live here — below both the mapper and the stringifier — because the
|
|
7
|
+
* *invariant* they exist to serve reaches into both. Annotate mode must differ
|
|
8
|
+
* from a plain export by the tokens and nothing else (see
|
|
9
|
+
* `lexicalToMdast.ts`'s annotated-serialize header): the whole recovered-range
|
|
10
|
+
* calculation in `annotation-marks.ts` is offset arithmetic against a plain
|
|
11
|
+
* export of the same state. So every *decision* either module makes from a
|
|
12
|
+
* piece of text — "does this list item already carry an explicit `[ ]`
|
|
13
|
+
* marker?", "does this paragraph end in a colon?" — has to be made against the
|
|
14
|
+
* sentinel-free form, or enabling annotate mode changes output somewhere the
|
|
15
|
+
* caller never looks (Liminis #970).
|
|
16
|
+
*/
|
|
17
|
+
export const SENTINEL_OPEN_START = '\u{E000}';
|
|
18
|
+
export const SENTINEL_OPEN_END = '\u{E001}';
|
|
19
|
+
export const SENTINEL_CLOSE_START = '\u{E002}';
|
|
20
|
+
export const SENTINEL_CLOSE_END = '\u{E003}';
|
|
21
|
+
/**
|
|
22
|
+
* Matches one whole sentinel token — an open pair or a close pair, with an
|
|
23
|
+
* annotation id between them.
|
|
24
|
+
*
|
|
25
|
+
* Two things keep this from eating real content, and both are load-bearing:
|
|
26
|
+
*
|
|
27
|
+
* 1. **The id is a negated character class** excluding all four delimiters, so
|
|
28
|
+
* the run cannot cross a delimiter and the match is forced to end at the
|
|
29
|
+
* first one it meets. Greediness is irrelevant for that reason — do not
|
|
30
|
+
* "simplify" this to `.*?`. A lazy dot matches delimiters happily, so on
|
|
31
|
+
* `…␀b␀id␁…` it would span from the *stray* opener to the first closer and
|
|
32
|
+
* delete the real text `b` along with the token.
|
|
33
|
+
* 2. **Each alternative pairs its own delimiters** — open-with-open,
|
|
34
|
+
* close-with-close. Choosing the two ends from independent classes would
|
|
35
|
+
* also match an open *paired with a close*, which the emitters never
|
|
36
|
+
* produce (see `openToken`/`closeToken` in `lexicalToMdast.ts`); on
|
|
37
|
+
* `…␀real text␃…` that mismatched span would be deleted, taking the real
|
|
38
|
+
* text with it. Only well-formed tokens should ever be removed, because
|
|
39
|
+
* this function feeds *decisions* — the explicit task-marker test and the
|
|
40
|
+
* ends-with-a-colon join rule — where a wrong strip silently changes
|
|
41
|
+
* output.
|
|
42
|
+
*/
|
|
43
|
+
const SENTINEL_ID_RUN = `[^${SENTINEL_OPEN_START}${SENTINEL_OPEN_END}${SENTINEL_CLOSE_START}${SENTINEL_CLOSE_END}]*`;
|
|
44
|
+
const SENTINEL_TOKEN = new RegExp(`${SENTINEL_OPEN_START}${SENTINEL_ID_RUN}${SENTINEL_OPEN_END}` +
|
|
45
|
+
`|${SENTINEL_CLOSE_START}${SENTINEL_ID_RUN}${SENTINEL_CLOSE_END}`, 'gu');
|
|
46
|
+
/**
|
|
47
|
+
* `text` with every annotate-mode sentinel token removed. A no-op — and cheap
|
|
48
|
+
* — for the overwhelmingly common case of a string that contains none, which
|
|
49
|
+
* is every string on the disk-write path.
|
|
50
|
+
*/
|
|
51
|
+
export function stripAnnotateSentinels(text) {
|
|
52
|
+
if (!text.includes(SENTINEL_OPEN_START) && !text.includes(SENTINEL_CLOSE_START))
|
|
53
|
+
return text;
|
|
54
|
+
return text.replace(SENTINEL_TOKEN, '');
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Split `text` into alternating sentinel-token and plain-text parts.
|
|
58
|
+
*
|
|
59
|
+
* Used by `stringify.ts`'s force-escape handling (#17): a force-escaped
|
|
60
|
+
* text node's `getTextContent()` can have a sentinel token spliced onto
|
|
61
|
+
* either end by `sentinelAugmentedText` (annotate-serialize mode, see the
|
|
62
|
+
* module doc comment above) before the force-escape data is read, so
|
|
63
|
+
* treating the whole string as "one force-escaped run" would wrap the
|
|
64
|
+
* token's own characters in escape placeholders too. Splitting first lets
|
|
65
|
+
* the caller escape only the real content and leave token parts verbatim.
|
|
66
|
+
*/
|
|
67
|
+
export function splitOnSentinelTokens(text) {
|
|
68
|
+
if (!text.includes(SENTINEL_OPEN_START) && !text.includes(SENTINEL_CLOSE_START)) {
|
|
69
|
+
return [{ text, isSentinel: false }];
|
|
70
|
+
}
|
|
71
|
+
const parts = [];
|
|
72
|
+
let cursor = 0;
|
|
73
|
+
SENTINEL_TOKEN.lastIndex = 0;
|
|
74
|
+
let match;
|
|
75
|
+
while ((match = SENTINEL_TOKEN.exec(text)) !== null) {
|
|
76
|
+
if (match.index > cursor) {
|
|
77
|
+
parts.push({ text: text.slice(cursor, match.index), isSentinel: false });
|
|
78
|
+
}
|
|
79
|
+
parts.push({ text: match[0], isSentinel: true });
|
|
80
|
+
cursor = match.index + match[0].length;
|
|
81
|
+
}
|
|
82
|
+
if (cursor < text.length) {
|
|
83
|
+
parts.push({ text: text.slice(cursor), isSentinel: false });
|
|
84
|
+
}
|
|
85
|
+
return parts;
|
|
86
|
+
}
|