@slexkit/tiptap 0.4.0 → 0.5.2
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/README.md +45 -88
- package/dist/index.d.ts +2 -2
- package/dist/index.js +147 -44
- package/package.json +68 -68
package/README.md
CHANGED
|
@@ -1,88 +1,45 @@
|
|
|
1
|
-
# @slexkit/tiptap
|
|
2
|
-
|
|
3
|
-
Tiptap
|
|
4
|
-
|
|
5
|
-
```
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
Markdown,
|
|
47
|
-
createSlexKitTiptapExtension({ artifactId: "doc" }),
|
|
48
|
-
],
|
|
49
|
-
content: markdown,
|
|
50
|
-
contentType: "markdown",
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
const nextMarkdown = editor.getMarkdown();
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
The adapter uses normal fenced code blocks instead of a custom tokenizer, so ` ```slex ` blocks remain readable in plain Markdown hosts.
|
|
57
|
-
|
|
58
|
-
## Shared state across fences
|
|
59
|
-
|
|
60
|
-
All `slex` blocks in one editor share the same artifact runtime by default. A state-only fence can seed later renderable fences:
|
|
61
|
-
|
|
62
|
-
````md
|
|
63
|
-
```slex
|
|
64
|
-
{
|
|
65
|
-
namespace: "calc",
|
|
66
|
-
g: { value: 21 }
|
|
67
|
-
}
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
```slex
|
|
71
|
-
{
|
|
72
|
-
namespace: "calc",
|
|
73
|
-
layout: {
|
|
74
|
-
"text:answer": { "$text": "'answer: ' + (g.value * 2)" }
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
```
|
|
78
|
-
````
|
|
79
|
-
|
|
80
|
-
## Runtime boundary
|
|
81
|
-
|
|
82
|
-
This adapter defaults to trusted runtime mode, intended for application-authored content, repository-maintained snippets, reviewed examples, and local document workflows. Use SlexKit secure runtime APIs for untrusted user Markdown or direct agent output.
|
|
83
|
-
|
|
84
|
-
## Documentation
|
|
85
|
-
|
|
86
|
-
- [Official Tiptap editor example](https://slexkit.app.simgor.cn/examples/tiptap-host)
|
|
87
|
-
- [Runnable example source](https://github.com/slexkit/slexkit/tree/main/examples/tiptap)
|
|
88
|
-
- [SlexKit Host integration](https://github.com/slexkit/slexkit/blob/main/site/content/reference/integration/en-US.md)
|
|
1
|
+
# @slexkit/tiptap
|
|
2
|
+
|
|
3
|
+
Official Tiptap code-block node view for HTML-first `slex` fences.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import StarterKit from "@tiptap/starter-kit";
|
|
7
|
+
import { createSlexKitTiptapExtension } from "@slexkit/tiptap";
|
|
8
|
+
import { createSlexKitMarkdownRuntimeHost } from "slexkit";
|
|
9
|
+
|
|
10
|
+
const runtimeHost = createSlexKitMarkdownRuntimeHost({
|
|
11
|
+
secureFrame: { runtimeUrl: "/assets/slexkit.runtime.js" },
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const extensions = [
|
|
15
|
+
StarterKit.configure({ codeBlock: false }),
|
|
16
|
+
createSlexKitTiptapExtension({ runtimeHost }),
|
|
17
|
+
];
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The extension replaces only code blocks whose language is exactly `slex`.
|
|
21
|
+
Ordinary code blocks retain normal Tiptap behavior and Markdown round-tripping.
|
|
22
|
+
|
|
23
|
+
````md
|
|
24
|
+
~~~slex
|
|
25
|
+
<slex-input id="voltage" type="engineering" label="Voltage" value="12"></slex-input>
|
|
26
|
+
<slex-stat id="power" label="Power" unit="W"></slex-stat>
|
|
27
|
+
<script type="module">
|
|
28
|
+
const voltage = slex.root.querySelector("#voltage");
|
|
29
|
+
const power = slex.root.querySelector("#power");
|
|
30
|
+
const update = () => { power.value = voltage.valueAsNumber * 2; };
|
|
31
|
+
voltage.addEventListener("input", update);
|
|
32
|
+
update();
|
|
33
|
+
</script>
|
|
34
|
+
~~~
|
|
35
|
+
````
|
|
36
|
+
|
|
37
|
+
Each editor owns a stable artifact; each node view owns a stable block ID.
|
|
38
|
+
Updates use the runtime host's preview/commit protocol, so invalid source reports
|
|
39
|
+
a diagnostic without destroying the last committed iframe. The adapter never
|
|
40
|
+
interprets DSL objects, namespaces, state-only blocks, expression strings, or
|
|
41
|
+
handler strings.
|
|
42
|
+
|
|
43
|
+
Options include `artifactId`, `runtimeHost`, `theme`, `showSource`, labels,
|
|
44
|
+
`className`, and `onError`. Custom legacy hosts remain supported for complete
|
|
45
|
+
commits, though atomic preservation requires `updateArtifact`.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { Editor } from "@tiptap/core";
|
|
2
|
-
import { type SlexKitMarkdownRuntimeHost, type
|
|
2
|
+
import { type SlexKitMarkdownRuntimeHost, type HtmlTheme } from "slexkit";
|
|
3
3
|
export type SlexKitTiptapOptions = {
|
|
4
4
|
artifactId?: string | ((ctx: {
|
|
5
5
|
editor: Editor;
|
|
6
6
|
}) => string);
|
|
7
7
|
runtimeHost?: SlexKitMarkdownRuntimeHost;
|
|
8
|
-
theme?:
|
|
8
|
+
theme?: HtmlTheme;
|
|
9
9
|
showSource?: boolean;
|
|
10
10
|
sourceLabel?: string;
|
|
11
11
|
previewLabel?: string;
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import CodeBlock from "@tiptap/extension-code-block";
|
|
2
|
-
import { createSlexKitMarkdownRuntimeHost,
|
|
2
|
+
import { createSlexKitMarkdownRuntimeHost, inspectSlexSource, } from "slexkit";
|
|
3
3
|
const DEFAULT_OPTIONS = {
|
|
4
4
|
showSource: true,
|
|
5
5
|
sourceLabel: "Source",
|
|
@@ -41,22 +41,10 @@ const editorArtifactIds = new WeakMap();
|
|
|
41
41
|
let nextArtifactId = 0;
|
|
42
42
|
const sourceOpenStates = new Map();
|
|
43
43
|
const editorArtifactRuntimes = new WeakMap();
|
|
44
|
-
function
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (!isRecord(value))
|
|
49
|
-
return false;
|
|
50
|
-
if ("layout" in value)
|
|
51
|
-
return isRecord(value.layout) && Object.keys(value.layout).length > 0;
|
|
52
|
-
if ("namespace" in value || "g" in value)
|
|
53
|
-
return false;
|
|
54
|
-
return Object.keys(value).some((key) => key.includes(":"));
|
|
55
|
-
}
|
|
56
|
-
function isStateOnlySource(value) {
|
|
57
|
-
return isRecord(value)
|
|
58
|
-
&& !isRenderableSource(value)
|
|
59
|
-
&& ("slex" in value || "namespace" in value || "g" in value);
|
|
44
|
+
function isIncrementalRuntimeHost(host) {
|
|
45
|
+
const candidate = host;
|
|
46
|
+
return typeof candidate.updateArtifact === "function"
|
|
47
|
+
&& typeof candidate.resetArtifact === "function";
|
|
60
48
|
}
|
|
61
49
|
function resolveArtifactId(editor, option) {
|
|
62
50
|
if (typeof option === "function")
|
|
@@ -99,6 +87,8 @@ function resolveArtifactRuntime(editor, artifactId, runtimeHost) {
|
|
|
99
87
|
runtimeHost: runtimeHost ?? createSlexKitMarkdownRuntimeHost(),
|
|
100
88
|
views: new Set(),
|
|
101
89
|
scheduled: false,
|
|
90
|
+
pending: Promise.resolve(),
|
|
91
|
+
nextBlockId: 0,
|
|
102
92
|
};
|
|
103
93
|
runtimes.set(artifactId, runtime);
|
|
104
94
|
}
|
|
@@ -110,8 +100,36 @@ function scheduleArtifactRender(runtime) {
|
|
|
110
100
|
runtime.scheduled = true;
|
|
111
101
|
queueMicrotask(() => {
|
|
112
102
|
runtime.scheduled = false;
|
|
113
|
-
|
|
114
|
-
|
|
103
|
+
const views = Array.from(runtime.views).sort(compareNodeViewOrder);
|
|
104
|
+
if (isIncrementalRuntimeHost(runtime.runtimeHost)) {
|
|
105
|
+
const prepared = views.map((view) => view.prepareIncrementalRender());
|
|
106
|
+
const phase = prepared.every((entry) => entry.valid)
|
|
107
|
+
? "commit"
|
|
108
|
+
: "preview";
|
|
109
|
+
const host = runtime.runtimeHost;
|
|
110
|
+
const update = async () => {
|
|
111
|
+
if (runtime.disposed)
|
|
112
|
+
return;
|
|
113
|
+
try {
|
|
114
|
+
await host.updateArtifact(runtime.artifactId, prepared.map((entry) => entry.block), { phase });
|
|
115
|
+
if (phase === "commit") {
|
|
116
|
+
for (const entry of prepared) {
|
|
117
|
+
entry.view.finishIncrementalRender();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
if (phase === "commit") {
|
|
123
|
+
for (const entry of prepared) {
|
|
124
|
+
entry.view.reportIncrementalError(error);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
runtime.pending = runtime.pending.then(update, update);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
for (const view of views) {
|
|
115
133
|
view.renderWithRuntime(runtime.runtimeHost);
|
|
116
134
|
}
|
|
117
135
|
});
|
|
@@ -122,6 +140,7 @@ function unregisterArtifactRuntime(editor, runtime, view) {
|
|
|
122
140
|
scheduleArtifactRender(runtime);
|
|
123
141
|
return;
|
|
124
142
|
}
|
|
143
|
+
runtime.disposed = true;
|
|
125
144
|
runtime.runtimeHost.disposeArtifact(runtime.artifactId);
|
|
126
145
|
editorArtifactRuntimes.get(editor)?.delete(runtime.artifactId);
|
|
127
146
|
}
|
|
@@ -160,18 +179,38 @@ function markPreviewInteractive(preview, force = false) {
|
|
|
160
179
|
}
|
|
161
180
|
preview.querySelectorAll(PREVIEW_INTERACTIVE_SELECTOR).forEach((element) => {
|
|
162
181
|
if (element instanceof HTMLElement && (force || element.getAttribute("contenteditable") !== "true")) {
|
|
163
|
-
if (force)
|
|
182
|
+
if (force && !element.contains(preview.ownerDocument.activeElement))
|
|
164
183
|
element.removeAttribute("contenteditable");
|
|
165
184
|
element.setAttribute("contenteditable", "true");
|
|
166
185
|
}
|
|
167
186
|
});
|
|
168
187
|
}
|
|
188
|
+
const previewMarks = new WeakMap();
|
|
189
|
+
function cancelPreviewInteractiveMark(preview) {
|
|
190
|
+
const pending = previewMarks.get(preview);
|
|
191
|
+
if (!pending)
|
|
192
|
+
return;
|
|
193
|
+
pending.cancelled = true;
|
|
194
|
+
if (pending.timer !== undefined)
|
|
195
|
+
preview.ownerDocument.defaultView?.clearTimeout(pending.timer);
|
|
196
|
+
if (pending.frame !== undefined)
|
|
197
|
+
preview.ownerDocument.defaultView?.cancelAnimationFrame(pending.frame);
|
|
198
|
+
previewMarks.delete(preview);
|
|
199
|
+
}
|
|
169
200
|
function schedulePreviewInteractiveMark(preview) {
|
|
170
201
|
markPreviewInteractive(preview);
|
|
171
|
-
|
|
202
|
+
if (previewMarks.has(preview))
|
|
203
|
+
return;
|
|
204
|
+
const pending = { cancelled: false };
|
|
205
|
+
previewMarks.set(preview, pending);
|
|
206
|
+
const mark = () => { if (!pending.cancelled)
|
|
207
|
+
markPreviewInteractive(preview, true); };
|
|
208
|
+
queueMicrotask(mark);
|
|
172
209
|
const win = preview.ownerDocument.defaultView;
|
|
173
|
-
win?.setTimeout(() =>
|
|
174
|
-
|
|
210
|
+
pending.timer = win?.setTimeout(() => { pending.timer = undefined; mark(); if (pending.frame === undefined)
|
|
211
|
+
previewMarks.delete(preview); }, 0);
|
|
212
|
+
pending.frame = win?.requestAnimationFrame(() => { pending.frame = undefined; mark(); if (pending.timer === undefined)
|
|
213
|
+
previewMarks.delete(preview); });
|
|
175
214
|
}
|
|
176
215
|
function renderError(error, source, container) {
|
|
177
216
|
const doc = container.ownerDocument || document;
|
|
@@ -218,15 +257,35 @@ class SlexKitCodeBlockNodeView {
|
|
|
218
257
|
editor;
|
|
219
258
|
options;
|
|
220
259
|
preview;
|
|
260
|
+
diagnostic;
|
|
221
261
|
runtime;
|
|
262
|
+
blockId;
|
|
222
263
|
interactiveObserver;
|
|
223
264
|
pendingFocusRestore;
|
|
224
265
|
pointerFocusContext;
|
|
266
|
+
focusTimers = new Set();
|
|
267
|
+
focusFrames = new Set();
|
|
268
|
+
clearKeyboardPointerIntent = () => {
|
|
269
|
+
this.pointerFocusContext = undefined;
|
|
270
|
+
this.pendingFocusRestore = undefined;
|
|
271
|
+
this.removeFocusRestoreListeners();
|
|
272
|
+
this.cancelFocusWork();
|
|
273
|
+
};
|
|
274
|
+
cancelFocusWork() {
|
|
275
|
+
const win = this.preview.ownerDocument.defaultView;
|
|
276
|
+
for (const timer of this.focusTimers)
|
|
277
|
+
win?.clearTimeout(timer);
|
|
278
|
+
for (const frame of this.focusFrames)
|
|
279
|
+
win?.cancelAnimationFrame(frame);
|
|
280
|
+
this.focusTimers.clear();
|
|
281
|
+
this.focusFrames.clear();
|
|
282
|
+
}
|
|
225
283
|
renderToken = 0;
|
|
226
284
|
focusPreviewPointerTarget = (event) => {
|
|
227
285
|
const restoreEditor = this.editor.isFocused;
|
|
228
|
-
this.pointerFocusContext = { restoreEditor, expiresAt: Date.now() + 500 };
|
|
229
286
|
const interactive = focusPreviewInteractiveTarget(event);
|
|
287
|
+
this.pointerFocusContext = { restoreEditor, expiresAt: Date.now() + 500,
|
|
288
|
+
scope: interactive?.closest(".slex-select,.slex-checkbox,.slex-switch,.slex-tabs") ?? interactive ?? undefined };
|
|
230
289
|
if (!interactive || !shouldRestorePreviewFocusAfterPointer(interactive))
|
|
231
290
|
return;
|
|
232
291
|
this.pendingFocusRestore = {
|
|
@@ -245,6 +304,8 @@ class SlexKitCodeBlockNodeView {
|
|
|
245
304
|
const interactive = targetElement?.closest(TRANSIENT_POINTER_INTERACTIVE_SELECTOR);
|
|
246
305
|
if (!(interactive instanceof HTMLElement))
|
|
247
306
|
return;
|
|
307
|
+
if (context.scope && !context.scope.contains(interactive))
|
|
308
|
+
return;
|
|
248
309
|
if (this.pendingFocusRestore?.target === interactive)
|
|
249
310
|
return;
|
|
250
311
|
this.queuePreviewFocusCleanup({
|
|
@@ -284,9 +345,14 @@ class SlexKitCodeBlockNodeView {
|
|
|
284
345
|
this.editor.commands.focus(undefined, { scrollIntoView: false });
|
|
285
346
|
}
|
|
286
347
|
};
|
|
287
|
-
win
|
|
288
|
-
|
|
289
|
-
|
|
348
|
+
if (!win)
|
|
349
|
+
return;
|
|
350
|
+
for (const ms of [0, 50]) {
|
|
351
|
+
const timer = win.setTimeout(() => { this.focusTimers.delete(timer); cleanupFocus(); }, ms);
|
|
352
|
+
this.focusTimers.add(timer);
|
|
353
|
+
}
|
|
354
|
+
const frame = win.requestAnimationFrame(() => { this.focusFrames.delete(frame); cleanupFocus(); });
|
|
355
|
+
this.focusFrames.add(frame);
|
|
290
356
|
}
|
|
291
357
|
cancelPreviewPointerFocus = (event) => {
|
|
292
358
|
this.finishPreviewPointerFocus(event);
|
|
@@ -297,6 +363,7 @@ class SlexKitCodeBlockNodeView {
|
|
|
297
363
|
this.editor = props.editor;
|
|
298
364
|
this.artifactId = resolveArtifactId(props.editor, options.artifactId);
|
|
299
365
|
this.runtime = resolveArtifactRuntime(props.editor, this.artifactId, options.runtimeHost);
|
|
366
|
+
this.blockId = `tiptap-block-${++this.runtime.nextBlockId}`;
|
|
300
367
|
this.runtime.views.add(this);
|
|
301
368
|
const doc = props.view.dom.ownerDocument || document;
|
|
302
369
|
this.dom = createElement(doc, "div", ["slex-tiptap-block", options.className].filter(Boolean).join(" "));
|
|
@@ -306,7 +373,9 @@ class SlexKitCodeBlockNodeView {
|
|
|
306
373
|
if (label)
|
|
307
374
|
label.textContent = options.previewLabel;
|
|
308
375
|
this.preview = createElement(doc, "div", "slex-tiptap-preview");
|
|
376
|
+
this.diagnostic = createElement(doc, "div", "slex-tiptap-diagnostic");
|
|
309
377
|
this.preview.setAttribute("contenteditable", "false");
|
|
378
|
+
doc.addEventListener("keydown", this.clearKeyboardPointerIntent, { capture: true });
|
|
310
379
|
this.preview.addEventListener("pointerdown", this.focusPreviewPointerTarget, { capture: true });
|
|
311
380
|
this.preview.addEventListener("focusin", this.clearPointerFocusedPreviewTarget, { capture: true });
|
|
312
381
|
this.preview.addEventListener("mousedown", focusPreviewInteractiveTarget, { capture: true });
|
|
@@ -356,13 +425,13 @@ class SlexKitCodeBlockNodeView {
|
|
|
356
425
|
sourceToggle.addEventListener("click", stopSourceClick, { capture: true });
|
|
357
426
|
setSourceOpen(sourceOpen);
|
|
358
427
|
sourceShell.append(sourceToggle, pre);
|
|
359
|
-
this.dom.append(...[label, this.preview, sourceShell].filter((element) => !!element));
|
|
428
|
+
this.dom.append(...[label, this.diagnostic, this.preview, sourceShell].filter((element) => !!element));
|
|
360
429
|
}
|
|
361
430
|
else {
|
|
362
431
|
const sourceShell = createElement(doc, "div", "slex-tiptap-source slex-tiptap-source-hidden");
|
|
363
432
|
sourceShell.hidden = true;
|
|
364
433
|
sourceShell.append(pre);
|
|
365
|
-
this.dom.append(...[label, this.preview, sourceShell].filter((element) => !!element));
|
|
434
|
+
this.dom.append(...[label, this.diagnostic, this.preview, sourceShell].filter((element) => !!element));
|
|
366
435
|
}
|
|
367
436
|
scheduleArtifactRender(this.runtime);
|
|
368
437
|
}
|
|
@@ -374,8 +443,15 @@ class SlexKitCodeBlockNodeView {
|
|
|
374
443
|
return true;
|
|
375
444
|
}
|
|
376
445
|
destroy() {
|
|
446
|
+
this.renderToken += 1;
|
|
447
|
+
this.cancelFocusWork();
|
|
448
|
+
this.preview.ownerDocument.removeEventListener("keydown", this.clearKeyboardPointerIntent, { capture: true });
|
|
449
|
+
cancelPreviewInteractiveMark(this.preview);
|
|
377
450
|
this.interactiveObserver?.disconnect();
|
|
378
451
|
this.removeFocusRestoreListeners();
|
|
452
|
+
this.preview.removeEventListener("pointerdown", this.focusPreviewPointerTarget, { capture: true });
|
|
453
|
+
this.preview.removeEventListener("mousedown", focusPreviewInteractiveTarget, { capture: true });
|
|
454
|
+
this.preview.removeEventListener("click", focusPreviewInteractiveTarget, { capture: true });
|
|
379
455
|
this.preview.removeEventListener("focusin", this.clearPointerFocusedPreviewTarget, { capture: true });
|
|
380
456
|
this.preview.replaceChildren();
|
|
381
457
|
unregisterArtifactRuntime(this.editor, this.runtime, this);
|
|
@@ -396,6 +472,8 @@ class SlexKitCodeBlockNodeView {
|
|
|
396
472
|
const target = mutation.target;
|
|
397
473
|
if (this.preview.contains(target))
|
|
398
474
|
return true;
|
|
475
|
+
if (this.diagnostic.contains(target))
|
|
476
|
+
return true;
|
|
399
477
|
if (target === this.contentDOM || this.contentDOM.contains(target))
|
|
400
478
|
return false;
|
|
401
479
|
const targetElement = target instanceof Element ? target : target.parentElement;
|
|
@@ -430,37 +508,62 @@ class SlexKitCodeBlockNodeView {
|
|
|
430
508
|
const source = this.source();
|
|
431
509
|
this.dom.hidden = false;
|
|
432
510
|
this.preview.hidden = false;
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
this.options.onError?.(parsed.error, source);
|
|
511
|
+
const inspected = inspectSlexSource(source);
|
|
512
|
+
if (!inspected.ok) {
|
|
513
|
+
renderError(inspected.error, source, this.diagnostic);
|
|
514
|
+
this.options.onError?.(inspected.error, source);
|
|
438
515
|
return;
|
|
439
516
|
}
|
|
440
517
|
try {
|
|
518
|
+
runtimeHost.disposeBlock(this.preview);
|
|
441
519
|
runtimeHost.mountBlock({
|
|
442
520
|
artifactId: this.artifactId,
|
|
521
|
+
blockId: this.blockId,
|
|
443
522
|
source,
|
|
444
523
|
container: this.preview,
|
|
445
524
|
theme: this.options.theme,
|
|
446
525
|
});
|
|
526
|
+
this.diagnostic.replaceChildren();
|
|
447
527
|
schedulePreviewInteractiveMark(this.preview);
|
|
448
528
|
}
|
|
449
529
|
catch (error) {
|
|
450
|
-
renderError(error, source, this.
|
|
530
|
+
renderError(error, source, this.diagnostic);
|
|
451
531
|
this.options.onError?.(error, source);
|
|
452
532
|
return;
|
|
453
533
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
this.
|
|
534
|
+
}
|
|
535
|
+
prepareIncrementalRender() {
|
|
536
|
+
const source = this.source();
|
|
537
|
+
const inspected = inspectSlexSource(source);
|
|
538
|
+
if (inspected.ok) {
|
|
539
|
+
this.diagnostic.replaceChildren();
|
|
540
|
+
}
|
|
541
|
+
else {
|
|
542
|
+
renderError(inspected.error, source, this.diagnostic);
|
|
543
|
+
this.options.onError?.(inspected.error, source);
|
|
463
544
|
}
|
|
545
|
+
return {
|
|
546
|
+
view: this,
|
|
547
|
+
block: {
|
|
548
|
+
artifactId: this.artifactId,
|
|
549
|
+
blockId: this.blockId,
|
|
550
|
+
source,
|
|
551
|
+
container: this.preview,
|
|
552
|
+
theme: this.options.theme,
|
|
553
|
+
},
|
|
554
|
+
valid: inspected.ok,
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
finishIncrementalRender() {
|
|
558
|
+
this.dom.hidden = false;
|
|
559
|
+
this.preview.hidden = false;
|
|
560
|
+
this.diagnostic.replaceChildren();
|
|
561
|
+
schedulePreviewInteractiveMark(this.preview);
|
|
562
|
+
}
|
|
563
|
+
reportIncrementalError(error) {
|
|
564
|
+
const source = this.source();
|
|
565
|
+
renderError(error, source, this.diagnostic);
|
|
566
|
+
this.options.onError?.(error, source);
|
|
464
567
|
}
|
|
465
568
|
}
|
|
466
569
|
export function createSlexKitTiptapExtension(options = {}) {
|
package/package.json
CHANGED
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@slexkit/tiptap",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Tiptap extension for SlexKit fenced UI blocks.",
|
|
5
|
-
"author": "SlexKit contributors",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"repository": {
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/slexkit/slexkit.git",
|
|
10
|
-
"directory": "packages/tiptap"
|
|
11
|
-
},
|
|
12
|
-
"homepage": "https://github.com/slexkit/slexkit/tree/main/packages/tiptap#readme",
|
|
13
|
-
"bugs": {
|
|
14
|
-
"url": "https://github.com/slexkit/slexkit/issues"
|
|
15
|
-
},
|
|
16
|
-
"main": "./dist/index.js",
|
|
17
|
-
"module": "./dist/index.js",
|
|
18
|
-
"types": "./dist/index.d.ts",
|
|
19
|
-
"exports": {
|
|
20
|
-
".": {
|
|
21
|
-
"types": "./dist/index.d.ts",
|
|
22
|
-
"import": "./dist/index.js"
|
|
23
|
-
},
|
|
24
|
-
"./style.css": "./style.css"
|
|
25
|
-
},
|
|
26
|
-
"files": [
|
|
27
|
-
"dist/",
|
|
28
|
-
"style.css",
|
|
29
|
-
"README.md",
|
|
30
|
-
"LICENSE"
|
|
31
|
-
],
|
|
32
|
-
"scripts": {
|
|
33
|
-
"build": "bun run scripts/build.ts"
|
|
34
|
-
},
|
|
35
|
-
"keywords": [
|
|
36
|
-
"slexkit",
|
|
37
|
-
"tiptap",
|
|
38
|
-
"prosemirror",
|
|
39
|
-
"markdown",
|
|
40
|
-
"editor"
|
|
41
|
-
],
|
|
42
|
-
"license": "MIT",
|
|
43
|
-
"publishConfig": {
|
|
44
|
-
"access": "public"
|
|
45
|
-
},
|
|
46
|
-
"peerDependencies": {
|
|
47
|
-
"@tiptap/core": "^3.0.0",
|
|
48
|
-
"@tiptap/extension-code-block": "^3.0.0",
|
|
49
|
-
"@tiptap/markdown": "^3.0.0",
|
|
50
|
-
"@tiptap/pm": "^3.0.0",
|
|
51
|
-
"slexkit": "^0.
|
|
52
|
-
},
|
|
53
|
-
"peerDependenciesMeta": {
|
|
54
|
-
"slexkit": {
|
|
55
|
-
"optional": true
|
|
56
|
-
},
|
|
57
|
-
"@tiptap/markdown": {
|
|
58
|
-
"optional": true
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
|
-
"devDependencies": {
|
|
62
|
-
"@tiptap/core": "^3.27.1",
|
|
63
|
-
"@tiptap/extension-code-block": "^3.27.1",
|
|
64
|
-
"@tiptap/markdown": "^3.27.1",
|
|
65
|
-
"@tiptap/pm": "^3.27.1",
|
|
66
|
-
"@tiptap/starter-kit": "^3.27.1"
|
|
67
|
-
}
|
|
68
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@slexkit/tiptap",
|
|
3
|
+
"version": "0.5.2",
|
|
4
|
+
"description": "Tiptap extension for SlexKit fenced UI blocks.",
|
|
5
|
+
"author": "SlexKit contributors",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/slexkit/slexkit.git",
|
|
10
|
+
"directory": "packages/tiptap"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/slexkit/slexkit/tree/main/packages/tiptap#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/slexkit/slexkit/issues"
|
|
15
|
+
},
|
|
16
|
+
"main": "./dist/index.js",
|
|
17
|
+
"module": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"import": "./dist/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./style.css": "./style.css"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist/",
|
|
28
|
+
"style.css",
|
|
29
|
+
"README.md",
|
|
30
|
+
"LICENSE"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "bun run scripts/build.ts"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"slexkit",
|
|
37
|
+
"tiptap",
|
|
38
|
+
"prosemirror",
|
|
39
|
+
"markdown",
|
|
40
|
+
"editor"
|
|
41
|
+
],
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"@tiptap/core": "^3.0.0",
|
|
48
|
+
"@tiptap/extension-code-block": "^3.0.0",
|
|
49
|
+
"@tiptap/markdown": "^3.0.0",
|
|
50
|
+
"@tiptap/pm": "^3.0.0",
|
|
51
|
+
"slexkit": "^0.5.2"
|
|
52
|
+
},
|
|
53
|
+
"peerDependenciesMeta": {
|
|
54
|
+
"slexkit": {
|
|
55
|
+
"optional": true
|
|
56
|
+
},
|
|
57
|
+
"@tiptap/markdown": {
|
|
58
|
+
"optional": true
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@tiptap/core": "^3.27.1",
|
|
63
|
+
"@tiptap/extension-code-block": "^3.27.1",
|
|
64
|
+
"@tiptap/markdown": "^3.27.1",
|
|
65
|
+
"@tiptap/pm": "^3.27.1",
|
|
66
|
+
"@tiptap/starter-kit": "^3.27.1"
|
|
67
|
+
}
|
|
68
|
+
}
|