@pilotiq/tiptap 3.2.0 → 3.3.0

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.
@@ -0,0 +1,354 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useRef, useState } from 'react';
3
+ import { useEditor, EditorContent } from '@tiptap/react';
4
+ import StarterKit from '@tiptap/starter-kit';
5
+ import Placeholder from '@tiptap/extension-placeholder';
6
+ import Image from '@tiptap/extension-image';
7
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
+ import { Markdown } from 'tiptap-markdown';
9
+ import { useCollabRoom, getCollabExtensions, } from '@pilotiq/pilotiq/react';
10
+ // Inline lucide.dev SVGs — same posture as `toolbarButtons.tsx` so this
11
+ // package doesn't pull `lucide-react` as a peer dep. Keep stroke / size
12
+ // consistent with the rich-text toolbar.
13
+ const ICON_PROPS = {
14
+ width: 14, height: 14, viewBox: '0 0 24 24',
15
+ fill: 'none', stroke: 'currentColor',
16
+ strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round',
17
+ 'aria-hidden': 'true',
18
+ };
19
+ const Spinner = (_jsx("svg", { ...ICON_PROPS, className: "animate-spin", children: _jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) }));
20
+ const SvgIcons = {
21
+ bold: (_jsxs("svg", { ...ICON_PROPS, strokeWidth: 2.25, children: [_jsx("path", { d: "M6 12h9a4 4 0 0 1 0 8H6Z" }), _jsx("path", { d: "M6 4h7a4 4 0 0 1 0 8H6Z" })] })),
22
+ italic: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("line", { x1: "19", y1: "4", x2: "10", y2: "4" }), _jsx("line", { x1: "14", y1: "20", x2: "5", y2: "20" }), _jsx("line", { x1: "15", y1: "4", x2: "9", y2: "20" })] })),
23
+ strike: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "M16 4H9a3 3 0 0 0-2.83 4" }), _jsx("path", { d: "M14 12a4 4 0 0 1 0 8H6" }), _jsx("line", { x1: "4", y1: "12", x2: "20", y2: "12" })] })),
24
+ link: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }), _jsx("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.71" })] })),
25
+ heading: _jsx("span", { className: "text-xs font-semibold leading-none", children: "H2" }),
26
+ bulletList: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("line", { x1: "8", y1: "6", x2: "21", y2: "6" }), _jsx("line", { x1: "8", y1: "12", x2: "21", y2: "12" }), _jsx("line", { x1: "8", y1: "18", x2: "21", y2: "18" }), _jsx("circle", { cx: "4", cy: "6", r: "1" }), _jsx("circle", { cx: "4", cy: "12", r: "1" }), _jsx("circle", { cx: "4", cy: "18", r: "1" })] })),
27
+ orderedList: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("line", { x1: "10", y1: "6", x2: "21", y2: "6" }), _jsx("line", { x1: "10", y1: "12", x2: "21", y2: "12" }), _jsx("line", { x1: "10", y1: "18", x2: "21", y2: "18" }), _jsx("path", { d: "M4 6h1v4" }), _jsx("path", { d: "M4 10h2" }), _jsx("path", { d: "M6 18H4c0-1 2-2 2-3s-1-1.5-2-1" })] })),
28
+ blockquote: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "M3 21c3 0 7-1 7-8V5a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h3" }), _jsx("path", { d: "M15 21c3 0 7-1 7-8V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h3" })] })),
29
+ codeBlock: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("polyline", { points: "16 18 22 12 16 6" }), _jsx("polyline", { points: "8 6 2 12 8 18" })] })),
30
+ attachFiles: (_jsx("svg", { ...ICON_PROPS, children: _jsx("path", { d: "m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48" }) })),
31
+ pencil: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "M12 20h9" }), _jsx("path", { d: "M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4Z" })] })),
32
+ source: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }), _jsx("polyline", { points: "14 2 14 8 20 8" }), _jsx("line", { x1: "9", y1: "13", x2: "15", y2: "13" }), _jsx("line", { x1: "9", y1: "17", x2: "15", y2: "17" })] })),
33
+ eye: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8Z" }), _jsx("circle", { cx: "12", cy: "12", r: "3" })] })),
34
+ };
35
+ /**
36
+ * Plug-in WYSIWYG markdown editor registered with pilotiq core's
37
+ * `registerMarkdownEditor()`. Replaces the legacy textarea + manual-toolbar
38
+ * UI with a real rich editor; serializes to markdown on every change via
39
+ * `tiptap-markdown` so the wire format stays a plain markdown string under
40
+ * the field name.
41
+ *
42
+ * Collab-aware: when a `<RecordCollabRoom>` is mounted up-tree AND
43
+ * `registerCollabExtensions()` ran (both shipped by `@pilotiq-pro/collab`),
44
+ * the editor binds to the room's shared `Y.XmlFragment` via Tiptap's
45
+ * `Collaboration` extension. Every peer mounts the same editor against the
46
+ * same fragment; markdown serialization runs locally per peer so only the
47
+ * ProseMirror tree crosses the wire.
48
+ *
49
+ * Tabs (top-right):
50
+ * - **Editor** (default) — WYSIWYG.
51
+ * - **Source** — raw markdown textarea; on switch back to Editor the editor
52
+ * parses the textarea contents (round-trips through tiptap-markdown).
53
+ * - **Preview** — read-only render of the current markdown via the editor's
54
+ * own HTML output. Same view a user would see on the public site if the
55
+ * resource ships a read-side renderer.
56
+ *
57
+ * Single-source-of-truth posture: the editor's `onUpdate` is the canonical
58
+ * write path. Source-tab edits flow back through the editor on tab-switch
59
+ * (no dual state, no drift between source and editor doc).
60
+ */
61
+ export function MarkdownEditor({ name, defaultValue, placeholder, disabled = false, onChange, onBlur, toolbarButtons, minHeight, maxHeight, fileAttachmentsDirectory, fileAttachmentsVisibility, uploadUrl, }) {
62
+ const room = useCollabRoom();
63
+ const factory = getCollabExtensions();
64
+ const collabActive = !!(room && factory);
65
+ const [tab, setTab] = useState('editor');
66
+ const [sourceDraft, setSourceDraft] = useState(defaultValue);
67
+ const [uploading, setUploading] = useState(false);
68
+ const fileInputRef = useRef(null);
69
+ // Collab extension factory output. Built once per editor mount (the
70
+ // factory closes over the room's ydoc + provider + field name); keyed
71
+ // remount below ensures we never swap it underneath the running editor.
72
+ const collabExtensions = useMemo(() => {
73
+ if (!collabActive || !room || !factory)
74
+ return [];
75
+ return factory({
76
+ ydoc: room.ydoc,
77
+ provider: room.provider,
78
+ fieldName: name,
79
+ ...(room.user ? { user: room.user } : {}),
80
+ });
81
+ // eslint-disable-next-line react-hooks/exhaustive-deps
82
+ }, [collabActive]);
83
+ const editor = useEditor({
84
+ editable: !disabled,
85
+ extensions: [
86
+ StarterKit.configure({
87
+ link: { openOnClick: false, autolink: true },
88
+ // Collaboration brings its own Yjs-backed history — disable
89
+ // StarterKit's local undoRedo when collab is active (else Tiptap
90
+ // logs a "not compatible with @tiptap/extension-undo-redo" warning).
91
+ ...(collabActive ? { undoRedo: false } : {}),
92
+ }),
93
+ // Markdown round-trip — parses `content` (when non-collab) and
94
+ // exposes `editor.storage.markdown.getMarkdown()`. We pass `html:
95
+ // false` because the wire format is markdown only.
96
+ Markdown.configure({
97
+ html: false,
98
+ tightLists: true,
99
+ breaks: false,
100
+ linkify: true,
101
+ transformPastedText: true,
102
+ transformCopiedText: true,
103
+ }),
104
+ Image.configure({ inline: false, allowBase64: false }),
105
+ Placeholder.configure({ placeholder: placeholder ?? 'Write in markdown…' }),
106
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
107
+ ...collabExtensions,
108
+ ],
109
+ // Collab takes ownership of the document — passing `content` would
110
+ // race the Y.XmlFragment sync. Seed after first connect (effect below).
111
+ content: collabActive ? '' : defaultValue,
112
+ onUpdate({ editor }) {
113
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
114
+ const storage = editor.storage.markdown;
115
+ const md = typeof storage?.getMarkdown === 'function' ? storage.getMarkdown() : '';
116
+ onChange(md);
117
+ },
118
+ onBlur() { onBlur?.(); },
119
+ },
120
+ // Re-mount when collab toggles. Other props (name, placeholder) are
121
+ // stable per mount — the field renderer doesn't swap them at runtime.
122
+ [collabActive]);
123
+ useEffect(() => {
124
+ if (!editor)
125
+ return;
126
+ editor.setEditable(!disabled && tab === 'editor');
127
+ }, [editor, disabled, tab]);
128
+ // First-load seed for collab. Collaboration starts the editor empty
129
+ // regardless of `content`; once the provider syncs from the server we
130
+ // check whether the field's `Y.XmlFragment` was ever written. Empty +
131
+ // we have an initial value = first session for this record. Mirrors
132
+ // the rich-text TiptapEditor seed path and the CollabTextRenderer seed.
133
+ const [hasSeeded, setHasSeeded] = useState(false);
134
+ useEffect(() => {
135
+ if (!editor || !collabActive || !room || hasSeeded)
136
+ return;
137
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
138
+ const ydoc = room.ydoc;
139
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
140
+ const provider = room.provider;
141
+ if (!ydoc || !provider)
142
+ return;
143
+ const trySeed = () => {
144
+ try {
145
+ const fragment = ydoc.getXmlFragment(name);
146
+ if (fragment && fragment.length === 0 && defaultValue) {
147
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
148
+ const cmd = editor.commands.setContent;
149
+ if (cmd)
150
+ cmd(defaultValue);
151
+ }
152
+ setHasSeeded(true);
153
+ }
154
+ catch {
155
+ setHasSeeded(true);
156
+ }
157
+ };
158
+ if (provider.synced) {
159
+ trySeed();
160
+ return;
161
+ }
162
+ provider.once('synced', trySeed);
163
+ return () => {
164
+ try {
165
+ provider.off?.('synced', trySeed);
166
+ }
167
+ catch { /* ignore */ }
168
+ };
169
+ // eslint-disable-next-line react-hooks/exhaustive-deps
170
+ }, [editor, collabActive, room]);
171
+ // Source-tab → Editor: parse the textarea back into the editor (this also
172
+ // emits onChange via the editor's onUpdate). One-way during the same flip.
173
+ const enterEditorTab = () => {
174
+ if (tab === 'source' && editor) {
175
+ try {
176
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
177
+ ;
178
+ editor.commands.setContent(sourceDraft);
179
+ }
180
+ catch { /* ignore parse errors */ }
181
+ }
182
+ setTab('editor');
183
+ };
184
+ const enterSourceTab = () => {
185
+ if (editor) {
186
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
187
+ const storage = editor.storage.markdown;
188
+ const md = typeof storage?.getMarkdown === 'function' ? storage.getMarkdown() : '';
189
+ setSourceDraft(md);
190
+ }
191
+ setTab('source');
192
+ };
193
+ const enterPreviewTab = () => {
194
+ setTab('preview');
195
+ };
196
+ // Live preview HTML is the editor's own HTML output — same renderer that
197
+ // produced what the user sees in the Editor tab. Read-only.
198
+ const previewHtml = useMemo(() => {
199
+ if (tab !== 'preview' || !editor)
200
+ return '';
201
+ try {
202
+ return editor.getHTML();
203
+ }
204
+ catch {
205
+ return '';
206
+ }
207
+ }, [tab, editor]);
208
+ const uploadAndInsert = async (file) => {
209
+ if (!uploadUrl || !editor)
210
+ return;
211
+ setUploading(true);
212
+ try {
213
+ const fd = new FormData();
214
+ fd.append('file', file);
215
+ if (fileAttachmentsDirectory)
216
+ fd.append('directory', fileAttachmentsDirectory);
217
+ if (fileAttachmentsVisibility)
218
+ fd.append('visibility', fileAttachmentsVisibility);
219
+ fd.append('fieldName', name);
220
+ const res = await fetch(uploadUrl, { method: 'POST', body: fd, headers: { Accept: 'application/json' } });
221
+ const data = await res.json().catch(() => ({}));
222
+ if (!res.ok || !data.ok || !data.url)
223
+ return;
224
+ const isImage = file.type.startsWith('image/');
225
+ if (isImage) {
226
+ editor.chain().focus().setImage({ src: data.url, alt: file.name }).run();
227
+ }
228
+ else {
229
+ editor.chain().focus().insertContent(`[${file.name}](${data.url})`).run();
230
+ }
231
+ }
232
+ finally {
233
+ setUploading(false);
234
+ }
235
+ };
236
+ const onAttachClick = () => {
237
+ const el = fileInputRef.current;
238
+ if (el)
239
+ el.click();
240
+ };
241
+ const onFilePicked = (e) => {
242
+ const file = e.target.files?.[0];
243
+ if (file)
244
+ void uploadAndInsert(file);
245
+ e.target.value = '';
246
+ };
247
+ // Toolbar item resolution. The pilotiq-side ids map onto Tiptap commands.
248
+ // attachFiles is gated on a configured uploadUrl (server strips it server-
249
+ // side when no adapter is registered, but defensive double-gate here too).
250
+ const allow = useMemo(() => new Set(toolbarButtons), [toolbarButtons]);
251
+ const canAttach = allow.has('attachFiles') && !!uploadUrl;
252
+ const exec = (id) => {
253
+ if (!editor)
254
+ return;
255
+ const c = editor.chain().focus();
256
+ switch (id) {
257
+ case 'bold':
258
+ c.toggleBold().run();
259
+ break;
260
+ case 'italic':
261
+ c.toggleItalic().run();
262
+ break;
263
+ case 'strike':
264
+ c.toggleStrike().run();
265
+ break;
266
+ case 'link': {
267
+ const prev = editor.getAttributes('link').href;
268
+ const url = window.prompt('URL', prev ?? '') ?? '';
269
+ if (url === '')
270
+ c.unsetLink().run();
271
+ else
272
+ c.extendMarkRange('link').setLink({ href: url }).run();
273
+ break;
274
+ }
275
+ case 'heading':
276
+ c.toggleHeading({ level: 2 }).run();
277
+ break;
278
+ case 'bulletList':
279
+ c.toggleBulletList().run();
280
+ break;
281
+ case 'orderedList':
282
+ c.toggleOrderedList().run();
283
+ break;
284
+ case 'blockquote':
285
+ c.toggleBlockquote().run();
286
+ break;
287
+ case 'codeBlock':
288
+ c.toggleCodeBlock().run();
289
+ break;
290
+ case 'attachFiles':
291
+ onAttachClick();
292
+ break;
293
+ default: /* unknown id — skip */ break;
294
+ }
295
+ };
296
+ const isActive = (id) => {
297
+ if (!editor)
298
+ return false;
299
+ switch (id) {
300
+ case 'bold': return editor.isActive('bold');
301
+ case 'italic': return editor.isActive('italic');
302
+ case 'strike': return editor.isActive('strike');
303
+ case 'link': return editor.isActive('link');
304
+ case 'heading': return editor.isActive('heading', { level: 2 });
305
+ case 'bulletList': return editor.isActive('bulletList');
306
+ case 'orderedList': return editor.isActive('orderedList');
307
+ case 'blockquote': return editor.isActive('blockquote');
308
+ case 'codeBlock': return editor.isActive('codeBlock');
309
+ default: return false;
310
+ }
311
+ };
312
+ const labels = {
313
+ bold: 'Bold (⌘B)',
314
+ italic: 'Italic (⌘I)',
315
+ strike: 'Strikethrough',
316
+ link: 'Link (⌘K)',
317
+ heading: 'Heading',
318
+ bulletList: 'Bulleted list',
319
+ orderedList: 'Numbered list',
320
+ blockquote: 'Quote',
321
+ codeBlock: 'Code block',
322
+ attachFiles: 'Attach file',
323
+ };
324
+ const wrapperStyle = {};
325
+ if (minHeight)
326
+ wrapperStyle.minHeight = minHeight;
327
+ if (maxHeight)
328
+ wrapperStyle.maxHeight = maxHeight;
329
+ return (_jsxs("div", { className: "flex flex-col rounded-md border bg-background", children: [canAttach && (_jsx("input", { ref: fileInputRef, type: "file", className: "hidden", onChange: onFilePicked })), _jsxs("div", { className: "flex items-center justify-between border-b px-2 py-1 gap-2", children: [_jsxs("div", { className: "flex items-center gap-0.5", children: [_jsxs(TabButton, { active: tab === 'editor', onClick: enterEditorTab, children: [SvgIcons['pencil'], " Editor"] }), _jsxs(TabButton, { active: tab === 'source', onClick: enterSourceTab, children: [SvgIcons['source'], " Source"] }), _jsxs(TabButton, { active: tab === 'preview', onClick: enterPreviewTab, children: [SvgIcons['eye'], " Preview"] })] }), tab === 'editor' && toolbarButtons.length > 0 && (_jsx("div", { className: "flex items-center gap-0.5", children: toolbarButtons.map((b) => {
330
+ if (b === 'attachFiles' && !canAttach)
331
+ return null;
332
+ const icon = SvgIcons[b];
333
+ if (!icon)
334
+ return null;
335
+ const isAttach = b === 'attachFiles';
336
+ const active = isActive(b);
337
+ return (_jsx("button", { type: "button", className: [
338
+ 'inline-flex size-7 items-center justify-center rounded text-foreground transition-colors',
339
+ active
340
+ ? 'bg-accent text-accent-foreground'
341
+ : 'hover:bg-accent hover:text-accent-foreground',
342
+ 'disabled:opacity-50',
343
+ ].join(' '), onClick: () => exec(b), disabled: disabled || (isAttach && uploading), title: labels[b] ?? b, "aria-label": labels[b] ?? b, "aria-pressed": active, children: isAttach && uploading ? Spinner : icon }, b));
344
+ }) }))] }), tab === 'editor' && (_jsx("div", { className: "prose prose-sm dark:prose-invert max-w-none px-3 py-2 [&_.ProseMirror]:outline-none [&_.ProseMirror]:min-h-[6rem]", style: wrapperStyle, children: _jsx(EditorContent, { editor: editor }) })), tab === 'source' && (_jsx("textarea", { className: "w-full resize-y bg-transparent px-3 py-2 text-sm font-mono leading-relaxed outline-none disabled:opacity-50", style: wrapperStyle, value: sourceDraft, onChange: (e) => setSourceDraft(e.target.value), ...(placeholder !== undefined ? { placeholder } : {}), disabled: disabled, "aria-label": `${name} (markdown source)` })), tab === 'preview' && (_jsx("div", { className: "prose prose-sm dark:prose-invert max-w-none px-3 py-2", style: wrapperStyle, dangerouslySetInnerHTML: { __html: previewHtml || '<p class="text-muted-foreground italic">Nothing to preview</p>' } }))] }));
345
+ }
346
+ function TabButton({ active, onClick, children }) {
347
+ return (_jsx("button", { type: "button", className: [
348
+ 'inline-flex items-center gap-1 rounded px-2 py-1 text-xs font-medium transition-colors',
349
+ active
350
+ ? 'bg-accent text-accent-foreground'
351
+ : 'text-muted-foreground hover:text-foreground',
352
+ ].join(' '), onClick: onClick, children: children }));
353
+ }
354
+ //# sourceMappingURL=MarkdownEditor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MarkdownEditor.js","sourceRoot":"","sources":["../../src/react/MarkdownEditor.tsx"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAGrC"}
1
+ {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAgBrC"}
package/dist/register.js CHANGED
@@ -1,6 +1,8 @@
1
- import { registerFieldRenderer } from '@pilotiq/pilotiq/react';
1
+ import { registerFieldRenderer, registerCollabTextRenderer, registerMarkdownEditor } from '@pilotiq/pilotiq/react';
2
2
  import { registerRichTextRenderer } from '@pilotiq/pilotiq/richtext';
3
3
  import { TiptapEditor } from './react/TiptapEditor.js';
4
+ import { CollabTextRenderer } from './react/CollabTextRenderer.js';
5
+ import { MarkdownEditor } from './react/MarkdownEditor.js';
4
6
  import { renderRichTextToHtml, isRichTextValue } from './render.js';
5
7
  /**
6
8
  * Register the Tiptap editor as the pilotiq renderer for `fieldType: 'richtext'`.
@@ -23,5 +25,18 @@ import { renderRichTextToHtml, isRichTextValue } from './render.js';
23
25
  export function registerTiptap() {
24
26
  registerFieldRenderer('richtext', TiptapEditor);
25
27
  registerRichTextRenderer(renderRichTextToHtml, isRichTextValue);
28
+ // Phase B — opt every plain-text field in the panel into y-prosemirror
29
+ // backing when collab is on. `TextLikeInput` checks this registry; if it's
30
+ // populated AND a `<RecordCollabRoom>` is up-tree AND the field hasn't opted
31
+ // out via `.collab(false)`, the renderer mounts `CollabTextRenderer`
32
+ // instead of the legacy `Y.Text` + `computeDelta` + `preserveCursor` path.
33
+ registerCollabTextRenderer(CollabTextRenderer);
34
+ // WYSIWYG markdown editor — replaces `MarkdownField`'s legacy textarea +
35
+ // manual-toolbar path with a real rich editor that serializes to markdown
36
+ // via `tiptap-markdown` on every change. Collab-aware on the same
37
+ // `useCollabRoom()` + `getCollabExtensions()` plumbing as the rich-text
38
+ // editor. Without `@pilotiq/tiptap` installed, `MarkdownInput` falls back
39
+ // to the textarea path so panels that skip the adapter still work.
40
+ registerMarkdownEditor(MarkdownEditor);
26
41
  }
27
42
  //# sourceMappingURL=register.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"register.js","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AAC9D,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAA;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AACtD,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAEnE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,cAAc;IAC5B,qBAAqB,CAAC,UAAU,EAAE,YAAY,CAAC,CAAA;IAC/C,wBAAwB,CAAC,oBAAoB,EAAE,eAAe,CAAC,CAAA;AACjE,CAAC"}
1
+ {"version":3,"file":"register.js","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,0BAA0B,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAA;AAClH,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAA;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAA;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAC1D,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAEnE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,cAAc;IAC5B,qBAAqB,CAAC,UAAU,EAAE,YAAY,CAAC,CAAA;IAC/C,wBAAwB,CAAC,oBAAoB,EAAE,eAAe,CAAC,CAAA;IAC/D,uEAAuE;IACvE,2EAA2E;IAC3E,6EAA6E;IAC7E,qEAAqE;IACrE,2EAA2E;IAC3E,0BAA0B,CAAC,kBAAkB,CAAC,CAAA;IAC9C,yEAAyE;IACzE,0EAA0E;IAC1E,kEAAkE;IAClE,wEAAwE;IACxE,0EAA0E;IAC1E,mEAAmE;IACnE,sBAAsB,CAAC,cAAc,CAAC,CAAA;AACxC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pilotiq/tiptap",
3
- "version": "3.2.0",
3
+ "version": "3.3.0",
4
4
  "description": "Tiptap rich-text editor adapter for @pilotiq/pilotiq — slash menu, draggable blocks, custom-block API",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -51,6 +51,7 @@
51
51
  "@tiptap/extension-table": "3.22.4",
52
52
  "@tiptap/extension-details": "3.22.4",
53
53
  "@tiptap/suggestion": "^3",
54
+ "tiptap-markdown": "^0.9",
54
55
  "react": "^18 || ^19",
55
56
  "react-dom": "^18 || ^19"
56
57
  },
@@ -73,13 +74,14 @@
73
74
  "@tiptap/extension-table": "3.22.4",
74
75
  "@tiptap/extension-details": "3.22.4",
75
76
  "@tiptap/suggestion": "^3",
77
+ "tiptap-markdown": "^0.9",
76
78
  "@types/node": "^20",
77
79
  "@types/react": "^19",
78
80
  "@types/react-dom": "^19",
79
81
  "react": "^19",
80
82
  "react-dom": "^19",
81
83
  "typescript": "^5",
82
- "@pilotiq/pilotiq": "^0.8.0"
84
+ "@pilotiq/pilotiq": "^0.15.0"
83
85
  },
84
86
  "author": "Suleiman Shahbari",
85
87
  "scripts": {
@@ -0,0 +1,158 @@
1
+ import { describe, it } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+
4
+ import {
5
+ createPlainTextEditor,
6
+ plainTextToDoc,
7
+ type PlainTextEditorOptions,
8
+ } from './PlainTextEditor.js'
9
+
10
+ describe('plainTextToDoc — single-line', () => {
11
+ it('empty string yields one empty paragraph', () => {
12
+ assert.deepEqual(plainTextToDoc('', false), {
13
+ type: 'doc',
14
+ content: [{ type: 'paragraph' }],
15
+ })
16
+ })
17
+
18
+ it('wraps a single run of text in one paragraph', () => {
19
+ assert.deepEqual(plainTextToDoc('hello', false), {
20
+ type: 'doc',
21
+ content: [{ type: 'paragraph', content: [{ type: 'text', text: 'hello' }] }],
22
+ })
23
+ })
24
+
25
+ it('strips embedded newlines (LF and CRLF) — single-line schema permits one paragraph only', () => {
26
+ assert.deepEqual(plainTextToDoc('a\nb', false), {
27
+ type: 'doc',
28
+ content: [{ type: 'paragraph', content: [{ type: 'text', text: 'ab' }] }],
29
+ })
30
+ assert.deepEqual(plainTextToDoc('a\r\nb', false), {
31
+ type: 'doc',
32
+ content: [{ type: 'paragraph', content: [{ type: 'text', text: 'ab' }] }],
33
+ })
34
+ })
35
+ })
36
+
37
+ describe('plainTextToDoc — multi-line', () => {
38
+ it('empty string yields one empty paragraph', () => {
39
+ assert.deepEqual(plainTextToDoc('', true), {
40
+ type: 'doc',
41
+ content: [{ type: 'paragraph' }],
42
+ })
43
+ })
44
+
45
+ it('splits LF-separated lines into separate paragraphs', () => {
46
+ assert.deepEqual(plainTextToDoc('a\nb', true), {
47
+ type: 'doc',
48
+ content: [
49
+ { type: 'paragraph', content: [{ type: 'text', text: 'a' }] },
50
+ { type: 'paragraph', content: [{ type: 'text', text: 'b' }] },
51
+ ],
52
+ })
53
+ })
54
+
55
+ it('preserves empty lines as empty paragraphs', () => {
56
+ assert.deepEqual(plainTextToDoc('a\n\nb', true), {
57
+ type: 'doc',
58
+ content: [
59
+ { type: 'paragraph', content: [{ type: 'text', text: 'a' }] },
60
+ { type: 'paragraph' },
61
+ { type: 'paragraph', content: [{ type: 'text', text: 'b' }] },
62
+ ],
63
+ })
64
+ })
65
+
66
+ it('normalises CRLF to single paragraph splits', () => {
67
+ assert.deepEqual(plainTextToDoc('a\r\nb', true), {
68
+ type: 'doc',
69
+ content: [
70
+ { type: 'paragraph', content: [{ type: 'text', text: 'a' }] },
71
+ { type: 'paragraph', content: [{ type: 'text', text: 'b' }] },
72
+ ],
73
+ })
74
+ })
75
+ })
76
+
77
+ describe('createPlainTextEditor — config shape', () => {
78
+ function names(extensions: ReadonlyArray<{ name: string }>): string[] {
79
+ return extensions.map((e) => e.name)
80
+ }
81
+
82
+ it('default config: single-line, editable, schema + single-line keymap only', () => {
83
+ const cfg = createPlainTextEditor()
84
+ assert.equal(cfg.editable, true)
85
+ assert.equal(cfg.content, '')
86
+ const exts = (cfg.extensions ?? []) as Array<{ name: string }>
87
+ assert.deepEqual(names(exts), ['doc', 'paragraph', 'text', 'plainTextSingleLineKeymap'])
88
+ assert.equal(cfg.editorProps, undefined)
89
+ assert.equal(cfg.onUpdate, undefined)
90
+ })
91
+
92
+ it('multiline mode drops the single-line keymap', () => {
93
+ const cfg = createPlainTextEditor({ multiline: true })
94
+ const exts = (cfg.extensions ?? []) as Array<{ name: string }>
95
+ assert.deepEqual(names(exts), ['doc', 'paragraph', 'text'])
96
+ })
97
+
98
+ it('placeholder appends the Placeholder extension', () => {
99
+ const cfg = createPlainTextEditor({ placeholder: 'Type here…' })
100
+ const exts = (cfg.extensions ?? []) as Array<{ name: string }>
101
+ assert.ok(exts.some((e) => e.name === 'placeholder'),
102
+ `expected placeholder extension, got ${names(exts).join(',')}`)
103
+ })
104
+
105
+ it('caller-supplied extensions land after schema + behavior', () => {
106
+ const fakeExt = { name: 'fake-collab' } as unknown as Parameters<typeof createPlainTextEditor>[0] extends infer T
107
+ ? T extends { extensions?: Array<infer E> } ? E : never : never
108
+ const cfg = createPlainTextEditor({ extensions: [fakeExt] })
109
+ const exts = (cfg.extensions ?? []) as Array<{ name: string }>
110
+ assert.equal(exts[exts.length - 1]?.name, 'fake-collab')
111
+ })
112
+
113
+ it('editable can be turned off', () => {
114
+ const cfg = createPlainTextEditor({ editable: false })
115
+ assert.equal(cfg.editable, false)
116
+ })
117
+
118
+ it('seeds content as a doc JSON when text provided (single-line)', () => {
119
+ const cfg = createPlainTextEditor({ content: 'hello' })
120
+ assert.deepEqual(cfg.content, {
121
+ type: 'doc',
122
+ content: [{ type: 'paragraph', content: [{ type: 'text', text: 'hello' }] }],
123
+ })
124
+ })
125
+
126
+ it('seeds content as multi-paragraph doc JSON when multiline + text provided', () => {
127
+ const cfg = createPlainTextEditor({ multiline: true, content: 'a\nb' })
128
+ assert.deepEqual(cfg.content, {
129
+ type: 'doc',
130
+ content: [
131
+ { type: 'paragraph', content: [{ type: 'text', text: 'a' }] },
132
+ { type: 'paragraph', content: [{ type: 'text', text: 'b' }] },
133
+ ],
134
+ })
135
+ })
136
+
137
+ it('empty content stays an empty string sentinel — Collaboration-friendly', () => {
138
+ // When collab is on, callers pass content omitted/'' so Collaboration's
139
+ // y-prosemirror binding takes ownership of the doc without a seed race.
140
+ const cfg = createPlainTextEditor({ content: '' })
141
+ assert.equal(cfg.content, '')
142
+ })
143
+
144
+ it('editorAttributes plumb into editorProps.attributes verbatim', () => {
145
+ const attrs = { class: 'foo bar', 'aria-label': 'Name' }
146
+ const cfg = createPlainTextEditor({ editorAttributes: attrs })
147
+ assert.deepEqual(cfg.editorProps, { attributes: attrs })
148
+ })
149
+
150
+ it('onUpdate is wired only when caller provides it', () => {
151
+ const withCb: PlainTextEditorOptions = { onUpdate: () => {} }
152
+ const cfgA = createPlainTextEditor(withCb)
153
+ assert.equal(typeof cfgA.onUpdate, 'function')
154
+
155
+ const cfgB = createPlainTextEditor()
156
+ assert.equal(cfgB.onUpdate, undefined)
157
+ })
158
+ })