@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 CHANGED
@@ -1,88 +1,45 @@
1
- # @slexkit/tiptap
2
-
3
- Tiptap extension for explicit `slex` fenced UI blocks.
4
-
5
- ```sh
6
- npm install slexkit @slexkit/theme-shadcn @slexkit/tiptap @tiptap/core @tiptap/pm @tiptap/starter-kit @tiptap/extension-code-block @tiptap/markdown
7
- ```
8
-
9
- Import the SlexKit theme once in the app entry:
10
-
11
- ```ts
12
- import "@slexkit/theme-shadcn/style.css";
13
- import "@slexkit/tiptap/style.css";
14
- ```
15
-
16
- ## Minimal setup
17
-
18
- ```ts
19
- import { Editor } from "@tiptap/core";
20
- import StarterKit from "@tiptap/starter-kit";
21
- import { createSlexKitTiptapExtension } from "@slexkit/tiptap";
22
-
23
- const editor = new Editor({
24
- element: document.querySelector("#editor"),
25
- extensions: [
26
- StarterKit.configure({ codeBlock: false }),
27
- createSlexKitTiptapExtension({
28
- artifactId: "note-42",
29
- }),
30
- ],
31
- });
32
- ```
33
-
34
- Only code blocks whose language is exactly `slex` are rendered by SlexKit. Other code blocks stay ordinary Tiptap code blocks.
35
-
36
- ## Markdown roundtrip
37
-
38
- Add Tiptap's Markdown extension when loading or exporting Markdown:
39
-
40
- ```ts
41
- import { Markdown } from "@tiptap/markdown";
42
-
43
- const editor = new Editor({
44
- extensions: [
45
- StarterKit.configure({ codeBlock: false }),
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 ThemeMode } from "slexkit";
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?: ThemeMode;
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, parseSlexSource, } from "slexkit";
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 isRecord(value) {
45
- return !!value && typeof value === "object" && !Array.isArray(value);
46
- }
47
- function isRenderableSource(value) {
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
- runtime.runtimeHost.disposeArtifact(runtime.artifactId);
114
- for (const view of Array.from(runtime.views).sort(compareNodeViewOrder)) {
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
- queueMicrotask(() => markPreviewInteractive(preview, true));
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(() => markPreviewInteractive(preview, true), 0);
174
- win?.requestAnimationFrame(() => markPreviewInteractive(preview, true));
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?.setTimeout(cleanupFocus, 0);
288
- win?.requestAnimationFrame(cleanupFocus);
289
- win?.setTimeout(cleanupFocus, 50);
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
- this.preview.replaceChildren();
434
- const parsed = parseSlexSource(source);
435
- if (!parsed.ok) {
436
- renderError(parsed.error, source, this.preview);
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.preview);
530
+ renderError(error, source, this.diagnostic);
451
531
  this.options.onError?.(error, source);
452
532
  return;
453
533
  }
454
- if (isStateOnlySource(parsed.value) && !this.preview.querySelector(".slexkit-root")) {
455
- if (!this.options.showSource) {
456
- this.dom.hidden = true;
457
- this.preview.hidden = true;
458
- return;
459
- }
460
- const placeholder = createElement(this.preview.ownerDocument || document, "div", "slex-tiptap-state");
461
- placeholder.textContent = "State block";
462
- this.preview.append(placeholder);
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.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.4.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
+ }