@pilotiq/tiptap 0.1.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.
Files changed (130) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +67 -0
  3. package/dist/Block.d.ts +47 -0
  4. package/dist/Block.d.ts.map +1 -0
  5. package/dist/Block.js +56 -0
  6. package/dist/Block.js.map +1 -0
  7. package/dist/MentionProvider.d.ts +97 -0
  8. package/dist/MentionProvider.d.ts.map +1 -0
  9. package/dist/MentionProvider.js +104 -0
  10. package/dist/MentionProvider.js.map +1 -0
  11. package/dist/RichTextField.d.ts +286 -0
  12. package/dist/RichTextField.d.ts.map +1 -0
  13. package/dist/RichTextField.js +369 -0
  14. package/dist/RichTextField.js.map +1 -0
  15. package/dist/extensions/BlockNodeExtension.d.ts +41 -0
  16. package/dist/extensions/BlockNodeExtension.d.ts.map +1 -0
  17. package/dist/extensions/BlockNodeExtension.js +103 -0
  18. package/dist/extensions/BlockNodeExtension.js.map +1 -0
  19. package/dist/extensions/DragHandleExtension.d.ts +19 -0
  20. package/dist/extensions/DragHandleExtension.d.ts.map +1 -0
  21. package/dist/extensions/DragHandleExtension.js +166 -0
  22. package/dist/extensions/DragHandleExtension.js.map +1 -0
  23. package/dist/extensions/GridExtension.d.ts +49 -0
  24. package/dist/extensions/GridExtension.d.ts.map +1 -0
  25. package/dist/extensions/GridExtension.js +105 -0
  26. package/dist/extensions/GridExtension.js.map +1 -0
  27. package/dist/extensions/MentionExtension.d.ts +71 -0
  28. package/dist/extensions/MentionExtension.d.ts.map +1 -0
  29. package/dist/extensions/MentionExtension.js +165 -0
  30. package/dist/extensions/MentionExtension.js.map +1 -0
  31. package/dist/extensions/MergeTagExtension.d.ts +24 -0
  32. package/dist/extensions/MergeTagExtension.d.ts.map +1 -0
  33. package/dist/extensions/MergeTagExtension.js +57 -0
  34. package/dist/extensions/MergeTagExtension.js.map +1 -0
  35. package/dist/extensions/SlashCommandExtension.d.ts +71 -0
  36. package/dist/extensions/SlashCommandExtension.d.ts.map +1 -0
  37. package/dist/extensions/SlashCommandExtension.js +244 -0
  38. package/dist/extensions/SlashCommandExtension.js.map +1 -0
  39. package/dist/extensions/TextSizeMarks.d.ts +33 -0
  40. package/dist/extensions/TextSizeMarks.d.ts.map +1 -0
  41. package/dist/extensions/TextSizeMarks.js +47 -0
  42. package/dist/extensions/TextSizeMarks.js.map +1 -0
  43. package/dist/index.d.ts +8 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +8 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/plugin.d.ts +18 -0
  48. package/dist/plugin.d.ts.map +1 -0
  49. package/dist/plugin.js +25 -0
  50. package/dist/plugin.js.map +1 -0
  51. package/dist/react/BlockNodeView.d.ts +19 -0
  52. package/dist/react/BlockNodeView.d.ts.map +1 -0
  53. package/dist/react/BlockNodeView.js +60 -0
  54. package/dist/react/BlockNodeView.js.map +1 -0
  55. package/dist/react/BlockSidePanel.d.ts +105 -0
  56. package/dist/react/BlockSidePanel.d.ts.map +1 -0
  57. package/dist/react/BlockSidePanel.js +339 -0
  58. package/dist/react/BlockSidePanel.js.map +1 -0
  59. package/dist/react/FloatingToolbar.d.ts +13 -0
  60. package/dist/react/FloatingToolbar.d.ts.map +1 -0
  61. package/dist/react/FloatingToolbar.js +113 -0
  62. package/dist/react/FloatingToolbar.js.map +1 -0
  63. package/dist/react/MentionMenu.d.ts +26 -0
  64. package/dist/react/MentionMenu.d.ts.map +1 -0
  65. package/dist/react/MentionMenu.js +64 -0
  66. package/dist/react/MentionMenu.js.map +1 -0
  67. package/dist/react/Palette.d.ts +26 -0
  68. package/dist/react/Palette.d.ts.map +1 -0
  69. package/dist/react/Palette.js +21 -0
  70. package/dist/react/Palette.js.map +1 -0
  71. package/dist/react/SlashMenu.d.ts +24 -0
  72. package/dist/react/SlashMenu.d.ts.map +1 -0
  73. package/dist/react/SlashMenu.js +74 -0
  74. package/dist/react/SlashMenu.js.map +1 -0
  75. package/dist/react/TableFloatingToolbar.d.ts +7 -0
  76. package/dist/react/TableFloatingToolbar.d.ts.map +1 -0
  77. package/dist/react/TableFloatingToolbar.js +108 -0
  78. package/dist/react/TableFloatingToolbar.js.map +1 -0
  79. package/dist/react/TiptapEditor.d.ts +20 -0
  80. package/dist/react/TiptapEditor.d.ts.map +1 -0
  81. package/dist/react/TiptapEditor.js +398 -0
  82. package/dist/react/TiptapEditor.js.map +1 -0
  83. package/dist/react/Toolbar.d.ts +45 -0
  84. package/dist/react/Toolbar.d.ts.map +1 -0
  85. package/dist/react/Toolbar.js +204 -0
  86. package/dist/react/Toolbar.js.map +1 -0
  87. package/dist/react/toolbarButtons.d.ts +36 -0
  88. package/dist/react/toolbarButtons.d.ts.map +1 -0
  89. package/dist/react/toolbarButtons.js +300 -0
  90. package/dist/react/toolbarButtons.js.map +1 -0
  91. package/dist/register.d.ts +20 -0
  92. package/dist/register.d.ts.map +1 -0
  93. package/dist/register.js +27 -0
  94. package/dist/register.js.map +1 -0
  95. package/dist/render.d.ts +89 -0
  96. package/dist/render.d.ts.map +1 -0
  97. package/dist/render.js +439 -0
  98. package/dist/render.js.map +1 -0
  99. package/package.json +92 -0
  100. package/src/Block.ts +75 -0
  101. package/src/MentionProvider.ts +153 -0
  102. package/src/RichTextField.test.ts +447 -0
  103. package/src/RichTextField.ts +508 -0
  104. package/src/extensions/BlockNodeExtension.ts +134 -0
  105. package/src/extensions/DragHandleExtension.ts +184 -0
  106. package/src/extensions/GridExtension.test.ts +31 -0
  107. package/src/extensions/GridExtension.ts +138 -0
  108. package/src/extensions/MentionExtension.ts +248 -0
  109. package/src/extensions/MergeTagExtension.ts +75 -0
  110. package/src/extensions/SlashCommandExtension.test.ts +147 -0
  111. package/src/extensions/SlashCommandExtension.ts +332 -0
  112. package/src/extensions/TextSizeMarks.ts +73 -0
  113. package/src/index.ts +28 -0
  114. package/src/plugin.test.ts +19 -0
  115. package/src/plugin.ts +26 -0
  116. package/src/react/BlockNodeView.tsx +99 -0
  117. package/src/react/BlockSidePanel.test.ts +412 -0
  118. package/src/react/BlockSidePanel.tsx +451 -0
  119. package/src/react/FloatingToolbar.tsx +304 -0
  120. package/src/react/MentionMenu.tsx +120 -0
  121. package/src/react/Palette.tsx +86 -0
  122. package/src/react/SlashMenu.tsx +129 -0
  123. package/src/react/TableFloatingToolbar.tsx +154 -0
  124. package/src/react/TiptapEditor.tsx +535 -0
  125. package/src/react/Toolbar.tsx +438 -0
  126. package/src/react/toolbarButtons.tsx +579 -0
  127. package/src/register.test.ts +14 -0
  128. package/src/register.ts +27 -0
  129. package/src/render.test.ts +745 -0
  130. package/src/render.ts +480 -0
