smartrte-react 0.3.5 → 1.0.0-beta.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/CHANGELOG.md +10 -0
- package/README.md +106 -14
- package/dist/adapters/domChecklistCommandBridge.js +1 -1
- package/dist/adapters/domCommandBridge.d.ts +2 -2
- package/dist/adapters/domCommandBridge.js +2 -2
- package/dist/adapters/domInlineAtomCommandBridge.d.ts +10 -9
- package/dist/adapters/domInlineAtomCommandBridge.js +154 -50
- package/dist/adapters/domInlineImageCommandBridge.d.ts +7 -7
- package/dist/adapters/domInlineImageCommandBridge.js +6 -72
- package/dist/adapters/domMoveCommandBridge.js +1 -1
- package/dist/adapters/domSelectionBridge.d.ts +3 -3
- package/dist/adapters/domSmartDocument.d.ts +4 -4
- package/dist/adapters/domSmartDocument.js +2 -2
- package/dist/adapters/domTableCommandBridge.d.ts +42 -16
- package/dist/adapters/domTableCommandBridge.js +203 -173
- package/dist/adapters/internalFlags.d.ts +0 -8
- package/dist/adapters/internalFlags.js +0 -24
- package/dist/adapters/legacyListShadowComparator.d.ts +1 -1
- package/dist/adapters/legacyListShadowComparator.js +1 -1
- package/dist/adapters/pdfPrint.d.ts +8 -0
- package/dist/adapters/pdfPrint.js +22 -0
- package/dist/adapters/shadowMode.d.ts +3 -3
- package/dist/adapters/shadowMode.js +1 -1
- package/dist/canonicalEditorRuntime.d.ts +53 -1
- package/dist/canonicalEditorRuntime.js +61 -4
- package/dist/capabilityPresets.d.ts +27 -0
- package/dist/capabilityPresets.js +21 -0
- package/dist/collabTransport.d.ts +66 -0
- package/dist/collabTransport.js +1 -0
- package/dist/commentProvider.d.ts +24 -0
- package/dist/commentProvider.js +1 -0
- package/dist/components/CanonicalAuthorityEditor.d.ts +35 -1
- package/dist/components/CanonicalAuthorityEditor.js +1086 -112
- package/dist/components/ClassicEditorAuthority.d.ts +93 -22
- package/dist/components/ClassicEditorAuthority.js +11 -43
- package/dist/components/ColorPickerPopover.d.ts +39 -0
- package/dist/components/ColorPickerPopover.js +285 -0
- package/dist/components/CommentMarkers.d.ts +25 -0
- package/dist/components/CommentMarkers.js +105 -0
- package/dist/components/CommentThreadPanel.d.ts +26 -0
- package/dist/components/CommentThreadPanel.js +47 -0
- package/dist/components/ContextMenu.d.ts +23 -0
- package/dist/components/ContextMenu.js +141 -0
- package/dist/components/FormulaLibraryPopover.d.ts +16 -0
- package/dist/components/FormulaLibraryPopover.js +107 -0
- package/dist/components/LinkEditorPopover.d.ts +3 -1
- package/dist/components/LinkEditorPopover.js +22 -6
- package/dist/components/MediaManager.d.ts +4 -20
- package/dist/components/MediaManager.js +35 -6
- package/dist/components/MediaOverlay.d.ts +36 -0
- package/dist/components/MediaOverlay.js +163 -0
- package/dist/components/SpecialCharacterPopover.d.ts +18 -0
- package/dist/components/SpecialCharacterPopover.js +99 -0
- package/dist/components/StructuralSuggestionMarkers.d.ts +20 -0
- package/dist/components/StructuralSuggestionMarkers.js +56 -0
- package/dist/components/SuggestionPanel.d.ts +26 -0
- package/dist/components/SuggestionPanel.js +33 -0
- package/dist/components/TableBorderPopover.d.ts +59 -0
- package/dist/components/TableBorderPopover.js +147 -0
- package/dist/components/TableResizeHandles.d.ts +25 -0
- package/dist/components/TableResizeHandles.js +301 -0
- package/dist/components/TableSizePickerPopover.d.ts +16 -0
- package/dist/components/TableSizePickerPopover.js +126 -0
- package/dist/components/ToolbarPrimitives.d.ts +129 -0
- package/dist/components/ToolbarPrimitives.js +146 -0
- package/dist/components/VersionHistoryPanel.d.ts +9 -0
- package/dist/components/VersionHistoryPanel.js +157 -0
- package/dist/formulaLibrary.d.ts +31 -0
- package/dist/formulaLibrary.js +90 -0
- package/dist/index.d.ts +22 -24
- package/dist/index.js +14 -11
- package/dist/mediaManagerAdapter.d.ts +12 -0
- package/dist/mediaManagerAdapter.js +34 -0
- package/dist/mediaProvider.d.ts +21 -1
- package/dist/specialCharacters.d.ts +22 -0
- package/dist/specialCharacters.js +86 -0
- package/dist/standalone/classic-editor-embed.js +19 -12
- package/dist/suggestionProvider.d.ts +17 -0
- package/dist/suggestionProvider.js +1 -0
- package/dist/theme.d.ts +1 -1
- package/dist/theme.js +133 -1
- package/dist/versionProvider.d.ts +29 -0
- package/dist/versionProvider.js +1 -0
- package/package.json +28 -14
- package/dist/adapters/canonicalInlineCommandBridge.d.ts +0 -15
- package/dist/adapters/canonicalInlineCommandBridge.js +0 -269
- package/dist/adapters/canonicalListCommandBridge.d.ts +0 -26
- package/dist/adapters/canonicalListCommandBridge.js +0 -430
- package/dist/adapters/coreBold.d.ts +0 -5
- package/dist/adapters/coreBold.js +0 -3
- package/dist/adapters/documentFormats.d.ts +0 -26
- package/dist/adapters/documentFormats.js +0 -49
- package/dist/adapters/docxFormat.d.ts +0 -5
- package/dist/adapters/docxFormat.js +0 -272
- package/dist/adapters/domBlockCommandBridge.d.ts +0 -19
- package/dist/adapters/domBlockCommandBridge.js +0 -79
- package/dist/adapters/inlineMarkCoreExecution.d.ts +0 -9
- package/dist/adapters/inlineMarkCoreExecution.js +0 -25
- package/dist/adapters/pdfFormat.d.ts +0 -23
- package/dist/adapters/pdfFormat.js +0 -204
- package/dist/adapters/portableDocxAtoms.d.ts +0 -4
- package/dist/adapters/portableDocxAtoms.js +0 -58
- package/dist/adapters/styledDocxFormat.d.ts +0 -8
- package/dist/adapters/styledDocxFormat.js +0 -190
- package/dist/builtInFormatDefinitions.d.ts +0 -5
- package/dist/builtInFormatDefinitions.js +0 -82
- package/dist/canonicalAuthorityFlag.d.ts +0 -32
- package/dist/canonicalAuthorityFlag.js +0 -49
- package/dist/canonicalClipboardRuntime.d.ts +0 -15
- package/dist/canonicalClipboardRuntime.js +0 -76
- package/dist/components/ClassicEditor.d.ts +0 -76
- package/dist/components/ClassicEditor.js +0 -6729
- package/dist/editorController.d.ts +0 -78
- package/dist/editorController.js +0 -298
- package/dist/formatFidelity.d.ts +0 -14
- package/dist/formatFidelity.js +0 -107
- package/dist/formatRuntime.d.ts +0 -50
- package/dist/formatRuntime.js +0 -20
- package/dist/pluginRuntime.d.ts +0 -68
- package/dist/pluginRuntime.js +0 -78
- package/dist/standalone/editor.js +0 -1068
- package/dist/test-harness/atomShadowComparator.d.ts +0 -14
- package/dist/test-harness/atomShadowComparator.js +0 -63
- package/dist/test-harness/blockShadowComparator.d.ts +0 -30
- package/dist/test-harness/blockShadowComparator.js +0 -140
- package/dist/test-harness/clipboardShadowComparator.d.ts +0 -12
- package/dist/test-harness/clipboardShadowComparator.js +0 -46
- package/dist/test-harness/inlineShadowComparator.d.ts +0 -32
- package/dist/test-harness/inlineShadowComparator.js +0 -152
- package/dist/test-harness/legacyAtomEngine.d.ts +0 -10
- package/dist/test-harness/legacyAtomEngine.js +0 -15
- package/dist/test-harness/legacyBlockEngine.d.ts +0 -31
- package/dist/test-harness/legacyBlockEngine.js +0 -31
- package/dist/test-harness/legacyClipboardEngine.d.ts +0 -14
- package/dist/test-harness/legacyClipboardEngine.js +0 -67
- package/dist/test-harness/legacyInlineEngine.d.ts +0 -19
- package/dist/test-harness/legacyInlineEngine.js +0 -49
- package/dist/test-harness/legacyTableEngine.d.ts +0 -12
- package/dist/test-harness/legacyTableEngine.js +0 -25
- package/dist/test-harness/tableShadowComparator.d.ts +0 -29
- package/dist/test-harness/tableShadowComparator.js +0 -101
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { DocumentVersion } from "smartrte-core/foundation";
|
|
2
|
+
/**
|
|
3
|
+
* The listing-cheap projection of a `DocumentVersion` - a host's backend
|
|
4
|
+
* shouldn't have to ship every saved version's full document body just to
|
|
5
|
+
* render a history list. Mirrors `MediaProvider.search` returning
|
|
6
|
+
* `MediaItem` (metadata) rather than raw asset bytes.
|
|
7
|
+
*/
|
|
8
|
+
export interface VersionListEntry {
|
|
9
|
+
readonly id: string;
|
|
10
|
+
readonly label?: string;
|
|
11
|
+
readonly createdAt: number;
|
|
12
|
+
readonly authorId?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Host-owned version-storage boundary, mirroring `MediaProvider`'s shape
|
|
16
|
+
* and its "the editor never persists anything itself" contract: the host
|
|
17
|
+
* backend is the source of truth, and `save`/`list`/`load`/`remove` are
|
|
18
|
+
* the entire boundary. There is deliberately no shipped default
|
|
19
|
+
* implementation, exactly like `MediaProvider` - when a host doesn't
|
|
20
|
+
* supply one, the version-history feature is simply unavailable, the same
|
|
21
|
+
* way the media toolbar buttons are disabled when `mediaProvider` is
|
|
22
|
+
* absent.
|
|
23
|
+
*/
|
|
24
|
+
export interface VersionProvider {
|
|
25
|
+
save(version: DocumentVersion): Promise<VersionListEntry>;
|
|
26
|
+
list(): Promise<readonly VersionListEntry[]>;
|
|
27
|
+
load(id: string): Promise<DocumentVersion>;
|
|
28
|
+
remove(id: string): Promise<void>;
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "smartrte-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0-beta.1",
|
|
4
4
|
"description": "A powerful, feature-rich Rich Text Editor for React with support for tables, mathematical formulas (LaTeX/KaTeX), and media management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"module": "dist/index.js",
|
|
8
8
|
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./standalone/classic-editor-embed": {
|
|
15
|
+
"types": "./dist/standalone/classic-editor-embed.d.ts",
|
|
16
|
+
"import": "./dist/standalone/classic-editor-embed.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
9
19
|
"files": [
|
|
10
20
|
"dist",
|
|
11
21
|
"!dist/**/*.test.js",
|
|
12
22
|
"!dist/**/*.test.d.ts",
|
|
23
|
+
"!dist/test-harness/**",
|
|
13
24
|
"README.md",
|
|
14
25
|
"CHANGELOG.md"
|
|
15
26
|
],
|
|
@@ -41,16 +52,6 @@
|
|
|
41
52
|
},
|
|
42
53
|
"author": "Smart RTE Contributors",
|
|
43
54
|
"license": "MIT",
|
|
44
|
-
"scripts": {
|
|
45
|
-
"build": "tsc -p tsconfig.json",
|
|
46
|
-
"prepublishOnly": "npm run build",
|
|
47
|
-
"dev": "pnpm build",
|
|
48
|
-
"lint": "tsc -p tsconfig.json --noEmit",
|
|
49
|
-
"test": "vitest run --passWithNoTests",
|
|
50
|
-
"storybook": "storybook dev -p 6006",
|
|
51
|
-
"build-storybook": "storybook build",
|
|
52
|
-
"e2e": "playwright test --pass-with-no-tests"
|
|
53
|
-
},
|
|
54
55
|
"publishConfig": {
|
|
55
56
|
"access": "public"
|
|
56
57
|
},
|
|
@@ -60,16 +61,18 @@
|
|
|
60
61
|
"react-dom": ">=18"
|
|
61
62
|
},
|
|
62
63
|
"devDependencies": {
|
|
64
|
+
"@axe-core/playwright": "^4.12.1",
|
|
63
65
|
"@playwright/test": "^1.48.2",
|
|
64
66
|
"@storybook/addon-essentials": "^8.6.14",
|
|
65
67
|
"@storybook/react": "^8.6.14",
|
|
66
68
|
"@storybook/react-vite": "^8.6.14",
|
|
69
|
+
"@types/node": "^22",
|
|
67
70
|
"@types/pdfjs-dist": "^2.10.377",
|
|
68
71
|
"@types/react": "^18.3.4",
|
|
69
72
|
"@types/react-dom": "^18.3.0",
|
|
73
|
+
"jsdom": "^25.0.1",
|
|
70
74
|
"react": "18.3.1",
|
|
71
75
|
"react-dom": "18.3.1",
|
|
72
|
-
"jsdom": "^25.0.1",
|
|
73
76
|
"storybook": "^8.6.14",
|
|
74
77
|
"typescript": "^5.6.2",
|
|
75
78
|
"vite": "^5.4.8",
|
|
@@ -77,8 +80,19 @@
|
|
|
77
80
|
},
|
|
78
81
|
"dependencies": {
|
|
79
82
|
"jszip": "^3.10.1",
|
|
83
|
+
"katex": "^0.18.4",
|
|
84
|
+
"lucide-react": "^1.35.0",
|
|
80
85
|
"mammoth": "^1.11.0",
|
|
81
86
|
"pdfjs-dist": "^5.4.530",
|
|
82
|
-
"smartrte-core": "^0.
|
|
87
|
+
"smartrte-core": "^1.0.0-beta.1"
|
|
88
|
+
},
|
|
89
|
+
"scripts": {
|
|
90
|
+
"build": "rm -rf dist && tsc -p tsconfig.json",
|
|
91
|
+
"dev": "pnpm build",
|
|
92
|
+
"lint": "tsc -p tsconfig.json --noEmit",
|
|
93
|
+
"test": "vitest run --passWithNoTests",
|
|
94
|
+
"storybook": "storybook dev -p 6006",
|
|
95
|
+
"build-storybook": "storybook build",
|
|
96
|
+
"e2e": "pnpm --filter smartrte-core build && pnpm build && playwright test --pass-with-no-tests"
|
|
83
97
|
}
|
|
84
|
-
}
|
|
98
|
+
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { inlineToolDeclarations, type Attrs, type SmartMark } from "smartrte-core";
|
|
2
|
-
export type CanonicalInlineToolId = typeof inlineToolDeclarations[number]["id"];
|
|
3
|
-
export type CanonicalInlineIntent = "apply" | "remove" | "toggle" | "setAttrs" | "clearAll" | "editLink";
|
|
4
|
-
export interface CanonicalInlineExecution {
|
|
5
|
-
readonly changed: boolean;
|
|
6
|
-
readonly operations: number;
|
|
7
|
-
readonly coverage: "all" | "partial" | "none";
|
|
8
|
-
}
|
|
9
|
-
export declare const executeCanonicalInlineTool: (root: HTMLElement, id: CanonicalInlineToolId, intent?: CanonicalInlineIntent, attrs?: Attrs) => CanonicalInlineExecution;
|
|
10
|
-
/** Consumes adapter stored marks for ordinary text input. Composition remains
|
|
11
|
-
* owned by the canonical surface; atom-aware composition is explicitly Phase 7. */
|
|
12
|
-
export declare const installCanonicalInlineStoredMarkInput: (root: HTMLElement) => (() => void);
|
|
13
|
-
export declare const canonicalInlineStoredMarks: (root: HTMLElement) => readonly SmartMark[];
|
|
14
|
-
export declare const hasCanonicalInlineStoredMarkOverride: (root: HTMLElement) => boolean;
|
|
15
|
-
export declare const describeCanonicalInlineCoverage: (root: HTMLElement) => Readonly<Record<string, "all" | "partial">>;
|
|
@@ -1,269 +0,0 @@
|
|
|
1
|
-
import { applyMarkCommand, applyOperations, clearAllMarksCommand, createFoundationEditor, createNodeId, createScopeIndex, editLinkCommand, executeMarkTool, foundationSchema, inlineToolDeclarations, parseCanonicalListHtml, removeLinkCommand, removeMarkCommand, serializeCanonicalListHtml, toggleMarkCommand, } from "smartrte-core";
|
|
2
|
-
const storedMarks = new WeakMap();
|
|
3
|
-
const ownerSelector = "p,h1,h2,h3,h4,h5,h6";
|
|
4
|
-
const ownerId = (owner) => {
|
|
5
|
-
var _a;
|
|
6
|
-
(_a = owner.dataset).smartId || (_a.smartId = createNodeId());
|
|
7
|
-
return owner.dataset.smartId;
|
|
8
|
-
};
|
|
9
|
-
const contentWidth = (node) => {
|
|
10
|
-
if (node.nodeType === node.TEXT_NODE)
|
|
11
|
-
return node.nodeValue?.length || 0;
|
|
12
|
-
if (!(node instanceof HTMLElement))
|
|
13
|
-
return [...node.childNodes].reduce((sum, child) => sum + contentWidth(child), 0);
|
|
14
|
-
if (node.closest("[data-smart-ui]"))
|
|
15
|
-
return 0;
|
|
16
|
-
if (node.matches("br,[data-smart-atomic='true']"))
|
|
17
|
-
return 1;
|
|
18
|
-
return [...node.childNodes].reduce((sum, child) => sum + contentWidth(child), 0);
|
|
19
|
-
};
|
|
20
|
-
const pointInOwner = (root, node, offset) => {
|
|
21
|
-
const element = node instanceof HTMLElement ? node : node?.parentElement;
|
|
22
|
-
const owner = element?.closest(ownerSelector);
|
|
23
|
-
if (!owner || !root.contains(owner) || !node)
|
|
24
|
-
return null;
|
|
25
|
-
const range = root.ownerDocument.createRange();
|
|
26
|
-
range.selectNodeContents(owner);
|
|
27
|
-
try {
|
|
28
|
-
range.setEnd(node, offset);
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
33
|
-
return { ownerId: ownerId(owner), offset: contentWidth(range.cloneContents()) };
|
|
34
|
-
};
|
|
35
|
-
const captureSelection = (root) => {
|
|
36
|
-
const native = root.ownerDocument.defaultView?.getSelection();
|
|
37
|
-
if (!native?.anchorNode || !native.focusNode)
|
|
38
|
-
return null;
|
|
39
|
-
const anchor = pointInOwner(root, native.anchorNode, native.anchorOffset);
|
|
40
|
-
const head = pointInOwner(root, native.focusNode, native.focusOffset);
|
|
41
|
-
return anchor && head ? { anchor, head } : null;
|
|
42
|
-
};
|
|
43
|
-
const domPointAt = (owner, requested) => {
|
|
44
|
-
let remaining = Math.max(0, requested);
|
|
45
|
-
const visit = (parent) => {
|
|
46
|
-
for (let index = 0; index < parent.childNodes.length; index += 1) {
|
|
47
|
-
const child = parent.childNodes[index];
|
|
48
|
-
if (child.nodeType === child.TEXT_NODE) {
|
|
49
|
-
const length = child.nodeValue?.length || 0;
|
|
50
|
-
if (remaining <= length)
|
|
51
|
-
return { node: child, offset: remaining };
|
|
52
|
-
remaining -= length;
|
|
53
|
-
}
|
|
54
|
-
else if (child instanceof HTMLElement && child.matches("br,[data-smart-atomic='true']")) {
|
|
55
|
-
if (remaining === 0)
|
|
56
|
-
return { node: parent, offset: index };
|
|
57
|
-
if (remaining === 1)
|
|
58
|
-
return { node: parent, offset: index + 1 };
|
|
59
|
-
remaining -= 1;
|
|
60
|
-
}
|
|
61
|
-
else if (!(child instanceof HTMLElement && child.closest("[data-smart-ui]"))) {
|
|
62
|
-
const found = visit(child);
|
|
63
|
-
if (found)
|
|
64
|
-
return found;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return null;
|
|
68
|
-
};
|
|
69
|
-
return visit(owner) || { node: owner, offset: owner.childNodes.length };
|
|
70
|
-
};
|
|
71
|
-
const restoreSelection = (root, snapshot) => {
|
|
72
|
-
const byId = (id) => Array.from(root.querySelectorAll("[data-smart-id]"))
|
|
73
|
-
.find((node) => node.matches(ownerSelector) && node.dataset.smartId === id) || null;
|
|
74
|
-
const anchorOwner = byId(snapshot.anchor.ownerId);
|
|
75
|
-
const headOwner = byId(snapshot.head.ownerId);
|
|
76
|
-
const native = root.ownerDocument.defaultView?.getSelection();
|
|
77
|
-
if (!anchorOwner || !headOwner || !native)
|
|
78
|
-
return;
|
|
79
|
-
const anchor = domPointAt(anchorOwner, snapshot.anchor.offset);
|
|
80
|
-
const head = domPointAt(headOwner, snapshot.head.offset);
|
|
81
|
-
native.setBaseAndExtent(anchor.node, anchor.offset, head.node, head.offset);
|
|
82
|
-
};
|
|
83
|
-
const selectedOwners = (root, snapshot) => {
|
|
84
|
-
const owners = Array.from(root.querySelectorAll(ownerSelector));
|
|
85
|
-
const indexes = [snapshot.anchor.ownerId, snapshot.head.ownerId].map((id) => owners.findIndex((owner) => ownerId(owner) === id));
|
|
86
|
-
if (indexes.some((index) => index < 0))
|
|
87
|
-
return [];
|
|
88
|
-
const [from, to] = [Math.min(...indexes), Math.max(...indexes)];
|
|
89
|
-
return owners.slice(from, to + 1);
|
|
90
|
-
};
|
|
91
|
-
const modelSelection = (document, snapshot) => {
|
|
92
|
-
const positions = createScopeIndex().positions(document, foundationSchema);
|
|
93
|
-
const point = (value) => {
|
|
94
|
-
const content = positions.contentRangeOf(value.ownerId);
|
|
95
|
-
return content ? { path: [...content.from.path], offset: value.offset } : null;
|
|
96
|
-
};
|
|
97
|
-
const anchor = point(snapshot.anchor);
|
|
98
|
-
const head = point(snapshot.head);
|
|
99
|
-
return anchor && head ? { type: "text", anchor, head } : null;
|
|
100
|
-
};
|
|
101
|
-
const parseOwners = (owners) => parseCanonicalListHtml(owners.map((owner) => owner.outerHTML).join(""));
|
|
102
|
-
const renderOwners = (root, owners, document, preserveEditedLink = false) => {
|
|
103
|
-
const template = root.ownerDocument.createElement("template");
|
|
104
|
-
template.innerHTML = serializeCanonicalListHtml(document, { fragment: true });
|
|
105
|
-
owners.forEach((owner) => {
|
|
106
|
-
const id = ownerId(owner);
|
|
107
|
-
const replacement = Array.from(template.content.querySelectorAll("[data-smart-id]"))
|
|
108
|
-
.find((candidate) => candidate.dataset.smartId === id);
|
|
109
|
-
if (replacement) {
|
|
110
|
-
const existingLinks = owner.querySelectorAll("a");
|
|
111
|
-
const replacementLinks = replacement.querySelectorAll("a");
|
|
112
|
-
if (preserveEditedLink && existingLinks.length === 1 && replacementLinks.length === 1
|
|
113
|
-
&& existingLinks[0].textContent === replacementLinks[0].textContent
|
|
114
|
-
&& owner.textContent === replacement.textContent) {
|
|
115
|
-
const next = replacementLinks[0];
|
|
116
|
-
existingLinks[0].setAttribute("href", next.getAttribute("href") || "");
|
|
117
|
-
if (next.target) {
|
|
118
|
-
existingLinks[0].target = next.target;
|
|
119
|
-
existingLinks[0].rel = "noopener noreferrer";
|
|
120
|
-
}
|
|
121
|
-
else {
|
|
122
|
-
existingLinks[0].removeAttribute("target");
|
|
123
|
-
existingLinks[0].removeAttribute("rel");
|
|
124
|
-
}
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
owner.innerHTML = replacement.innerHTML;
|
|
128
|
-
owner.querySelectorAll("span[style]").forEach((span) => {
|
|
129
|
-
const color = span.style.color;
|
|
130
|
-
const background = span.style.backgroundColor;
|
|
131
|
-
const size = span.style.fontSize;
|
|
132
|
-
const family = span.style.fontFamily;
|
|
133
|
-
span.removeAttribute("style");
|
|
134
|
-
if (color)
|
|
135
|
-
span.style.color = color;
|
|
136
|
-
if (background)
|
|
137
|
-
span.style.backgroundColor = background;
|
|
138
|
-
if (size)
|
|
139
|
-
span.style.fontSize = size;
|
|
140
|
-
if (family)
|
|
141
|
-
span.style.fontFamily = family;
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
};
|
|
146
|
-
const declarationFor = (id) => {
|
|
147
|
-
const declaration = inlineToolDeclarations.find((tool) => tool.id === id);
|
|
148
|
-
if (!declaration)
|
|
149
|
-
throw new Error(`Unknown inline tool "${id}".`);
|
|
150
|
-
return declaration;
|
|
151
|
-
};
|
|
152
|
-
export const executeCanonicalInlineTool = (root, id, intent = "toggle", attrs) => {
|
|
153
|
-
const ownerElements = Array.from(root.querySelectorAll(ownerSelector));
|
|
154
|
-
const ownersWithPersistedIds = new Set(ownerElements.filter((owner) => owner.hasAttribute("data-smart-id")));
|
|
155
|
-
const cleanupTemporaryIds = () => ownerElements.forEach((owner) => {
|
|
156
|
-
if (!ownersWithPersistedIds.has(owner))
|
|
157
|
-
owner.removeAttribute("data-smart-id");
|
|
158
|
-
});
|
|
159
|
-
const snapshot = captureSelection(root);
|
|
160
|
-
if (!snapshot)
|
|
161
|
-
return { changed: false, operations: 0, coverage: "none" };
|
|
162
|
-
const owners = selectedOwners(root, snapshot);
|
|
163
|
-
if (!owners.length)
|
|
164
|
-
return { changed: false, operations: 0, coverage: "none" };
|
|
165
|
-
const document = parseOwners(owners);
|
|
166
|
-
const selection = modelSelection(document, snapshot);
|
|
167
|
-
if (!selection)
|
|
168
|
-
return { changed: false, operations: 0, coverage: "none" };
|
|
169
|
-
const declaration = declarationFor(id);
|
|
170
|
-
const index = createScopeIndex();
|
|
171
|
-
const scope = index.resolve(document, selection, { want: "inline-range" }, foundationSchema);
|
|
172
|
-
const description = index.resolve(document, selection, { want: "describe" }, foundationSchema);
|
|
173
|
-
const coverage = description.marks.some((entry) => entry.mark.type === declaration.markType && entry.coverage === "all")
|
|
174
|
-
? "all" : description.marks.some((entry) => entry.mark.type === declaration.markType) ? "partial" : "none";
|
|
175
|
-
if (scope.collapsed && !(declaration.markType === "link" && (intent === "remove" || intent === "editLink"))) {
|
|
176
|
-
const editor = createFoundationEditor({ document, selection, storedMarks: storedMarks.get(root) });
|
|
177
|
-
executeMarkTool(editor, declaration, intent, attrs);
|
|
178
|
-
storedMarks.set(root, [...(editor.storedMarks || [])]);
|
|
179
|
-
cleanupTemporaryIds();
|
|
180
|
-
return { changed: false, operations: 0, coverage };
|
|
181
|
-
}
|
|
182
|
-
const ctx = { schema: foundationSchema, positions: index.positions(document, foundationSchema) };
|
|
183
|
-
const operations = intent === "clearAll" ? clearAllMarksCommand(document, scope, {}, ctx)
|
|
184
|
-
: intent === "remove" && declaration.markType === "link" ? removeLinkCommand(document, scope, { markType: "link" }, ctx)
|
|
185
|
-
: intent === "editLink" && declaration.markType === "link" ? editLinkCommand(document, scope, attrs, ctx)
|
|
186
|
-
: intent === "remove" ? removeMarkCommand(document, scope, { markType: declaration.markType }, ctx)
|
|
187
|
-
: intent === "toggle" ? toggleMarkCommand(document, scope, { markType: declaration.markType, attrs, coverage }, ctx)
|
|
188
|
-
: applyMarkCommand(document, scope, { markType: declaration.markType, attrs }, ctx);
|
|
189
|
-
if (!operations.length)
|
|
190
|
-
return { changed: false, operations: 0, coverage };
|
|
191
|
-
const output = applyOperations(document, operations);
|
|
192
|
-
renderOwners(root, owners, output, intent === "editLink");
|
|
193
|
-
restoreSelection(root, snapshot);
|
|
194
|
-
cleanupTemporaryIds();
|
|
195
|
-
storedMarks.delete(root);
|
|
196
|
-
return { changed: true, operations: operations.length, coverage };
|
|
197
|
-
};
|
|
198
|
-
/** Consumes adapter stored marks for ordinary text input. Composition remains
|
|
199
|
-
* owned by the canonical surface; atom-aware composition is explicitly Phase 7. */
|
|
200
|
-
export const installCanonicalInlineStoredMarkInput = (root) => {
|
|
201
|
-
const beforeInput = (event) => {
|
|
202
|
-
if (!storedMarks.has(root) || event.inputType !== "insertText" || !event.data)
|
|
203
|
-
return;
|
|
204
|
-
const marks = storedMarks.get(root) || [];
|
|
205
|
-
const ownerElements = Array.from(root.querySelectorAll(ownerSelector));
|
|
206
|
-
const ownersWithPersistedIds = new Set(ownerElements.filter((owner) => owner.hasAttribute("data-smart-id")));
|
|
207
|
-
const snapshot = captureSelection(root);
|
|
208
|
-
if (!snapshot || snapshot.anchor.ownerId !== snapshot.head.ownerId || snapshot.anchor.offset !== snapshot.head.offset)
|
|
209
|
-
return;
|
|
210
|
-
event.preventDefault();
|
|
211
|
-
const owners = selectedOwners(root, snapshot);
|
|
212
|
-
const document = parseOwners(owners);
|
|
213
|
-
const selection = modelSelection(document, snapshot);
|
|
214
|
-
if (!selection)
|
|
215
|
-
return;
|
|
216
|
-
const editor = createFoundationEditor({ document, selection, storedMarks: marks });
|
|
217
|
-
if (marks.length)
|
|
218
|
-
editor.typeText(event.data, { timestamp: Date.now() });
|
|
219
|
-
else
|
|
220
|
-
editor.transact((builder) => {
|
|
221
|
-
builder.operations.push({ type: "insertText", pos: selection.head, text: event.data });
|
|
222
|
-
const next = { path: [...selection.head.path], offset: selection.head.offset + event.data.length };
|
|
223
|
-
builder.setSelection({ type: "text", anchor: next, head: next });
|
|
224
|
-
builder.setStoredMarks([]);
|
|
225
|
-
}, { source: "input", timestamp: Date.now(), addToHistory: true });
|
|
226
|
-
renderOwners(root, owners, editor.document);
|
|
227
|
-
const next = { anchor: { ...snapshot.anchor, offset: snapshot.anchor.offset + event.data.length }, head: { ...snapshot.head, offset: snapshot.head.offset + event.data.length } };
|
|
228
|
-
restoreSelection(root, next);
|
|
229
|
-
ownerElements.forEach((owner) => { if (!ownersWithPersistedIds.has(owner))
|
|
230
|
-
owner.removeAttribute("data-smart-id"); });
|
|
231
|
-
storedMarks.set(root, [...(editor.storedMarks || [])]);
|
|
232
|
-
};
|
|
233
|
-
const selectionChange = () => {
|
|
234
|
-
const ownerElements = Array.from(root.querySelectorAll(ownerSelector));
|
|
235
|
-
const ownersWithPersistedIds = new Set(ownerElements.filter((owner) => owner.hasAttribute("data-smart-id")));
|
|
236
|
-
const snapshot = captureSelection(root);
|
|
237
|
-
if (!snapshot || snapshot.anchor.ownerId !== snapshot.head.ownerId || snapshot.anchor.offset !== snapshot.head.offset)
|
|
238
|
-
storedMarks.delete(root);
|
|
239
|
-
ownerElements.forEach((owner) => { if (!ownersWithPersistedIds.has(owner))
|
|
240
|
-
owner.removeAttribute("data-smart-id"); });
|
|
241
|
-
};
|
|
242
|
-
root.addEventListener("beforeinput", beforeInput);
|
|
243
|
-
root.ownerDocument.addEventListener("selectionchange", selectionChange);
|
|
244
|
-
return () => {
|
|
245
|
-
root.removeEventListener("beforeinput", beforeInput);
|
|
246
|
-
root.ownerDocument.removeEventListener("selectionchange", selectionChange);
|
|
247
|
-
storedMarks.delete(root);
|
|
248
|
-
};
|
|
249
|
-
};
|
|
250
|
-
export const canonicalInlineStoredMarks = (root) => storedMarks.get(root) || [];
|
|
251
|
-
export const hasCanonicalInlineStoredMarkOverride = (root) => storedMarks.has(root);
|
|
252
|
-
export const describeCanonicalInlineCoverage = (root) => {
|
|
253
|
-
if (storedMarks.has(root))
|
|
254
|
-
return Object.fromEntries((storedMarks.get(root) || []).map((mark) => [mark.type, "all"]));
|
|
255
|
-
const ownerElements = Array.from(root.querySelectorAll(ownerSelector));
|
|
256
|
-
const ownersWithPersistedIds = new Set(ownerElements.filter((owner) => owner.hasAttribute("data-smart-id")));
|
|
257
|
-
const snapshot = captureSelection(root);
|
|
258
|
-
if (!snapshot)
|
|
259
|
-
return {};
|
|
260
|
-
const owners = selectedOwners(root, snapshot);
|
|
261
|
-
const document = parseOwners(owners);
|
|
262
|
-
const selection = modelSelection(document, snapshot);
|
|
263
|
-
const description = selection
|
|
264
|
-
? createScopeIndex().resolve(document, selection, { want: "describe" }, foundationSchema)
|
|
265
|
-
: null;
|
|
266
|
-
ownerElements.forEach((owner) => { if (!ownersWithPersistedIds.has(owner))
|
|
267
|
-
owner.removeAttribute("data-smart-id"); });
|
|
268
|
-
return Object.fromEntries((description?.marks || []).map((entry) => [entry.mark.type, entry.coverage]));
|
|
269
|
-
};
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { type SmartSelection } from "smartrte-core";
|
|
2
|
-
export declare const executeCanonicalListDepth: (root: HTMLElement, items: readonly HTMLElement[], direction: "indent" | "outdent") => boolean;
|
|
3
|
-
export declare const executeCanonicalListMove: (root: HTMLElement, items: readonly HTMLElement[], direction: "up" | "down") => boolean;
|
|
4
|
-
export declare const executeCanonicalListStyle: (args: {
|
|
5
|
-
root: HTMLElement;
|
|
6
|
-
items: readonly HTMLElement[];
|
|
7
|
-
style?: string;
|
|
8
|
-
preset?: string;
|
|
9
|
-
checkable?: boolean;
|
|
10
|
-
}) => boolean;
|
|
11
|
-
export declare const executeCanonicalListCheck: (root: HTMLElement, item: HTMLElement, checked: boolean) => boolean;
|
|
12
|
-
export declare const executeCanonicalListToggle: (args: {
|
|
13
|
-
root: HTMLElement;
|
|
14
|
-
items: readonly HTMLElement[];
|
|
15
|
-
blocks: readonly HTMLElement[];
|
|
16
|
-
listTag: "ul" | "ol";
|
|
17
|
-
range?: Range;
|
|
18
|
-
style?: string;
|
|
19
|
-
preset?: string;
|
|
20
|
-
checkable?: boolean;
|
|
21
|
-
}) => boolean;
|
|
22
|
-
export interface CanonicalListBridgeResult {
|
|
23
|
-
readonly changed: boolean;
|
|
24
|
-
readonly selection: SmartSelection | null;
|
|
25
|
-
}
|
|
26
|
-
export declare const executeCanonicalListStructuralInput: (root: HTMLElement, input: "enter" | "backspace" | "delete") => boolean;
|