@pilotiq/tiptap 3.10.4 → 3.10.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +745 -0
- package/boost/guidelines.md +268 -0
- package/boost/skills/pilotiq-tiptap-blocks/SKILL.md +48 -0
- package/boost/skills/pilotiq-tiptap-blocks/rules/custom-blocks.md +90 -0
- package/boost/skills/pilotiq-tiptap-blocks/rules/slash-menu-and-mentions.md +101 -0
- package/boost/skills/pilotiq-tiptap-blocks/rules/toolbar-and-extensibility.md +161 -0
- package/dist/react/CollabTextRenderer.d.ts.map +1 -1
- package/dist/react/CollabTextRenderer.js +4 -4
- package/dist/react/CollabTextRenderer.js.map +1 -1
- package/dist/react/MarkdownEditor.d.ts.map +1 -1
- package/dist/react/MarkdownEditor.js +4 -5
- package/dist/react/MarkdownEditor.js.map +1 -1
- package/dist/react/TiptapEditor.d.ts.map +1 -1
- package/dist/react/TiptapEditor.js +8 -7
- package/dist/react/TiptapEditor.js.map +1 -1
- package/package.json +6 -3
- package/dist/collabShapes.d.ts +0 -22
- package/dist/collabShapes.d.ts.map +0 -1
- package/dist/collabShapes.js +0 -2
- package/dist/collabShapes.js.map +0 -1
- package/src/Block.ts +0 -75
- package/src/MentionProvider.ts +0 -153
- package/src/PlainTextEditor.dom.test.ts +0 -111
- package/src/PlainTextEditor.test.ts +0 -158
- package/src/PlainTextEditor.ts +0 -229
- package/src/RichTextField.test.ts +0 -447
- package/src/RichTextField.ts +0 -508
- package/src/collabShapes.ts +0 -22
- package/src/extensions/AiInlineDiffExtension.ts +0 -286
- package/src/extensions/AiSuggestionExtension.test.ts +0 -141
- package/src/extensions/AiSuggestionExtension.ts +0 -522
- package/src/extensions/BlockNodeExtension.ts +0 -134
- package/src/extensions/DragHandleExtension.ts +0 -184
- package/src/extensions/GridExtension.test.ts +0 -31
- package/src/extensions/GridExtension.ts +0 -138
- package/src/extensions/MentionExtension.ts +0 -248
- package/src/extensions/MergeTagExtension.ts +0 -75
- package/src/extensions/SlashCommandExtension.test.ts +0 -147
- package/src/extensions/SlashCommandExtension.ts +0 -332
- package/src/extensions/TextSizeMarks.ts +0 -73
- package/src/index.ts +0 -62
- package/src/markdownExtension.ts +0 -19
- package/src/markdownStorage.ts +0 -49
- package/src/plugin.test.ts +0 -19
- package/src/plugin.ts +0 -26
- package/src/react/AiSuggestionBanner.tsx +0 -185
- package/src/react/BlockNodeView.tsx +0 -99
- package/src/react/BlockSidePanel.dom.test.tsx +0 -38
- package/src/react/BlockSidePanel.test.ts +0 -412
- package/src/react/BlockSidePanel.tsx +0 -451
- package/src/react/CollabTextRenderer.tsx +0 -230
- package/src/react/FloatingToolbar.tsx +0 -304
- package/src/react/MarkdownEditor.tsx +0 -606
- package/src/react/MentionMenu.tsx +0 -120
- package/src/react/Palette.tsx +0 -86
- package/src/react/SlashMenu.tsx +0 -129
- package/src/react/TableFloatingToolbar.tsx +0 -154
- package/src/react/TiptapEditor.dom.test.tsx +0 -112
- package/src/react/TiptapEditor.tsx +0 -776
- package/src/react/Toolbar.tsx +0 -438
- package/src/react/toolbarButtons.tsx +0 -579
- package/src/react/useAiInlineDiff.ts +0 -342
- package/src/react/useAiSuggestionBridge.ts +0 -223
- package/src/register.test.ts +0 -14
- package/src/register.ts +0 -42
- package/src/render.test.ts +0 -745
- package/src/render.ts +0 -480
- package/src/surgicalOps.ts +0 -205
- package/src/test/setup.ts +0 -64
|
@@ -1,579 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from 'react'
|
|
2
|
-
import type { Editor } from '@tiptap/core'
|
|
3
|
-
import type { ToolbarButtonId } from '../RichTextField.js'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* One toolbar button's behavior. The renderer wires `<button>` chrome — this
|
|
7
|
-
* descriptor only knows the editor command, the active-state predicate, and
|
|
8
|
-
* the icon.
|
|
9
|
-
*
|
|
10
|
-
* Buttons that depend on extensions registered in later phases (textColor,
|
|
11
|
-
* highlight, attachFiles, table*) are registered with `available: false`
|
|
12
|
-
* until that phase ships, so config that targets them today is silently
|
|
13
|
-
* dropped instead of crashing.
|
|
14
|
-
*/
|
|
15
|
-
export interface ToolbarButtonDef {
|
|
16
|
-
id: ToolbarButtonId
|
|
17
|
-
label: string
|
|
18
|
-
shortcut?: string
|
|
19
|
-
icon: ReactNode
|
|
20
|
-
/** Phase-A buttons set this to `true`; later-phase placeholders set `false`. */
|
|
21
|
-
available: boolean
|
|
22
|
-
/** Whether the button reflects the editor's "currently active" state. */
|
|
23
|
-
isActive?: (editor: Editor) => boolean
|
|
24
|
-
/** Whether the button is disabled given the current selection. */
|
|
25
|
-
isDisabled?: (editor: Editor) => boolean
|
|
26
|
-
/** Run on click. */
|
|
27
|
-
command: (editor: Editor) => void
|
|
28
|
-
/**
|
|
29
|
-
* Optional opt-in for buttons whose behavior is "open a UI" rather than
|
|
30
|
-
* fire a chain — the editor surface keeps its own state for these and the
|
|
31
|
-
* default click handler doesn't run a chain.
|
|
32
|
-
*/
|
|
33
|
-
custom?: 'link' | 'textColor' | 'highlight' | 'attachFiles'
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const ICON_PROPS = {
|
|
37
|
-
width: 14, height: 14, viewBox: '0 0 24 24',
|
|
38
|
-
fill: 'none', stroke: 'currentColor',
|
|
39
|
-
strokeWidth: 2, strokeLinecap: 'round' as const, strokeLinejoin: 'round' as const,
|
|
40
|
-
'aria-hidden': 'true' as const,
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Inline SVGs (lucide.dev paths). Kept inline so the package doesn't pull
|
|
44
|
-
// `lucide-react` as a peer dep.
|
|
45
|
-
const Icons = {
|
|
46
|
-
bold: (
|
|
47
|
-
<svg {...ICON_PROPS} strokeWidth={2.25}>
|
|
48
|
-
<path d="M6 12h9a4 4 0 0 1 0 8H6Z" />
|
|
49
|
-
<path d="M6 4h7a4 4 0 0 1 0 8H6Z" />
|
|
50
|
-
</svg>
|
|
51
|
-
),
|
|
52
|
-
italic: (
|
|
53
|
-
<svg {...ICON_PROPS}>
|
|
54
|
-
<line x1="19" y1="4" x2="10" y2="4" />
|
|
55
|
-
<line x1="14" y1="20" x2="5" y2="20" />
|
|
56
|
-
<line x1="15" y1="4" x2="9" y2="20" />
|
|
57
|
-
</svg>
|
|
58
|
-
),
|
|
59
|
-
underline: (
|
|
60
|
-
<svg {...ICON_PROPS}>
|
|
61
|
-
<path d="M6 4v6a6 6 0 0 0 12 0V4" />
|
|
62
|
-
<line x1="4" y1="20" x2="20" y2="20" />
|
|
63
|
-
</svg>
|
|
64
|
-
),
|
|
65
|
-
strike: (
|
|
66
|
-
<svg {...ICON_PROPS}>
|
|
67
|
-
<path d="M16 4H9a3 3 0 0 0-2.83 4" />
|
|
68
|
-
<path d="M14 12a4 4 0 0 1 0 8H6" />
|
|
69
|
-
<line x1="4" y1="12" x2="20" y2="12" />
|
|
70
|
-
</svg>
|
|
71
|
-
),
|
|
72
|
-
subscript: (
|
|
73
|
-
<svg {...ICON_PROPS}>
|
|
74
|
-
<path d="m4 5 8 10" />
|
|
75
|
-
<path d="m12 5-8 10" />
|
|
76
|
-
<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" />
|
|
77
|
-
</svg>
|
|
78
|
-
),
|
|
79
|
-
superscript: (
|
|
80
|
-
<svg {...ICON_PROPS}>
|
|
81
|
-
<path d="m4 19 8-10" />
|
|
82
|
-
<path d="m12 19-8-10" />
|
|
83
|
-
<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" />
|
|
84
|
-
</svg>
|
|
85
|
-
),
|
|
86
|
-
code: (
|
|
87
|
-
<svg {...ICON_PROPS}>
|
|
88
|
-
<polyline points="16 18 22 12 16 6" />
|
|
89
|
-
<polyline points="8 6 2 12 8 18" />
|
|
90
|
-
</svg>
|
|
91
|
-
),
|
|
92
|
-
paragraph: (
|
|
93
|
-
<svg {...ICON_PROPS}>
|
|
94
|
-
<path d="M13 4v16" />
|
|
95
|
-
<path d="M17 4v16" />
|
|
96
|
-
<path d="M19 4H9.5a4.5 4.5 0 0 0 0 9H13" />
|
|
97
|
-
</svg>
|
|
98
|
-
),
|
|
99
|
-
h1: <span className="text-xs font-semibold leading-none">H1</span>,
|
|
100
|
-
h2: <span className="text-xs font-semibold leading-none">H2</span>,
|
|
101
|
-
h3: <span className="text-xs font-semibold leading-none">H3</span>,
|
|
102
|
-
h4: <span className="text-xs font-semibold leading-none">H4</span>,
|
|
103
|
-
h5: <span className="text-xs font-semibold leading-none">H5</span>,
|
|
104
|
-
h6: <span className="text-xs font-semibold leading-none">H6</span>,
|
|
105
|
-
// Sized text-string glyphs match the visual contrast of the marks they
|
|
106
|
-
// toggle — lead is the larger of the pair, small is the slightly smaller.
|
|
107
|
-
lead: <span className="text-sm font-semibold leading-none tracking-tight">P+</span>,
|
|
108
|
-
small: <span className="text-[10px] font-semibold leading-none tracking-tight">P-</span>,
|
|
109
|
-
alignStart: (
|
|
110
|
-
<svg {...ICON_PROPS}>
|
|
111
|
-
<line x1="3" y1="6" x2="21" y2="6" />
|
|
112
|
-
<line x1="3" y1="12" x2="15" y2="12" />
|
|
113
|
-
<line x1="3" y1="18" x2="18" y2="18" />
|
|
114
|
-
</svg>
|
|
115
|
-
),
|
|
116
|
-
alignCenter: (
|
|
117
|
-
<svg {...ICON_PROPS}>
|
|
118
|
-
<line x1="3" y1="6" x2="21" y2="6" />
|
|
119
|
-
<line x1="6" y1="12" x2="18" y2="12" />
|
|
120
|
-
<line x1="4" y1="18" x2="20" y2="18" />
|
|
121
|
-
</svg>
|
|
122
|
-
),
|
|
123
|
-
alignEnd: (
|
|
124
|
-
<svg {...ICON_PROPS}>
|
|
125
|
-
<line x1="3" y1="6" x2="21" y2="6" />
|
|
126
|
-
<line x1="9" y1="12" x2="21" y2="12" />
|
|
127
|
-
<line x1="6" y1="18" x2="21" y2="18" />
|
|
128
|
-
</svg>
|
|
129
|
-
),
|
|
130
|
-
alignJustify: (
|
|
131
|
-
<svg {...ICON_PROPS}>
|
|
132
|
-
<line x1="3" y1="6" x2="21" y2="6" />
|
|
133
|
-
<line x1="3" y1="12" x2="21" y2="12" />
|
|
134
|
-
<line x1="3" y1="18" x2="21" y2="18" />
|
|
135
|
-
</svg>
|
|
136
|
-
),
|
|
137
|
-
blockquote: (
|
|
138
|
-
<svg {...ICON_PROPS}>
|
|
139
|
-
<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" />
|
|
140
|
-
<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" />
|
|
141
|
-
</svg>
|
|
142
|
-
),
|
|
143
|
-
codeBlock: (
|
|
144
|
-
<svg {...ICON_PROPS}>
|
|
145
|
-
<rect x="3" y="3" width="18" height="18" rx="2" />
|
|
146
|
-
<polyline points="10 10 7 13 10 16" />
|
|
147
|
-
<polyline points="14 10 17 13 14 16" />
|
|
148
|
-
</svg>
|
|
149
|
-
),
|
|
150
|
-
bulletList: (
|
|
151
|
-
<svg {...ICON_PROPS}>
|
|
152
|
-
<line x1="8" y1="6" x2="21" y2="6" />
|
|
153
|
-
<line x1="8" y1="12" x2="21" y2="12" />
|
|
154
|
-
<line x1="8" y1="18" x2="21" y2="18" />
|
|
155
|
-
<circle cx="4" cy="6" r="1" fill="currentColor" />
|
|
156
|
-
<circle cx="4" cy="12" r="1" fill="currentColor" />
|
|
157
|
-
<circle cx="4" cy="18" r="1" fill="currentColor" />
|
|
158
|
-
</svg>
|
|
159
|
-
),
|
|
160
|
-
orderedList: (
|
|
161
|
-
<svg {...ICON_PROPS}>
|
|
162
|
-
<line x1="10" y1="6" x2="21" y2="6" />
|
|
163
|
-
<line x1="10" y1="12" x2="21" y2="12" />
|
|
164
|
-
<line x1="10" y1="18" x2="21" y2="18" />
|
|
165
|
-
<path d="M4 6h1v4" />
|
|
166
|
-
<path d="M4 10h2" />
|
|
167
|
-
<path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1" />
|
|
168
|
-
</svg>
|
|
169
|
-
),
|
|
170
|
-
horizontalRule: (
|
|
171
|
-
<svg {...ICON_PROPS}>
|
|
172
|
-
<line x1="3" y1="12" x2="21" y2="12" />
|
|
173
|
-
</svg>
|
|
174
|
-
),
|
|
175
|
-
clearFormatting: (
|
|
176
|
-
<svg {...ICON_PROPS}>
|
|
177
|
-
<path d="M4 7V4h16v3" />
|
|
178
|
-
<line x1="5" y1="20" x2="21" y2="20" />
|
|
179
|
-
<line x1="15" y1="4" x2="9" y2="20" />
|
|
180
|
-
<line x1="3" y1="3" x2="21" y2="21" />
|
|
181
|
-
</svg>
|
|
182
|
-
),
|
|
183
|
-
link: (
|
|
184
|
-
<svg {...ICON_PROPS}>
|
|
185
|
-
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
|
|
186
|
-
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
|
|
187
|
-
</svg>
|
|
188
|
-
),
|
|
189
|
-
textColor: (
|
|
190
|
-
<svg {...ICON_PROPS}>
|
|
191
|
-
<path d="M5 21h14" />
|
|
192
|
-
<path d="m6 18 6-13 6 13" />
|
|
193
|
-
<path d="M9 13h6" />
|
|
194
|
-
</svg>
|
|
195
|
-
),
|
|
196
|
-
highlight: (
|
|
197
|
-
<svg {...ICON_PROPS}>
|
|
198
|
-
<path d="m9 11-6 6v3h3l6-6" />
|
|
199
|
-
<path d="M22 12 12 2l-2 2 10 10z" />
|
|
200
|
-
<path d="m14 6 6 6" />
|
|
201
|
-
</svg>
|
|
202
|
-
),
|
|
203
|
-
attachFiles: (
|
|
204
|
-
<svg {...ICON_PROPS}>
|
|
205
|
-
<rect x="3" y="3" width="18" height="18" rx="2" />
|
|
206
|
-
<circle cx="9" cy="9" r="2" />
|
|
207
|
-
<path d="m21 15-5-5L5 21" />
|
|
208
|
-
</svg>
|
|
209
|
-
),
|
|
210
|
-
table: (
|
|
211
|
-
<svg {...ICON_PROPS}>
|
|
212
|
-
<rect x="3" y="3" width="18" height="18" rx="2" />
|
|
213
|
-
<line x1="3" y1="9" x2="21" y2="9" />
|
|
214
|
-
<line x1="3" y1="15" x2="21" y2="15" />
|
|
215
|
-
<line x1="9" y1="3" x2="9" y2="21" />
|
|
216
|
-
<line x1="15" y1="3" x2="15" y2="21" />
|
|
217
|
-
</svg>
|
|
218
|
-
),
|
|
219
|
-
tableAddColumnBefore: (
|
|
220
|
-
<svg {...ICON_PROPS}>
|
|
221
|
-
<rect x="11" y="3" width="10" height="18" rx="1" />
|
|
222
|
-
<line x1="6" y1="12" x2="2" y2="12" />
|
|
223
|
-
<line x1="4" y1="10" x2="4" y2="14" />
|
|
224
|
-
</svg>
|
|
225
|
-
),
|
|
226
|
-
tableAddColumnAfter: (
|
|
227
|
-
<svg {...ICON_PROPS}>
|
|
228
|
-
<rect x="3" y="3" width="10" height="18" rx="1" />
|
|
229
|
-
<line x1="18" y1="12" x2="22" y2="12" />
|
|
230
|
-
<line x1="20" y1="10" x2="20" y2="14" />
|
|
231
|
-
</svg>
|
|
232
|
-
),
|
|
233
|
-
tableDeleteColumn: (
|
|
234
|
-
<svg {...ICON_PROPS}>
|
|
235
|
-
<rect x="3" y="3" width="18" height="18" rx="2" />
|
|
236
|
-
<line x1="9" y1="3" x2="9" y2="21" />
|
|
237
|
-
<line x1="15" y1="3" x2="15" y2="21" />
|
|
238
|
-
<line x1="10" y1="9" x2="14" y2="13" />
|
|
239
|
-
<line x1="14" y1="9" x2="10" y2="13" />
|
|
240
|
-
</svg>
|
|
241
|
-
),
|
|
242
|
-
tableAddRowBefore: (
|
|
243
|
-
<svg {...ICON_PROPS}>
|
|
244
|
-
<rect x="3" y="11" width="18" height="10" rx="1" />
|
|
245
|
-
<line x1="12" y1="6" x2="12" y2="2" />
|
|
246
|
-
<line x1="10" y1="4" x2="14" y2="4" />
|
|
247
|
-
</svg>
|
|
248
|
-
),
|
|
249
|
-
tableAddRowAfter: (
|
|
250
|
-
<svg {...ICON_PROPS}>
|
|
251
|
-
<rect x="3" y="3" width="18" height="10" rx="1" />
|
|
252
|
-
<line x1="12" y1="18" x2="12" y2="22" />
|
|
253
|
-
<line x1="10" y1="20" x2="14" y2="20" />
|
|
254
|
-
</svg>
|
|
255
|
-
),
|
|
256
|
-
tableDeleteRow: (
|
|
257
|
-
<svg {...ICON_PROPS}>
|
|
258
|
-
<rect x="3" y="3" width="18" height="18" rx="2" />
|
|
259
|
-
<line x1="3" y1="9" x2="21" y2="9" />
|
|
260
|
-
<line x1="3" y1="15" x2="21" y2="15" />
|
|
261
|
-
<line x1="9" y1="11" x2="13" y2="13" />
|
|
262
|
-
<line x1="13" y1="11" x2="9" y2="13" />
|
|
263
|
-
</svg>
|
|
264
|
-
),
|
|
265
|
-
tableMergeCells: (
|
|
266
|
-
<svg {...ICON_PROPS}>
|
|
267
|
-
<rect x="3" y="3" width="18" height="18" rx="2" />
|
|
268
|
-
<line x1="3" y1="12" x2="9" y2="12" />
|
|
269
|
-
<line x1="15" y1="12" x2="21" y2="12" />
|
|
270
|
-
<line x1="12" y1="3" x2="12" y2="9" />
|
|
271
|
-
<line x1="12" y1="15" x2="12" y2="21" />
|
|
272
|
-
</svg>
|
|
273
|
-
),
|
|
274
|
-
tableSplitCell: (
|
|
275
|
-
<svg {...ICON_PROPS}>
|
|
276
|
-
<rect x="3" y="3" width="18" height="18" rx="2" />
|
|
277
|
-
<line x1="3" y1="12" x2="21" y2="12" />
|
|
278
|
-
<line x1="12" y1="3" x2="12" y2="21" />
|
|
279
|
-
</svg>
|
|
280
|
-
),
|
|
281
|
-
tableToggleHeaderRow: (
|
|
282
|
-
<svg {...ICON_PROPS}>
|
|
283
|
-
<rect x="3" y="3" width="18" height="18" rx="2" />
|
|
284
|
-
<rect x="3" y="3" width="18" height="6" fill="currentColor" opacity="0.25" stroke="none" />
|
|
285
|
-
<line x1="3" y1="9" x2="21" y2="9" />
|
|
286
|
-
<line x1="3" y1="15" x2="21" y2="15" />
|
|
287
|
-
</svg>
|
|
288
|
-
),
|
|
289
|
-
tableToggleHeaderCell: (
|
|
290
|
-
<svg {...ICON_PROPS}>
|
|
291
|
-
<rect x="3" y="3" width="18" height="18" rx="2" />
|
|
292
|
-
<rect x="3" y="3" width="9" height="6" fill="currentColor" opacity="0.25" stroke="none" />
|
|
293
|
-
<line x1="3" y1="9" x2="21" y2="9" />
|
|
294
|
-
<line x1="3" y1="15" x2="21" y2="15" />
|
|
295
|
-
<line x1="12" y1="3" x2="12" y2="21" />
|
|
296
|
-
</svg>
|
|
297
|
-
),
|
|
298
|
-
tableDelete: (
|
|
299
|
-
<svg {...ICON_PROPS}>
|
|
300
|
-
<rect x="3" y="3" width="18" height="18" rx="2" />
|
|
301
|
-
<line x1="6" y1="6" x2="18" y2="18" />
|
|
302
|
-
<line x1="18" y1="6" x2="6" y2="18" />
|
|
303
|
-
</svg>
|
|
304
|
-
),
|
|
305
|
-
undo: (
|
|
306
|
-
<svg {...ICON_PROPS}>
|
|
307
|
-
<path d="M3 7v6h6" />
|
|
308
|
-
<path d="M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13" />
|
|
309
|
-
</svg>
|
|
310
|
-
),
|
|
311
|
-
redo: (
|
|
312
|
-
<svg {...ICON_PROPS}>
|
|
313
|
-
<path d="M21 7v6h-6" />
|
|
314
|
-
<path d="M3 17a9 9 0 0 1 9-9 9 9 0 0 1 6 2.3L21 13" />
|
|
315
|
-
</svg>
|
|
316
|
-
),
|
|
317
|
-
details: (
|
|
318
|
-
<svg {...ICON_PROPS}>
|
|
319
|
-
<polyline points="6 9 12 15 18 9" />
|
|
320
|
-
<line x1="3" y1="4" x2="21" y2="4" />
|
|
321
|
-
<line x1="3" y1="20" x2="21" y2="20" />
|
|
322
|
-
</svg>
|
|
323
|
-
),
|
|
324
|
-
grid: (
|
|
325
|
-
<svg {...ICON_PROPS}>
|
|
326
|
-
<rect x="3" y="4" width="8" height="16" rx="1" />
|
|
327
|
-
<rect x="13" y="4" width="8" height="16" rx="1" />
|
|
328
|
-
</svg>
|
|
329
|
-
),
|
|
330
|
-
gridDelete: (
|
|
331
|
-
<svg {...ICON_PROPS}>
|
|
332
|
-
<rect x="3" y="4" width="8" height="16" rx="1" />
|
|
333
|
-
<rect x="13" y="4" width="8" height="16" rx="1" />
|
|
334
|
-
<line x1="2" y1="22" x2="22" y2="2" />
|
|
335
|
-
</svg>
|
|
336
|
-
),
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
/** Lookup table for every recognized button id. */
|
|
340
|
-
export const TOOLBAR_BUTTONS: Record<ToolbarButtonId, ToolbarButtonDef> = {
|
|
341
|
-
bold: {
|
|
342
|
-
id: 'bold', label: 'Bold', shortcut: 'B', available: true, icon: Icons.bold,
|
|
343
|
-
isActive: (ed) => ed.isActive('bold'),
|
|
344
|
-
command: (ed) => { ed.chain().focus().toggleBold().run() },
|
|
345
|
-
},
|
|
346
|
-
italic: {
|
|
347
|
-
id: 'italic', label: 'Italic', shortcut: 'I', available: true, icon: Icons.italic,
|
|
348
|
-
isActive: (ed) => ed.isActive('italic'),
|
|
349
|
-
command: (ed) => { ed.chain().focus().toggleItalic().run() },
|
|
350
|
-
},
|
|
351
|
-
underline: {
|
|
352
|
-
id: 'underline', label: 'Underline', shortcut: 'U', available: true, icon: Icons.underline,
|
|
353
|
-
isActive: (ed) => ed.isActive('underline'),
|
|
354
|
-
command: (ed) => { ed.chain().focus().toggleUnderline().run() },
|
|
355
|
-
},
|
|
356
|
-
strike: {
|
|
357
|
-
id: 'strike', label: 'Strikethrough', shortcut: '⇧X', available: true, icon: Icons.strike,
|
|
358
|
-
isActive: (ed) => ed.isActive('strike'),
|
|
359
|
-
command: (ed) => { ed.chain().focus().toggleStrike().run() },
|
|
360
|
-
},
|
|
361
|
-
subscript: {
|
|
362
|
-
id: 'subscript', label: 'Subscript', shortcut: ',', available: true, icon: Icons.subscript,
|
|
363
|
-
isActive: (ed) => ed.isActive('subscript'),
|
|
364
|
-
command: (ed) => { ed.chain().focus().toggleSubscript().run() },
|
|
365
|
-
},
|
|
366
|
-
superscript: {
|
|
367
|
-
id: 'superscript', label: 'Superscript', shortcut: '.', available: true, icon: Icons.superscript,
|
|
368
|
-
isActive: (ed) => ed.isActive('superscript'),
|
|
369
|
-
command: (ed) => { ed.chain().focus().toggleSuperscript().run() },
|
|
370
|
-
},
|
|
371
|
-
code: {
|
|
372
|
-
id: 'code', label: 'Inline code', shortcut: 'E', available: true, icon: Icons.code,
|
|
373
|
-
isActive: (ed) => ed.isActive('code'),
|
|
374
|
-
command: (ed) => { ed.chain().focus().toggleCode().run() },
|
|
375
|
-
},
|
|
376
|
-
lead: {
|
|
377
|
-
id: 'lead', label: 'Lead', available: true, icon: Icons.lead,
|
|
378
|
-
isActive: (ed) => ed.isActive('lead'),
|
|
379
|
-
// `toggleMark` is the safest call — `chain().toggleLead()` would require
|
|
380
|
-
// the command type to be visible to the chain at compile time, and the
|
|
381
|
-
// marks ship from a sibling extension file the toolbar can't see typewise.
|
|
382
|
-
command: (ed) => { ed.chain().focus().toggleMark('lead').run() },
|
|
383
|
-
},
|
|
384
|
-
small: {
|
|
385
|
-
id: 'small', label: 'Small', available: true, icon: Icons.small,
|
|
386
|
-
isActive: (ed) => ed.isActive('small'),
|
|
387
|
-
command: (ed) => { ed.chain().focus().toggleMark('small').run() },
|
|
388
|
-
},
|
|
389
|
-
paragraph: {
|
|
390
|
-
id: 'paragraph', label: 'Paragraph', available: true, icon: Icons.paragraph,
|
|
391
|
-
isActive: (ed) => ed.isActive('paragraph'),
|
|
392
|
-
command: (ed) => { ed.chain().focus().setParagraph().run() },
|
|
393
|
-
},
|
|
394
|
-
h1: makeHeading(1, Icons.h1),
|
|
395
|
-
h2: makeHeading(2, Icons.h2),
|
|
396
|
-
h3: makeHeading(3, Icons.h3),
|
|
397
|
-
h4: makeHeading(4, Icons.h4),
|
|
398
|
-
h5: makeHeading(5, Icons.h5),
|
|
399
|
-
h6: makeHeading(6, Icons.h6),
|
|
400
|
-
alignStart: makeAlign('left', 'Align left', Icons.alignStart),
|
|
401
|
-
alignCenter: makeAlign('center', 'Align center', Icons.alignCenter),
|
|
402
|
-
alignEnd: makeAlign('right', 'Align right', Icons.alignEnd),
|
|
403
|
-
alignJustify: makeAlign('justify','Justify', Icons.alignJustify),
|
|
404
|
-
blockquote: {
|
|
405
|
-
id: 'blockquote', label: 'Quote', available: true, icon: Icons.blockquote,
|
|
406
|
-
isActive: (ed) => ed.isActive('blockquote'),
|
|
407
|
-
command: (ed) => { ed.chain().focus().toggleBlockquote().run() },
|
|
408
|
-
},
|
|
409
|
-
codeBlock: {
|
|
410
|
-
id: 'codeBlock', label: 'Code block', available: true, icon: Icons.codeBlock,
|
|
411
|
-
isActive: (ed) => ed.isActive('codeBlock'),
|
|
412
|
-
command: (ed) => { ed.chain().focus().toggleCodeBlock().run() },
|
|
413
|
-
},
|
|
414
|
-
bulletList: {
|
|
415
|
-
id: 'bulletList', label: 'Bullet list', available: true, icon: Icons.bulletList,
|
|
416
|
-
isActive: (ed) => ed.isActive('bulletList'),
|
|
417
|
-
command: (ed) => { ed.chain().focus().toggleBulletList().run() },
|
|
418
|
-
},
|
|
419
|
-
orderedList: {
|
|
420
|
-
id: 'orderedList', label: 'Numbered list', available: true, icon: Icons.orderedList,
|
|
421
|
-
isActive: (ed) => ed.isActive('orderedList'),
|
|
422
|
-
command: (ed) => { ed.chain().focus().toggleOrderedList().run() },
|
|
423
|
-
},
|
|
424
|
-
horizontalRule: {
|
|
425
|
-
id: 'horizontalRule', label: 'Divider', available: true, icon: Icons.horizontalRule,
|
|
426
|
-
command: (ed) => { ed.chain().focus().setHorizontalRule().run() },
|
|
427
|
-
},
|
|
428
|
-
clearFormatting: {
|
|
429
|
-
id: 'clearFormatting', label: 'Clear formatting', available: true, icon: Icons.clearFormatting,
|
|
430
|
-
command: (ed) => { ed.chain().focus().clearNodes().unsetAllMarks().run() },
|
|
431
|
-
},
|
|
432
|
-
link: {
|
|
433
|
-
id: 'link', label: 'Link', shortcut: 'K', available: true, icon: Icons.link, custom: 'link',
|
|
434
|
-
isActive: (ed) => ed.isActive('link'),
|
|
435
|
-
// Click-handling lives in the toolbar itself — the dialog needs React state.
|
|
436
|
-
command: () => {},
|
|
437
|
-
},
|
|
438
|
-
undo: {
|
|
439
|
-
id: 'undo', label: 'Undo', shortcut: 'Z', available: true, icon: Icons.undo,
|
|
440
|
-
isDisabled: (ed) => !ed.can().undo(),
|
|
441
|
-
command: (ed) => { ed.chain().focus().undo().run() },
|
|
442
|
-
},
|
|
443
|
-
redo: {
|
|
444
|
-
id: 'redo', label: 'Redo', shortcut: '⇧Z', available: true, icon: Icons.redo,
|
|
445
|
-
isDisabled: (ed) => !ed.can().redo(),
|
|
446
|
-
command: (ed) => { ed.chain().focus().redo().run() },
|
|
447
|
-
},
|
|
448
|
-
textColor: {
|
|
449
|
-
id: 'textColor', label: 'Text color', available: true, icon: Icons.textColor, custom: 'textColor',
|
|
450
|
-
isActive: (ed) => Boolean(ed.getAttributes('textStyle')['color']),
|
|
451
|
-
command: () => {},
|
|
452
|
-
},
|
|
453
|
-
highlight: {
|
|
454
|
-
id: 'highlight', label: 'Highlight', available: true, icon: Icons.highlight, custom: 'highlight',
|
|
455
|
-
isActive: (ed) => ed.isActive('highlight'),
|
|
456
|
-
command: () => {},
|
|
457
|
-
},
|
|
458
|
-
attachFiles: {
|
|
459
|
-
id: 'attachFiles', label: 'Attach files', available: true, icon: Icons.attachFiles, custom: 'attachFiles',
|
|
460
|
-
// Click-handling lives in the toolbar — opens a Base UI dialog driven
|
|
461
|
-
// by React state (file picker + alt text + upload progress).
|
|
462
|
-
command: () => {},
|
|
463
|
-
},
|
|
464
|
-
// ---- Tables (Phase F). ----------------------------------------------------
|
|
465
|
-
// The "insert table" button is always enabled (creates a table at the cursor);
|
|
466
|
-
// every cell-action button is gated on `inTable` so the user can't run a
|
|
467
|
-
// no-op command outside a table. Tiptap returns `false` from these commands
|
|
468
|
-
// when the cursor is elsewhere — `can()` is the canonical check.
|
|
469
|
-
table: {
|
|
470
|
-
id: 'table', label: 'Insert table', available: true, icon: Icons.table,
|
|
471
|
-
command: (ed) => {
|
|
472
|
-
ed.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run()
|
|
473
|
-
},
|
|
474
|
-
},
|
|
475
|
-
tableAddColumnBefore: {
|
|
476
|
-
id: 'tableAddColumnBefore', label: 'Add column before', available: true, icon: Icons.tableAddColumnBefore,
|
|
477
|
-
isDisabled: (ed) => !ed.can().addColumnBefore(),
|
|
478
|
-
command: (ed) => { ed.chain().focus().addColumnBefore().run() },
|
|
479
|
-
},
|
|
480
|
-
tableAddColumnAfter: {
|
|
481
|
-
id: 'tableAddColumnAfter', label: 'Add column after', available: true, icon: Icons.tableAddColumnAfter,
|
|
482
|
-
isDisabled: (ed) => !ed.can().addColumnAfter(),
|
|
483
|
-
command: (ed) => { ed.chain().focus().addColumnAfter().run() },
|
|
484
|
-
},
|
|
485
|
-
tableDeleteColumn: {
|
|
486
|
-
id: 'tableDeleteColumn', label: 'Delete column', available: true, icon: Icons.tableDeleteColumn,
|
|
487
|
-
isDisabled: (ed) => !ed.can().deleteColumn(),
|
|
488
|
-
command: (ed) => { ed.chain().focus().deleteColumn().run() },
|
|
489
|
-
},
|
|
490
|
-
tableAddRowBefore: {
|
|
491
|
-
id: 'tableAddRowBefore', label: 'Add row before', available: true, icon: Icons.tableAddRowBefore,
|
|
492
|
-
isDisabled: (ed) => !ed.can().addRowBefore(),
|
|
493
|
-
command: (ed) => { ed.chain().focus().addRowBefore().run() },
|
|
494
|
-
},
|
|
495
|
-
tableAddRowAfter: {
|
|
496
|
-
id: 'tableAddRowAfter', label: 'Add row after', available: true, icon: Icons.tableAddRowAfter,
|
|
497
|
-
isDisabled: (ed) => !ed.can().addRowAfter(),
|
|
498
|
-
command: (ed) => { ed.chain().focus().addRowAfter().run() },
|
|
499
|
-
},
|
|
500
|
-
tableDeleteRow: {
|
|
501
|
-
id: 'tableDeleteRow', label: 'Delete row', available: true, icon: Icons.tableDeleteRow,
|
|
502
|
-
isDisabled: (ed) => !ed.can().deleteRow(),
|
|
503
|
-
command: (ed) => { ed.chain().focus().deleteRow().run() },
|
|
504
|
-
},
|
|
505
|
-
tableMergeCells: {
|
|
506
|
-
id: 'tableMergeCells', label: 'Merge cells', available: true, icon: Icons.tableMergeCells,
|
|
507
|
-
isDisabled: (ed) => !ed.can().mergeCells(),
|
|
508
|
-
command: (ed) => { ed.chain().focus().mergeCells().run() },
|
|
509
|
-
},
|
|
510
|
-
tableSplitCell: {
|
|
511
|
-
id: 'tableSplitCell', label: 'Split cell', available: true, icon: Icons.tableSplitCell,
|
|
512
|
-
isDisabled: (ed) => !ed.can().splitCell(),
|
|
513
|
-
command: (ed) => { ed.chain().focus().splitCell().run() },
|
|
514
|
-
},
|
|
515
|
-
tableToggleHeaderRow: {
|
|
516
|
-
id: 'tableToggleHeaderRow', label: 'Toggle header row', available: true, icon: Icons.tableToggleHeaderRow,
|
|
517
|
-
isDisabled: (ed) => !ed.can().toggleHeaderRow(),
|
|
518
|
-
command: (ed) => { ed.chain().focus().toggleHeaderRow().run() },
|
|
519
|
-
},
|
|
520
|
-
tableToggleHeaderCell: {
|
|
521
|
-
id: 'tableToggleHeaderCell', label: 'Toggle header cell', available: true, icon: Icons.tableToggleHeaderCell,
|
|
522
|
-
isDisabled: (ed) => !ed.can().toggleHeaderCell(),
|
|
523
|
-
isActive: (ed) => ed.isActive('tableHeader'),
|
|
524
|
-
command: (ed) => { ed.chain().focus().toggleHeaderCell().run() },
|
|
525
|
-
},
|
|
526
|
-
tableDelete: {
|
|
527
|
-
id: 'tableDelete', label: 'Delete table', available: true, icon: Icons.tableDelete,
|
|
528
|
-
isDisabled: (ed) => !ed.can().deleteTable(),
|
|
529
|
-
command: (ed) => { ed.chain().focus().deleteTable().run() },
|
|
530
|
-
},
|
|
531
|
-
// Wraps the current paragraph in a `<details>` node with an empty summary
|
|
532
|
-
// and the existing content as the body. `setDetails` is shipped by the
|
|
533
|
-
// `@tiptap/extension-details` package; `unsetDetails` (toggle off) sits
|
|
534
|
-
// behind a separate slash entry to keep the toolbar surface compact.
|
|
535
|
-
details: {
|
|
536
|
-
id: 'details', label: 'Collapsible block', available: true, icon: Icons.details,
|
|
537
|
-
isActive: (ed) => ed.isActive('details'),
|
|
538
|
-
isDisabled: (ed) => !ed.can().setDetails(),
|
|
539
|
-
command: (ed) => { ed.chain().focus().setDetails().run() },
|
|
540
|
-
},
|
|
541
|
-
// Multi-column grid layout. Toolbar button defaults to 2 columns — most-
|
|
542
|
-
// common-case UX. Users wanting 3 columns reach for the slash menu's
|
|
543
|
-
// dedicated entry. `gridDelete` unwraps the enclosing grid; greyed out
|
|
544
|
-
// when the cursor isn't inside one.
|
|
545
|
-
grid: {
|
|
546
|
-
id: 'grid', label: '2-column grid', available: true, icon: Icons.grid,
|
|
547
|
-
isActive: () => false,
|
|
548
|
-
isDisabled: (ed) => !ed.can().setGrid({ columns: 2 }),
|
|
549
|
-
command: (ed) => { ed.chain().focus().setGrid({ columns: 2 }).run() },
|
|
550
|
-
},
|
|
551
|
-
gridDelete: {
|
|
552
|
-
id: 'gridDelete', label: 'Remove grid', available: true, icon: Icons.gridDelete,
|
|
553
|
-
isActive: (ed) => ed.isActive('grid'),
|
|
554
|
-
isDisabled: (ed) => !ed.can().unsetGrid(),
|
|
555
|
-
command: (ed) => { ed.chain().focus().unsetGrid().run() },
|
|
556
|
-
},
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
function makeHeading(level: 1 | 2 | 3 | 4 | 5 | 6, icon: ReactNode): ToolbarButtonDef {
|
|
560
|
-
return {
|
|
561
|
-
id: `h${level}` as ToolbarButtonId,
|
|
562
|
-
label: `Heading ${level}`,
|
|
563
|
-
available: true,
|
|
564
|
-
icon,
|
|
565
|
-
isActive: (ed) => ed.isActive('heading', { level }),
|
|
566
|
-
command: (ed) => { ed.chain().focus().toggleHeading({ level }).run() },
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
function makeAlign(value: 'left' | 'center' | 'right' | 'justify', label: string, icon: ReactNode): ToolbarButtonDef {
|
|
571
|
-
return {
|
|
572
|
-
id: `align${value === 'left' ? 'Start' : value === 'right' ? 'End' : value.charAt(0).toUpperCase() + value.slice(1)}` as ToolbarButtonId,
|
|
573
|
-
label,
|
|
574
|
-
available: true,
|
|
575
|
-
icon,
|
|
576
|
-
isActive: (ed) => ed.isActive({ textAlign: value }),
|
|
577
|
-
command: (ed) => { ed.chain().focus().setTextAlign(value).run() },
|
|
578
|
-
}
|
|
579
|
-
}
|