@@ -0,0 +1,204 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
3
+ import { Tooltip } from '@base-ui/react/tooltip';
4
+ import { Dialog } from '@base-ui/react/dialog';
5
+ import { TOOLBAR_BUTTONS } from './toolbarButtons.js';
6
+ import { Palette } from './Palette.js';
7
+ /**
8
+ * Always-on toolbar rendered above the editor. Groups are joined with thin
9
+ * dividers; unknown / unavailable button ids are silently dropped so config
10
+ * can target later-phase buttons today without crashing.
11
+ *
12
+ * Inline link button opens a Base UI dialog with a URL input — kept here
13
+ * (not in the FloatingToolbar's dialog) because each toolbar instance owns
14
+ * its own React state for the modal.
15
+ */
16
+ export function Toolbar({ editor, groups, tick, textColors, customTextColors, highlightColors, onAttachOpenChange, }) {
17
+ const [linkOpen, setLinkOpen] = useState(false);
18
+ const [linkUrl, setLinkUrl] = useState('');
19
+ const filteredGroups = groups
20
+ .map((g) => g.map((id) => TOOLBAR_BUTTONS[id]).filter((b) => Boolean(b?.available)))
21
+ .filter((g) => g.length > 0);
22
+ if (filteredGroups.length === 0)
23
+ return null;
24
+ const openLinkDialog = () => {
25
+ const previousUrl = editor.getAttributes('link')['href'];
26
+ setLinkUrl(previousUrl ?? '');
27
+ setLinkOpen(true);
28
+ };
29
+ const applyLink = () => {
30
+ setLinkOpen(false);
31
+ const trimmed = linkUrl.trim();
32
+ if (trimmed === '') {
33
+ editor.chain().focus().extendMarkRange('link').unsetLink().run();
34
+ return;
35
+ }
36
+ editor.chain().focus().extendMarkRange('link').setLink({ href: trimmed }).run();
37
+ };
38
+ const removeLink = () => {
39
+ setLinkOpen(false);
40
+ editor.chain().focus().extendMarkRange('link').unsetLink().run();
41
+ };
42
+ return (_jsxs(_Fragment, { children: [_jsx(Tooltip.Provider, { delay: 400, children: _jsx("div", {
43
+ // The toolbar sits visually attached to the top of the editor body —
44
+ // shared border-radius + a `border-b` on the toolbar gives a single
45
+ // grouped look. Editor body's own border-radius is reset top-side
46
+ // via global classnames in the editor.
47
+ className: "flex flex-wrap items-center gap-0.5 rounded-t-md border border-input bg-muted/30 px-1 py-1 text-foreground",
48
+ // Mousedown anywhere in the toolbar should not steal focus from the
49
+ // editor — `preventDefault` keeps the selection alive while the
50
+ // command runs.
51
+ onMouseDown: (e) => { e.preventDefault(); }, "data-tick": tick, children: filteredGroups.map((group, gi) => (_jsxs("div", { className: "flex items-center gap-0.5", children: [gi > 0 && _jsx("span", { "aria-hidden": true, className: "mx-1 h-5 w-px shrink-0 bg-border" }), group.map((btn) => {
52
+ if (btn.custom === 'textColor') {
53
+ return (_jsx(Palette, { swatches: textColors, custom: customTextColors, activeColor: editor.getAttributes('textStyle')['color'], onPick: (value) => editor.chain().focus().setColor(value).run(), onClear: () => editor.chain().focus().unsetColor().run(), clearLabel: "Reset color", trigger: _jsx(ToolbarButton, { def: btn, editor: editor }) }, btn.id));
54
+ }
55
+ if (btn.custom === 'highlight') {
56
+ return (_jsx(Palette, { swatches: highlightColors, custom: false, activeColor: editor.getAttributes('highlight')['color'], onPick: (value) => editor.chain().focus().toggleHighlight({ color: value }).run(), onClear: () => editor.chain().focus().unsetHighlight().run(), clearLabel: "Remove highlight", trigger: _jsx(ToolbarButton, { def: btn, editor: editor }) }, btn.id));
57
+ }
58
+ const customClick = btn.custom === 'link' ? openLinkDialog :
59
+ btn.custom === 'attachFiles' ? () => onAttachOpenChange(true) :
60
+ undefined;
61
+ return (_jsx(ToolbarButton, { def: btn, editor: editor, onCustomClick: customClick }, btn.id));
62
+ })] }, gi))) }) }), _jsx(LinkDialog, { open: linkOpen, onOpenChange: setLinkOpen, url: linkUrl, onUrlChange: setLinkUrl, onApply: applyLink, onRemove: editor.isActive('link') ? removeLink : null, isEdit: editor.isActive('link') })] }));
63
+ }
64
+ function ToolbarButton({ def, editor, onCustomClick }) {
65
+ const active = def.isActive?.(editor) ?? false;
66
+ const disabled = def.isDisabled?.(editor) ?? false;
67
+ const mod = isMac() ? '⌘' : 'Ctrl+';
68
+ const tooltip = def.shortcut ? `${def.label} (${mod}${def.shortcut})` : def.label;
69
+ return (_jsxs(Tooltip.Root, { children: [_jsx(Tooltip.Trigger, { render: (props) => (_jsx("button", { ...props, type: "button", disabled: disabled, onClick: () => {
70
+ if (onCustomClick) {
71
+ onCustomClick();
72
+ return;
73
+ }
74
+ def.command(editor);
75
+ }, className: `inline-flex h-7 w-7 items-center justify-center rounded text-foreground transition-colors disabled:opacity-50 disabled:pointer-events-none ${active ? 'bg-accent text-accent-foreground' : 'hover:bg-accent/60'}`, "aria-label": def.label, "aria-pressed": active, children: def.icon })) }), _jsx(Tooltip.Portal, { children: _jsx(Tooltip.Positioner, { side: "bottom", sideOffset: 6, className: "isolate z-50", children: _jsx(Tooltip.Popup, { className: "rounded-md bg-foreground px-2 py-1 text-xs text-background shadow-md data-[side=bottom]:slide-in-from-top-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", children: tooltip }) }) })] }));
76
+ }
77
+ export function AttachFilesDialog({ open, onOpenChange, editor, uploadUrl, fieldName, acceptedFileTypes, maxFileSize, directory, visibility, }) {
78
+ const [file, setFile] = useState(null);
79
+ const [alt, setAlt] = useState('');
80
+ const [busy, setBusy] = useState(false);
81
+ const [error, setError] = useState(null);
82
+ // Reset transient state every time the dialog opens — otherwise the
83
+ // previous upload's filename / alt text would leak into the next one.
84
+ useEffect(() => {
85
+ if (open) {
86
+ setFile(null);
87
+ setAlt('');
88
+ setError(null);
89
+ setBusy(false);
90
+ }
91
+ }, [open]);
92
+ const accept = acceptedFileTypes?.join(',') ?? 'image/*';
93
+ const onPickFile = (f) => {
94
+ setError(null);
95
+ if (!f) {
96
+ setFile(null);
97
+ return;
98
+ }
99
+ if (maxFileSize !== undefined && f.size > maxFileSize) {
100
+ setError(`File exceeds the maximum size of ${formatBytes(maxFileSize)}.`);
101
+ setFile(null);
102
+ return;
103
+ }
104
+ setFile(f);
105
+ if (alt === '')
106
+ setAlt(stripExtension(f.name));
107
+ };
108
+ const onUpload = async () => {
109
+ if (!file)
110
+ return;
111
+ if (!uploadUrl) {
112
+ setError('No upload route configured for this panel.');
113
+ return;
114
+ }
115
+ setBusy(true);
116
+ setError(null);
117
+ try {
118
+ const fd = new FormData();
119
+ fd.append('file', file);
120
+ fd.append('fieldName', fieldName);
121
+ if (directory)
122
+ fd.append('directory', directory);
123
+ if (visibility)
124
+ fd.append('visibility', visibility);
125
+ const res = await fetch(uploadUrl, {
126
+ method: 'POST',
127
+ body: fd,
128
+ headers: { Accept: 'application/json' },
129
+ });
130
+ const data = await res.json().catch(() => ({}));
131
+ if (!res.ok || !data.ok || !data.url) {
132
+ setError(data.error ?? `Upload failed (status ${res.status}).`);
133
+ return;
134
+ }
135
+ // Image vs non-image file. Tiptap's StarterKit doesn't ship a generic
136
+ // file/attachment node, so for non-images we fall back to inserting
137
+ // a link mark on the filename — same shape MarkdownField uses.
138
+ if (file.type.startsWith('image/')) {
139
+ editor.chain().focus().setImage({ src: data.url, alt: alt || file.name }).run();
140
+ }
141
+ else {
142
+ editor.chain().focus()
143
+ .insertContent({
144
+ type: 'text',
145
+ text: alt || file.name,
146
+ marks: [{ type: 'link', attrs: { href: data.url } }],
147
+ })
148
+ .run();
149
+ }
150
+ onOpenChange(false);
151
+ }
152
+ catch (err) {
153
+ setError(err instanceof Error ? err.message : String(err));
154
+ }
155
+ finally {
156
+ setBusy(false);
157
+ }
158
+ };
159
+ return (_jsx(Dialog.Root, { open: open, onOpenChange: onOpenChange, children: _jsxs(Dialog.Portal, { children: [_jsx(Dialog.Backdrop, { className: "fixed inset-0 z-50 bg-black/50 transition-opacity duration-150 data-[starting-style]:opacity-0 data-[ending-style]:opacity-0" }), _jsxs(Dialog.Popup, { className: "fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] sm:max-w-md translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg transition-[opacity,transform] duration-150 data-[starting-style]:scale-95 data-[starting-style]:opacity-0 data-[ending-style]:scale-95 data-[ending-style]:opacity-0", children: [_jsx(Dialog.Title, { className: "text-lg leading-none font-semibold", children: "Attach file" }), _jsxs("div", { className: "flex flex-col gap-3", children: [_jsx("input", { type: "file", accept: accept, disabled: busy, onChange: (e) => onPickFile(e.target.files?.[0] ?? null), className: "block w-full text-sm file:me-3 file:rounded-md file:border-0 file:bg-muted file:px-3 file:py-1.5 file:text-sm file:font-medium hover:file:bg-muted/80" }), file && (_jsx("input", { type: "text", value: alt, onChange: (e) => setAlt(e.target.value), placeholder: "Alt text (described for screen readers)", disabled: busy, className: "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring" })), maxFileSize !== undefined && (_jsxs("p", { className: "text-xs text-muted-foreground", children: ["Maximum size: ", formatBytes(maxFileSize), "."] })), error && (_jsx("p", { className: "text-xs text-destructive", children: error }))] }), _jsxs("div", { className: "flex flex-row-reverse items-center gap-2", children: [_jsx("button", { type: "button", onClick: onUpload, disabled: !file || busy, className: "inline-flex h-9 items-center justify-center rounded-md bg-primary px-3 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50 disabled:pointer-events-none", children: busy ? 'Uploading…' : 'Insert' }), _jsx("button", { type: "button", onClick: () => onOpenChange(false), disabled: busy, className: "inline-flex h-9 items-center justify-center rounded-md border border-input bg-background px-3 text-sm font-medium hover:bg-accent hover:text-accent-foreground disabled:opacity-50", children: "Cancel" })] })] })] }) }));
160
+ }
161
+ function stripExtension(name) {
162
+ const i = name.lastIndexOf('.');
163
+ return i > 0 ? name.slice(0, i) : name;
164
+ }
165
+ function formatBytes(bytes) {
166
+ if (bytes < 1024)
167
+ return `${bytes} B`;
168
+ if (bytes < 1024 * 1024)
169
+ return `${(bytes / 1024).toFixed(0)} KB`;
170
+ return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
171
+ }
172
+ function LinkDialog({ open, onOpenChange, url, onUrlChange, onApply, onRemove, isEdit, }) {
173
+ return (_jsx(Dialog.Root, { open: open, onOpenChange: onOpenChange, children: _jsxs(Dialog.Portal, { children: [_jsx(Dialog.Backdrop, { className: "fixed inset-0 z-50 bg-black/50 transition-opacity duration-150 data-[starting-style]:opacity-0 data-[ending-style]:opacity-0" }), _jsxs(Dialog.Popup, { className: "fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] sm:max-w-md translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg transition-[opacity,transform] duration-150 data-[starting-style]:scale-95 data-[starting-style]:opacity-0 data-[ending-style]:scale-95 data-[ending-style]:opacity-0", children: [_jsx(Dialog.Title, { className: "text-lg leading-none font-semibold", children: isEdit ? 'Edit link' : 'Add link' }), _jsx("input", { type: "url", value: url, onChange: (e) => onUrlChange(e.target.value), onKeyDown: (e) => {
174
+ if (e.key === 'Enter') {
175
+ e.preventDefault();
176
+ onApply();
177
+ }
178
+ }, placeholder: "https://example.com", autoFocus: true, className: "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring" }), _jsxs("div", { className: "flex flex-row-reverse items-center gap-2", children: [_jsx("button", { type: "button", onClick: onApply, className: "inline-flex h-9 items-center justify-center rounded-md bg-primary px-3 text-sm font-medium text-primary-foreground hover:bg-primary/90", children: isEdit ? 'Update' : 'Add' }), _jsx("button", { type: "button", onClick: () => onOpenChange(false), className: "inline-flex h-9 items-center justify-center rounded-md border border-input bg-background px-3 text-sm font-medium hover:bg-accent hover:text-accent-foreground", children: "Cancel" }), onRemove && (_jsx("button", { type: "button", onClick: onRemove, className: "me-auto inline-flex h-9 items-center justify-center rounded-md px-3 text-sm font-medium text-destructive hover:bg-destructive/10", children: "Remove link" }))] })] })] }) }));
179
+ }
180
+ /**
181
+ * Hook returning a tick that bumps on any selection / transaction change.
182
+ * The Toolbar renders dependent on this so active-state booleans stay fresh.
183
+ */
184
+ export function useEditorTick(editor) {
185
+ const [tick, setTick] = useState(0);
186
+ useEffect(() => {
187
+ if (!editor)
188
+ return;
189
+ const bump = () => setTick((t) => t + 1);
190
+ editor.on('selectionUpdate', bump);
191
+ editor.on('transaction', bump);
192
+ return () => {
193
+ editor.off('selectionUpdate', bump);
194
+ editor.off('transaction', bump);
195
+ };
196
+ }, [editor]);
197
+ return tick;
198
+ }
199
+ function isMac() {
200
+ if (typeof navigator === 'undefined')
201
+ return false;
202
+ return /Mac|iPhone|iPad|iPod/.test(navigator.platform);
203
+ }
204
+ //# sourceMappingURL=Toolbar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Toolbar.js","sourceRoot":"","sources":["../../src/react/Toolbar.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { Editor } from '@tiptap/core';
3
+ import type { ToolbarButtonId } from '../RichTextField.js';
4
+ /**
5
+ * One toolbar button's behavior. The renderer wires `<button>` chrome — this
6
+ * descriptor only knows the editor command, the active-state predicate, and
7
+ * the icon.
8
+ *
9
+ * Buttons that depend on extensions registered in later phases (textColor,
10
+ * highlight, attachFiles, table*) are registered with `available: false`
11
+ * until that phase ships, so config that targets them today is silently
12
+ * dropped instead of crashing.
13
+ */
14
+ export interface ToolbarButtonDef {
15
+ id: ToolbarButtonId;
16
+ label: string;
17
+ shortcut?: string;
18
+ icon: ReactNode;
19
+ /** Phase-A buttons set this to `true`; later-phase placeholders set `false`. */
20
+ available: boolean;
21
+ /** Whether the button reflects the editor's "currently active" state. */
22
+ isActive?: (editor: Editor) => boolean;
23
+ /** Whether the button is disabled given the current selection. */
24
+ isDisabled?: (editor: Editor) => boolean;
25
+ /** Run on click. */
26
+ command: (editor: Editor) => void;
27
+ /**
28
+ * Optional opt-in for buttons whose behavior is "open a UI" rather than
29
+ * fire a chain — the editor surface keeps its own state for these and the
30
+ * default click handler doesn't run a chain.
31
+ */
32
+ custom?: 'link' | 'textColor' | 'highlight' | 'attachFiles';
33
+ }
34
+ /** Lookup table for every recognized button id. */
35
+ export declare const TOOLBAR_BUTTONS: Record<ToolbarButtonId, ToolbarButtonDef>;
36
+ //# sourceMappingURL=toolbarButtons.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolbarButtons.d.ts","sourceRoot":"","sources":["../../src/react/toolbarButtons.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAE1D;;;;;;;;;GASG;AACH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAS,eAAe,CAAA;IAC1B,KAAK,EAAM,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,EAAO,SAAS,CAAA;IACpB,gFAAgF;IAChF,SAAS,EAAE,OAAO,CAAA;IAClB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAA;IACtC,kEAAkE;IAClE,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAA;IACxC,oBAAoB;IACpB,OAAO,EAAI,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,WAAW,GAAG,aAAa,CAAA;CAC5D;AAiTD,mDAAmD;AACnD,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,eAAe,EAAE,gBAAgB,CAyNrE,CAAA"}
@@ -0,0 +1,300 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ const ICON_PROPS = {
3
+ width: 14, height: 14, viewBox: '0 0 24 24',
4
+ fill: 'none', stroke: 'currentColor',
5
+ strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round',
6
+ 'aria-hidden': 'true',
7
+ };
8
+ // Inline SVGs (lucide.dev paths). Kept inline so the package doesn't pull
9
+ // `lucide-react` as a peer dep.
10
+ const Icons = {
11
+ 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" })] })),
12
+ 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" })] })),
13
+ underline: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "M6 4v6a6 6 0 0 0 12 0V4" }), _jsx("line", { x1: "4", y1: "20", x2: "20", y2: "20" })] })),
14
+ 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" })] })),
15
+ subscript: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "m4 5 8 10" }), _jsx("path", { d: "m12 5-8 10" }), _jsx("path", { d: "M20 21h-4c0-1.5.44-2 1.5-2.5S20 17.33 20 16c0-.47-.17-.93-.48-1.29a2.11 2.11 0 0 0-2.62-.44c-.42.24-.74.62-.9 1.07" })] })),
16
+ superscript: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "m4 19 8-10" }), _jsx("path", { d: "m12 19-8-10" }), _jsx("path", { d: "M20 11h-4c0-1.5.44-2 1.5-2.5S20 7.33 20 6c0-.47-.17-.93-.48-1.29a2.11 2.11 0 0 0-2.62-.44c-.42.24-.74.62-.9 1.07" })] })),
17
+ code: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("polyline", { points: "16 18 22 12 16 6" }), _jsx("polyline", { points: "8 6 2 12 8 18" })] })),
18
+ paragraph: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "M13 4v16" }), _jsx("path", { d: "M17 4v16" }), _jsx("path", { d: "M19 4H9.5a4.5 4.5 0 0 0 0 9H13" })] })),
19
+ h1: _jsx("span", { className: "text-xs font-semibold leading-none", children: "H1" }),
20
+ h2: _jsx("span", { className: "text-xs font-semibold leading-none", children: "H2" }),
21
+ h3: _jsx("span", { className: "text-xs font-semibold leading-none", children: "H3" }),
22
+ h4: _jsx("span", { className: "text-xs font-semibold leading-none", children: "H4" }),
23
+ h5: _jsx("span", { className: "text-xs font-semibold leading-none", children: "H5" }),
24
+ h6: _jsx("span", { className: "text-xs font-semibold leading-none", children: "H6" }),
25
+ // Sized text-string glyphs match the visual contrast of the marks they
26
+ // toggle — lead is the larger of the pair, small is the slightly smaller.
27
+ lead: _jsx("span", { className: "text-sm font-semibold leading-none tracking-tight", children: "P+" }),
28
+ small: _jsx("span", { className: "text-[10px] font-semibold leading-none tracking-tight", children: "P-" }),
29
+ alignStart: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("line", { x1: "3", y1: "6", x2: "21", y2: "6" }), _jsx("line", { x1: "3", y1: "12", x2: "15", y2: "12" }), _jsx("line", { x1: "3", y1: "18", x2: "18", y2: "18" })] })),
30
+ alignCenter: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("line", { x1: "3", y1: "6", x2: "21", y2: "6" }), _jsx("line", { x1: "6", y1: "12", x2: "18", y2: "12" }), _jsx("line", { x1: "4", y1: "18", x2: "20", y2: "18" })] })),
31
+ alignEnd: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("line", { x1: "3", y1: "6", x2: "21", y2: "6" }), _jsx("line", { x1: "9", y1: "12", x2: "21", y2: "12" }), _jsx("line", { x1: "6", y1: "18", x2: "21", y2: "18" })] })),
32
+ alignJustify: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("line", { x1: "3", y1: "6", x2: "21", y2: "6" }), _jsx("line", { x1: "3", y1: "12", x2: "21", y2: "12" }), _jsx("line", { x1: "3", y1: "18", x2: "21", y2: "18" })] })),
33
+ blockquote: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z" }), _jsx("path", { d: "M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z" })] })),
34
+ codeBlock: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), _jsx("polyline", { points: "10 10 7 13 10 16" }), _jsx("polyline", { points: "14 10 17 13 14 16" })] })),
35
+ 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", fill: "currentColor" }), _jsx("circle", { cx: "4", cy: "12", r: "1", fill: "currentColor" }), _jsx("circle", { cx: "4", cy: "18", r: "1", fill: "currentColor" })] })),
36
+ 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" })] })),
37
+ horizontalRule: (_jsx("svg", { ...ICON_PROPS, children: _jsx("line", { x1: "3", y1: "12", x2: "21", y2: "12" }) })),
38
+ clearFormatting: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "M4 7V4h16v3" }), _jsx("line", { x1: "5", y1: "20", x2: "21", y2: "20" }), _jsx("line", { x1: "15", y1: "4", x2: "9", y2: "20" }), _jsx("line", { x1: "3", y1: "3", x2: "21", y2: "21" })] })),
39
+ 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.71-1.71" })] })),
40
+ textColor: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "M5 21h14" }), _jsx("path", { d: "m6 18 6-13 6 13" }), _jsx("path", { d: "M9 13h6" })] })),
41
+ highlight: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "m9 11-6 6v3h3l6-6" }), _jsx("path", { d: "M22 12 12 2l-2 2 10 10z" }), _jsx("path", { d: "m14 6 6 6" })] })),
42
+ attachFiles: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), _jsx("circle", { cx: "9", cy: "9", r: "2" }), _jsx("path", { d: "m21 15-5-5L5 21" })] })),
43
+ table: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), _jsx("line", { x1: "3", y1: "9", x2: "21", y2: "9" }), _jsx("line", { x1: "3", y1: "15", x2: "21", y2: "15" }), _jsx("line", { x1: "9", y1: "3", x2: "9", y2: "21" }), _jsx("line", { x1: "15", y1: "3", x2: "15", y2: "21" })] })),
44
+ tableAddColumnBefore: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "11", y: "3", width: "10", height: "18", rx: "1" }), _jsx("line", { x1: "6", y1: "12", x2: "2", y2: "12" }), _jsx("line", { x1: "4", y1: "10", x2: "4", y2: "14" })] })),
45
+ tableAddColumnAfter: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "3", width: "10", height: "18", rx: "1" }), _jsx("line", { x1: "18", y1: "12", x2: "22", y2: "12" }), _jsx("line", { x1: "20", y1: "10", x2: "20", y2: "14" })] })),
46
+ tableDeleteColumn: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), _jsx("line", { x1: "9", y1: "3", x2: "9", y2: "21" }), _jsx("line", { x1: "15", y1: "3", x2: "15", y2: "21" }), _jsx("line", { x1: "10", y1: "9", x2: "14", y2: "13" }), _jsx("line", { x1: "14", y1: "9", x2: "10", y2: "13" })] })),
47
+ tableAddRowBefore: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "11", width: "18", height: "10", rx: "1" }), _jsx("line", { x1: "12", y1: "6", x2: "12", y2: "2" }), _jsx("line", { x1: "10", y1: "4", x2: "14", y2: "4" })] })),
48
+ tableAddRowAfter: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "10", rx: "1" }), _jsx("line", { x1: "12", y1: "18", x2: "12", y2: "22" }), _jsx("line", { x1: "10", y1: "20", x2: "14", y2: "20" })] })),
49
+ tableDeleteRow: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), _jsx("line", { x1: "3", y1: "9", x2: "21", y2: "9" }), _jsx("line", { x1: "3", y1: "15", x2: "21", y2: "15" }), _jsx("line", { x1: "9", y1: "11", x2: "13", y2: "13" }), _jsx("line", { x1: "13", y1: "11", x2: "9", y2: "13" })] })),
50
+ tableMergeCells: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), _jsx("line", { x1: "3", y1: "12", x2: "9", y2: "12" }), _jsx("line", { x1: "15", y1: "12", x2: "21", y2: "12" }), _jsx("line", { x1: "12", y1: "3", x2: "12", y2: "9" }), _jsx("line", { x1: "12", y1: "15", x2: "12", y2: "21" })] })),
51
+ tableSplitCell: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), _jsx("line", { x1: "3", y1: "12", x2: "21", y2: "12" }), _jsx("line", { x1: "12", y1: "3", x2: "12", y2: "21" })] })),
52
+ tableToggleHeaderRow: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), _jsx("rect", { x: "3", y: "3", width: "18", height: "6", fill: "currentColor", opacity: "0.25", stroke: "none" }), _jsx("line", { x1: "3", y1: "9", x2: "21", y2: "9" }), _jsx("line", { x1: "3", y1: "15", x2: "21", y2: "15" })] })),
53
+ tableToggleHeaderCell: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), _jsx("rect", { x: "3", y: "3", width: "9", height: "6", fill: "currentColor", opacity: "0.25", stroke: "none" }), _jsx("line", { x1: "3", y1: "9", x2: "21", y2: "9" }), _jsx("line", { x1: "3", y1: "15", x2: "21", y2: "15" }), _jsx("line", { x1: "12", y1: "3", x2: "12", y2: "21" })] })),
54
+ tableDelete: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), _jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" }), _jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" })] })),
55
+ undo: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "M3 7v6h6" }), _jsx("path", { d: "M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13" })] })),
56
+ redo: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("path", { d: "M21 7v6h-6" }), _jsx("path", { d: "M3 17a9 9 0 0 1 9-9 9 9 0 0 1 6 2.3L21 13" })] })),
57
+ details: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("polyline", { points: "6 9 12 15 18 9" }), _jsx("line", { x1: "3", y1: "4", x2: "21", y2: "4" }), _jsx("line", { x1: "3", y1: "20", x2: "21", y2: "20" })] })),
58
+ grid: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "4", width: "8", height: "16", rx: "1" }), _jsx("rect", { x: "13", y: "4", width: "8", height: "16", rx: "1" })] })),
59
+ gridDelete: (_jsxs("svg", { ...ICON_PROPS, children: [_jsx("rect", { x: "3", y: "4", width: "8", height: "16", rx: "1" }), _jsx("rect", { x: "13", y: "4", width: "8", height: "16", rx: "1" }), _jsx("line", { x1: "2", y1: "22", x2: "22", y2: "2" })] })),
60
+ };
61
+ /** Lookup table for every recognized button id. */
62
+ export const TOOLBAR_BUTTONS = {
63
+ bold: {
64
+ id: 'bold', label: 'Bold', shortcut: 'B', available: true, icon: Icons.bold,
65
+ isActive: (ed) => ed.isActive('bold'),
66
+ command: (ed) => { ed.chain().focus().toggleBold().run(); },
67
+ },
68
+ italic: {
69
+ id: 'italic', label: 'Italic', shortcut: 'I', available: true, icon: Icons.italic,
70
+ isActive: (ed) => ed.isActive('italic'),
71
+ command: (ed) => { ed.chain().focus().toggleItalic().run(); },
72
+ },
73
+ underline: {
74
+ id: 'underline', label: 'Underline', shortcut: 'U', available: true, icon: Icons.underline,
75
+ isActive: (ed) => ed.isActive('underline'),
76
+ command: (ed) => { ed.chain().focus().toggleUnderline().run(); },
77
+ },
78
+ strike: {
79
+ id: 'strike', label: 'Strikethrough', shortcut: '⇧X', available: true, icon: Icons.strike,
80
+ isActive: (ed) => ed.isActive('strike'),
81
+ command: (ed) => { ed.chain().focus().toggleStrike().run(); },
82
+ },
83
+ subscript: {
84
+ id: 'subscript', label: 'Subscript', shortcut: ',', available: true, icon: Icons.subscript,
85
+ isActive: (ed) => ed.isActive('subscript'),
86
+ command: (ed) => { ed.chain().focus().toggleSubscript().run(); },
87
+ },
88
+ superscript: {
89
+ id: 'superscript', label: 'Superscript', shortcut: '.', available: true, icon: Icons.superscript,
90
+ isActive: (ed) => ed.isActive('superscript'),
91
+ command: (ed) => { ed.chain().focus().toggleSuperscript().run(); },
92
+ },
93
+ code: {
94
+ id: 'code', label: 'Inline code', shortcut: 'E', available: true, icon: Icons.code,
95
+ isActive: (ed) => ed.isActive('code'),
96
+ command: (ed) => { ed.chain().focus().toggleCode().run(); },
97
+ },
98
+ lead: {
99
+ id: 'lead', label: 'Lead', available: true, icon: Icons.lead,
100
+ isActive: (ed) => ed.isActive('lead'),
101
+ // `toggleMark` is the safest call — `chain().toggleLead()` would require
102
+ // the command type to be visible to the chain at compile time, and the
103
+ // marks ship from a sibling extension file the toolbar can't see typewise.
104
+ command: (ed) => { ed.chain().focus().toggleMark('lead').run(); },
105
+ },
106
+ small: {
107
+ id: 'small', label: 'Small', available: true, icon: Icons.small,
108
+ isActive: (ed) => ed.isActive('small'),
109
+ command: (ed) => { ed.chain().focus().toggleMark('small').run(); },
110
+ },
111
+ paragraph: {
112
+ id: 'paragraph', label: 'Paragraph', available: true, icon: Icons.paragraph,
113
+ isActive: (ed) => ed.isActive('paragraph'),
114
+ command: (ed) => { ed.chain().focus().setParagraph().run(); },
115
+ },
116
+ h1: makeHeading(1, Icons.h1),
117
+ h2: makeHeading(2, Icons.h2),
118
+ h3: makeHeading(3, Icons.h3),
119
+ h4: makeHeading(4, Icons.h4),
120
+ h5: makeHeading(5, Icons.h5),
121
+ h6: makeHeading(6, Icons.h6),
122
+ alignStart: makeAlign('left', 'Align left', Icons.alignStart),
123
+ alignCenter: makeAlign('center', 'Align center', Icons.alignCenter),
124
+ alignEnd: makeAlign('right', 'Align right', Icons.alignEnd),
125
+ alignJustify: makeAlign('justify', 'Justify', Icons.alignJustify),
126
+ blockquote: {
127
+ id: 'blockquote', label: 'Quote', available: true, icon: Icons.blockquote,
128
+ isActive: (ed) => ed.isActive('blockquote'),
129
+ command: (ed) => { ed.chain().focus().toggleBlockquote().run(); },
130
+ },
131
+ codeBlock: {
132
+ id: 'codeBlock', label: 'Code block', available: true, icon: Icons.codeBlock,
133
+ isActive: (ed) => ed.isActive('codeBlock'),
134
+ command: (ed) => { ed.chain().focus().toggleCodeBlock().run(); },
135
+ },
136
+ bulletList: {
137
+ id: 'bulletList', label: 'Bullet list', available: true, icon: Icons.bulletList,
138
+ isActive: (ed) => ed.isActive('bulletList'),
139
+ command: (ed) => { ed.chain().focus().toggleBulletList().run(); },
140
+ },
141
+ orderedList: {
142
+ id: 'orderedList', label: 'Numbered list', available: true, icon: Icons.orderedList,
143
+ isActive: (ed) => ed.isActive('orderedList'),
144
+ command: (ed) => { ed.chain().focus().toggleOrderedList().run(); },
145
+ },
146
+ horizontalRule: {
147
+ id: 'horizontalRule', label: 'Divider', available: true, icon: Icons.horizontalRule,
148
+ command: (ed) => { ed.chain().focus().setHorizontalRule().run(); },
149
+ },
150
+ clearFormatting: {
151
+ id: 'clearFormatting', label: 'Clear formatting', available: true, icon: Icons.clearFormatting,
152
+ command: (ed) => { ed.chain().focus().clearNodes().unsetAllMarks().run(); },
153
+ },
154
+ link: {
155
+ id: 'link', label: 'Link', shortcut: 'K', available: true, icon: Icons.link, custom: 'link',
156
+ isActive: (ed) => ed.isActive('link'),
157
+ // Click-handling lives in the toolbar itself — the dialog needs React state.
158
+ command: () => { },
159
+ },
160
+ undo: {
161
+ id: 'undo', label: 'Undo', shortcut: 'Z', available: true, icon: Icons.undo,
162
+ isDisabled: (ed) => !ed.can().undo(),
163
+ command: (ed) => { ed.chain().focus().undo().run(); },
164
+ },
165
+ redo: {
166
+ id: 'redo', label: 'Redo', shortcut: '⇧Z', available: true, icon: Icons.redo,
167
+ isDisabled: (ed) => !ed.can().redo(),
168
+ command: (ed) => { ed.chain().focus().redo().run(); },
169
+ },
170
+ textColor: {
171
+ id: 'textColor', label: 'Text color', available: true, icon: Icons.textColor, custom: 'textColor',
172
+ isActive: (ed) => Boolean(ed.getAttributes('textStyle')['color']),
173
+ command: () => { },
174
+ },
175
+ highlight: {
176
+ id: 'highlight', label: 'Highlight', available: true, icon: Icons.highlight, custom: 'highlight',
177
+ isActive: (ed) => ed.isActive('highlight'),
178
+ command: () => { },
179
+ },
180
+ attachFiles: {
181
+ id: 'attachFiles', label: 'Attach files', available: true, icon: Icons.attachFiles, custom: 'attachFiles',
182
+ // Click-handling lives in the toolbar — opens a Base UI dialog driven
183
+ // by React state (file picker + alt text + upload progress).
184
+ command: () => { },
185
+ },
186
+ // ---- Tables (Phase F). ----------------------------------------------------
187
+ // The "insert table" button is always enabled (creates a table at the cursor);
188
+ // every cell-action button is gated on `inTable` so the user can't run a
189
+ // no-op command outside a table. Tiptap returns `false` from these commands
190
+ // when the cursor is elsewhere — `can()` is the canonical check.
191
+ table: {
192
+ id: 'table', label: 'Insert table', available: true, icon: Icons.table,
193
+ command: (ed) => {
194
+ ed.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run();
195
+ },
196
+ },
197
+ tableAddColumnBefore: {
198
+ id: 'tableAddColumnBefore', label: 'Add column before', available: true, icon: Icons.tableAddColumnBefore,
199
+ isDisabled: (ed) => !ed.can().addColumnBefore(),
200
+ command: (ed) => { ed.chain().focus().addColumnBefore().run(); },
201
+ },
202
+ tableAddColumnAfter: {
203
+ id: 'tableAddColumnAfter', label: 'Add column after', available: true, icon: Icons.tableAddColumnAfter,
204
+ isDisabled: (ed) => !ed.can().addColumnAfter(),
205
+ command: (ed) => { ed.chain().focus().addColumnAfter().run(); },
206
+ },
207
+ tableDeleteColumn: {
208
+ id: 'tableDeleteColumn', label: 'Delete column', available: true, icon: Icons.tableDeleteColumn,
209
+ isDisabled: (ed) => !ed.can().deleteColumn(),
210
+ command: (ed) => { ed.chain().focus().deleteColumn().run(); },
211
+ },
212
+ tableAddRowBefore: {
213
+ id: 'tableAddRowBefore', label: 'Add row before', available: true, icon: Icons.tableAddRowBefore,
214
+ isDisabled: (ed) => !ed.can().addRowBefore(),
215
+ command: (ed) => { ed.chain().focus().addRowBefore().run(); },
216
+ },
217
+ tableAddRowAfter: {
218
+ id: 'tableAddRowAfter', label: 'Add row after', available: true, icon: Icons.tableAddRowAfter,
219
+ isDisabled: (ed) => !ed.can().addRowAfter(),
220
+ command: (ed) => { ed.chain().focus().addRowAfter().run(); },
221
+ },
222
+ tableDeleteRow: {
223
+ id: 'tableDeleteRow', label: 'Delete row', available: true, icon: Icons.tableDeleteRow,
224
+ isDisabled: (ed) => !ed.can().deleteRow(),
225
+ command: (ed) => { ed.chain().focus().deleteRow().run(); },
226
+ },
227
+ tableMergeCells: {
228
+ id: 'tableMergeCells', label: 'Merge cells', available: true, icon: Icons.tableMergeCells,
229
+ isDisabled: (ed) => !ed.can().mergeCells(),
230
+ command: (ed) => { ed.chain().focus().mergeCells().run(); },
231
+ },
232
+ tableSplitCell: {
233
+ id: 'tableSplitCell', label: 'Split cell', available: true, icon: Icons.tableSplitCell,
234
+ isDisabled: (ed) => !ed.can().splitCell(),
235
+ command: (ed) => { ed.chain().focus().splitCell().run(); },
236
+ },
237
+ tableToggleHeaderRow: {
238
+ id: 'tableToggleHeaderRow', label: 'Toggle header row', available: true, icon: Icons.tableToggleHeaderRow,
239
+ isDisabled: (ed) => !ed.can().toggleHeaderRow(),
240
+ command: (ed) => { ed.chain().focus().toggleHeaderRow().run(); },
241
+ },
242
+ tableToggleHeaderCell: {
243
+ id: 'tableToggleHeaderCell', label: 'Toggle header cell', available: true, icon: Icons.tableToggleHeaderCell,
244
+ isDisabled: (ed) => !ed.can().toggleHeaderCell(),
245
+ isActive: (ed) => ed.isActive('tableHeader'),
246
+ command: (ed) => { ed.chain().focus().toggleHeaderCell().run(); },
247
+ },
248
+ tableDelete: {
249
+ id: 'tableDelete', label: 'Delete table', available: true, icon: Icons.tableDelete,
250
+ isDisabled: (ed) => !ed.can().deleteTable(),
251
+ command: (ed) => { ed.chain().focus().deleteTable().run(); },
252
+ },
253
+ // Wraps the current paragraph in a `<details>` node with an empty summary
254
+ // and the existing content as the body. `setDetails` is shipped by the
255
+ // `@tiptap/extension-details` package; `unsetDetails` (toggle off) sits
256
+ // behind a separate slash entry to keep the toolbar surface compact.
257
+ details: {
258
+ id: 'details', label: 'Collapsible block', available: true, icon: Icons.details,
259
+ isActive: (ed) => ed.isActive('details'),
260
+ isDisabled: (ed) => !ed.can().setDetails(),
261
+ command: (ed) => { ed.chain().focus().setDetails().run(); },
262
+ },
263
+ // Multi-column grid layout. Toolbar button defaults to 2 columns — most-
264
+ // common-case UX. Users wanting 3 columns reach for the slash menu's
265
+ // dedicated entry. `gridDelete` unwraps the enclosing grid; greyed out
266
+ // when the cursor isn't inside one.
267
+ grid: {
268
+ id: 'grid', label: '2-column grid', available: true, icon: Icons.grid,
269
+ isActive: () => false,
270
+ isDisabled: (ed) => !ed.can().setGrid({ columns: 2 }),
271
+ command: (ed) => { ed.chain().focus().setGrid({ columns: 2 }).run(); },
272
+ },
273
+ gridDelete: {
274
+ id: 'gridDelete', label: 'Remove grid', available: true, icon: Icons.gridDelete,
275
+ isActive: (ed) => ed.isActive('grid'),
276
+ isDisabled: (ed) => !ed.can().unsetGrid(),
277
+ command: (ed) => { ed.chain().focus().unsetGrid().run(); },
278
+ },
279
+ };
280
+ function makeHeading(level, icon) {
281
+ return {
282
+ id: `h${level}`,
283
+ label: `Heading ${level}`,
284
+ available: true,
285
+ icon,
286
+ isActive: (ed) => ed.isActive('heading', { level }),
287
+ command: (ed) => { ed.chain().focus().toggleHeading({ level }).run(); },
288
+ };
289
+ }
290
+ function makeAlign(value, label, icon) {
291
+ return {
292
+ id: `align${value === 'left' ? 'Start' : value === 'right' ? 'End' : value.charAt(0).toUpperCase() + value.slice(1)}`,
293
+ label,
294
+ available: true,
295
+ icon,
296
+ isActive: (ed) => ed.isActive({ textAlign: value }),
297
+ command: (ed) => { ed.chain().focus().setTextAlign(value).run(); },
298
+ };
299
+ }
300
+ //# sourceMappingURL=toolbarButtons.js.map