@wakastellar/ui 3.3.3 → 3.5.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 (140) hide show
  1. package/dist/badge-BbwO7QeZ.js +1 -0
  2. package/dist/badge-BfiocODp.mjs +23 -0
  3. package/dist/charts.cjs.js +1 -1
  4. package/dist/charts.es.js +1 -1
  5. package/dist/chunk-14q5BKub.js +1 -0
  6. package/dist/{chunk-BH6uBOac.mjs → chunk-Cr9pTUWm.mjs} +5 -5
  7. package/dist/cn-DEtaFQsA.js +1 -0
  8. package/dist/cn-DUn6aSIQ.mjs +24 -0
  9. package/dist/doc.cjs.js +2 -2
  10. package/dist/doc.es.js +19 -19
  11. package/dist/editor.cjs.js +48 -0
  12. package/dist/editor.d.ts +1 -0
  13. package/dist/editor.es.js +6551 -0
  14. package/dist/{exceljs.min-DG9M8IZ1.mjs → exceljs.min-DL1XYDll.mjs} +1 -1
  15. package/dist/{exceljs.min-BuefmDRS.js → exceljs.min-qeIfSCbF.js} +1 -1
  16. package/dist/export.cjs.js +1 -1
  17. package/dist/export.es.js +1 -1
  18. package/dist/index.cjs.js +150 -150
  19. package/dist/index.es.js +26782 -27591
  20. package/dist/input-BfaSAGVw.js +1 -0
  21. package/dist/input-DVr_Qkl8.mjs +14 -0
  22. package/dist/rich-text.cjs.js +1 -1
  23. package/dist/rich-text.es.js +1 -1
  24. package/dist/security-CyBpuklN.mjs +122 -0
  25. package/dist/security-bFWwDrlg.js +1 -0
  26. package/dist/separator-NrkltulH.js +1 -0
  27. package/dist/separator-ibN2mycs.mjs +51 -0
  28. package/dist/src/components/editor/blocks/index.d.ts +51 -0
  29. package/dist/src/components/editor/blocks/waka-acceptance-criteria-block.d.ts +60 -0
  30. package/dist/src/components/editor/blocks/waka-ai-assist-block.d.ts +58 -0
  31. package/dist/src/components/editor/blocks/waka-api-endpoint-block.d.ts +63 -0
  32. package/dist/src/components/editor/blocks/waka-code-playground-block.d.ts +61 -0
  33. package/dist/src/components/editor/blocks/waka-comment-thread-block.d.ts +85 -0
  34. package/dist/src/components/editor/blocks/waka-diagram-block.d.ts +52 -0
  35. package/dist/src/components/editor/blocks/waka-embed-block.d.ts +58 -0
  36. package/dist/src/components/editor/blocks/waka-slash-menu-block.d.ts +67 -0
  37. package/dist/src/components/editor/blocks/waka-user-story-block.d.ts +79 -0
  38. package/dist/src/components/editor/blocks/waka-version-diff-block.d.ts +73 -0
  39. package/dist/src/components/editor/index.d.ts +66 -0
  40. package/dist/src/components/editor/waka-ai-writer.d.ts +80 -0
  41. package/dist/src/components/editor/waka-collaborative-editor.d.ts +93 -0
  42. package/dist/src/components/editor/waka-diff-viewer.d.ts +71 -0
  43. package/dist/src/components/editor/waka-dnd-editor.d.ts +64 -0
  44. package/dist/src/components/editor/waka-document-editor.d.ts +92 -0
  45. package/dist/src/components/editor/waka-editor-elements.d.ts +79 -0
  46. package/dist/src/components/editor/waka-editor-leaves.d.ts +39 -0
  47. package/dist/src/components/editor/waka-editor-plugins.d.ts +41 -0
  48. package/dist/src/components/editor/waka-editor-toolbar.d.ts +20 -0
  49. package/dist/src/components/editor/waka-editor.d.ts +59 -0
  50. package/dist/src/components/editor/waka-floating-toolbar.d.ts +47 -0
  51. package/dist/src/components/editor/waka-markdown-editor.d.ts +60 -0
  52. package/dist/src/components/editor/waka-mention-editor.d.ts +125 -0
  53. package/dist/src/components/editor/waka-slash-menu.d.ts +70 -0
  54. package/dist/src/components/editor/waka-spec-editor.d.ts +88 -0
  55. package/dist/src/components/index.d.ts +1 -15
  56. package/dist/src/editor.d.ts +26 -0
  57. package/dist/textarea-CdQWggYG.js +1 -0
  58. package/dist/textarea-DJDXJ3nd.mjs +23 -0
  59. package/dist/types-C2St0wOW.js +1 -0
  60. package/dist/{types-B6GVaSIP.mjs → types-JnqoLyuv.mjs} +214 -211
  61. package/dist/{useDataTableImport-BPvfo--2.mjs → useDataTableImport-BWUFesPi.mjs} +3 -3
  62. package/dist/{useDataTableImport-Cm_pCKnO.js → useDataTableImport-T7ddpN5k.js} +3 -3
  63. package/dist/waka-doc-renderer-CTxC7Trf.js +3 -0
  64. package/dist/{waka-doc-renderer-BkIvas3z.mjs → waka-doc-renderer-Cw-Xnyen.mjs} +264 -281
  65. package/dist/waka-editor-plugins-DR6tpsUC.mjs +135 -0
  66. package/dist/waka-editor-plugins-sGSh9hn2.js +1 -0
  67. package/dist/waka-rich-text-editor-BlIdtknG.js +1 -0
  68. package/dist/waka-rich-text-editor-D1uA3zbB.js +1 -0
  69. package/dist/waka-rich-text-editor-DgSWiXMW.mjs +342 -0
  70. package/dist/waka-rich-text-editor-DndVJuDw.mjs +2 -0
  71. package/package.json +87 -2
  72. package/src/blocks/footer/index.tsx +1 -6
  73. package/src/blocks/login/index.tsx +1 -7
  74. package/src/blocks/profile/index.tsx +3 -5
  75. package/src/components/editor/blocks/index.ts +182 -0
  76. package/src/components/editor/blocks/waka-acceptance-criteria-block.tsx +326 -0
  77. package/src/components/editor/blocks/waka-ai-assist-block.tsx +284 -0
  78. package/src/components/editor/blocks/waka-api-endpoint-block.tsx +382 -0
  79. package/src/components/editor/blocks/waka-code-playground-block.tsx +331 -0
  80. package/src/components/editor/blocks/waka-comment-thread-block.tsx +448 -0
  81. package/src/components/editor/blocks/waka-diagram-block.tsx +293 -0
  82. package/src/components/editor/blocks/waka-embed-block.tsx +416 -0
  83. package/src/components/editor/blocks/waka-slash-menu-block.tsx +432 -0
  84. package/src/components/editor/blocks/waka-user-story-block.tsx +295 -0
  85. package/src/components/editor/blocks/waka-version-diff-block.tsx +426 -0
  86. package/src/components/editor/index.ts +279 -0
  87. package/src/components/editor/waka-ai-writer.tsx +434 -0
  88. package/src/components/editor/waka-collaborative-editor.tsx +426 -0
  89. package/src/components/editor/waka-diff-viewer.tsx +352 -0
  90. package/src/components/editor/waka-dnd-editor.tsx +284 -0
  91. package/src/components/editor/waka-document-editor.tsx +502 -0
  92. package/src/components/editor/waka-editor-elements.tsx +312 -0
  93. package/src/components/editor/waka-editor-leaves.tsx +101 -0
  94. package/src/components/editor/waka-editor-plugins.ts +207 -0
  95. package/src/components/editor/waka-editor-toolbar.tsx +358 -0
  96. package/src/components/editor/waka-editor.tsx +431 -0
  97. package/src/components/editor/waka-floating-toolbar.tsx +268 -0
  98. package/src/components/editor/waka-markdown-editor.tsx +395 -0
  99. package/src/components/editor/waka-mention-editor.tsx +459 -0
  100. package/src/components/editor/waka-slash-menu.tsx +392 -0
  101. package/src/components/editor/waka-spec-editor.tsx +657 -0
  102. package/src/components/index.ts +1 -18
  103. package/dist/chunk-BDDJmn7V.js +0 -1
  104. package/dist/cn-DnPbmOCy.js +0 -1
  105. package/dist/cn-DpLcAzrf.mjs +0 -22
  106. package/dist/separator-BDReXBvI.mjs +0 -59
  107. package/dist/separator-BKjNl9sI.js +0 -1
  108. package/dist/src/components/waka-actor-badge/index.d.ts +0 -8
  109. package/dist/src/components/waka-actors-list/index.d.ts +0 -18
  110. package/dist/src/components/waka-ai-assistant-button/index.d.ts +0 -8
  111. package/dist/src/components/waka-document-flyover/index.d.ts +0 -10
  112. package/dist/src/components/waka-document-preview-popup/index.d.ts +0 -26
  113. package/dist/src/components/waka-hour-balance-badge/index.d.ts +0 -8
  114. package/dist/src/components/waka-hour-consumption-table/index.d.ts +0 -15
  115. package/dist/src/components/waka-hour-pack-dialog/index.d.ts +0 -8
  116. package/dist/src/components/waka-project-stats-header/index.d.ts +0 -15
  117. package/dist/src/components/waka-step-comment-bubble/index.d.ts +0 -13
  118. package/dist/src/components/waka-step-comment-panel/index.d.ts +0 -20
  119. package/dist/src/components/waka-step-permission-matrix/index.d.ts +0 -12
  120. package/dist/src/components/waka-time-entry-dialog/index.d.ts +0 -16
  121. package/dist/src/components/waka-time-tracking-flyover/index.d.ts +0 -11
  122. package/dist/types-BH9cQRqZ.js +0 -1
  123. package/dist/waka-doc-renderer-BZ2-SqyT.js +0 -3
  124. package/dist/waka-rich-text-editor-BJGlQgpq.js +0 -1
  125. package/dist/waka-rich-text-editor-BJzzxeP1.mjs +0 -361
  126. package/dist/waka-rich-text-editor-wnXLwvUo.js +0 -1
  127. package/src/components/waka-actor-badge/index.tsx +0 -34
  128. package/src/components/waka-actors-list/index.tsx +0 -125
  129. package/src/components/waka-ai-assistant-button/index.tsx +0 -31
  130. package/src/components/waka-document-flyover/index.tsx +0 -36
  131. package/src/components/waka-document-preview-popup/index.tsx +0 -103
  132. package/src/components/waka-hour-balance-badge/index.tsx +0 -43
  133. package/src/components/waka-hour-consumption-table/index.tsx +0 -72
  134. package/src/components/waka-hour-pack-dialog/index.tsx +0 -72
  135. package/src/components/waka-project-stats-header/index.tsx +0 -69
  136. package/src/components/waka-step-comment-bubble/index.tsx +0 -71
  137. package/src/components/waka-step-comment-panel/index.tsx +0 -106
  138. package/src/components/waka-step-permission-matrix/index.tsx +0 -65
  139. package/src/components/waka-time-entry-dialog/index.tsx +0 -131
  140. package/src/components/waka-time-tracking-flyover/index.tsx +0 -41
@@ -0,0 +1,392 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import {
5
+ Heading1,
6
+ Heading2,
7
+ Heading3,
8
+ Table,
9
+ Columns2,
10
+ Image,
11
+ Code,
12
+ Minus,
13
+ Quote,
14
+ MessageSquarePlus,
15
+ List,
16
+ ListOrdered,
17
+ Link,
18
+ Sparkles,
19
+ type LucideIcon,
20
+ } from "lucide-react"
21
+ import { cn } from "../../utils/cn"
22
+
23
+ // ─── Types ───────────────────────────────────────────────────────────────────
24
+
25
+ /** A single slash command entry */
26
+ export interface SlashCommand {
27
+ /** Unique key used for filtering */
28
+ key: string
29
+ /** Display label */
30
+ label: string
31
+ /** Optional description shown below the label */
32
+ description?: string
33
+ /** Lucide icon component */
34
+ icon: LucideIcon
35
+ /** Group name (commands are visually grouped) */
36
+ group: string
37
+ /** Search keywords for filtering */
38
+ keywords?: string[]
39
+ /**
40
+ * Action to execute when the command is selected.
41
+ * Receives the Plate editor instance.
42
+ */
43
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
+ action: (editor: any) => void
45
+ }
46
+
47
+ export interface WakaSlashMenuProps {
48
+ /** Commands to display in the menu */
49
+ commands?: SlashCommand[]
50
+ /** CSS class for the menu container */
51
+ className?: string
52
+ /** Callback when a command is selected. Receives the command key. */
53
+ onSelect?: (key: string) => void
54
+ }
55
+
56
+ // ─── Default commands ────────────────────────────────────────────────────────
57
+
58
+ /** Default slash commands for WakaStart editors */
59
+ export const DEFAULT_SLASH_COMMANDS: SlashCommand[] = [
60
+ // Basic blocks
61
+ {
62
+ key: "h1",
63
+ label: "Titre 1",
64
+ description: "Titre principal de section",
65
+ icon: Heading1,
66
+ group: "Blocs de base",
67
+ keywords: ["heading", "titre", "h1"],
68
+ action: (e) => { try { e.tf.h1.toggle() } catch { /* */ } },
69
+ },
70
+ {
71
+ key: "h2",
72
+ label: "Titre 2",
73
+ description: "Sous-titre de section",
74
+ icon: Heading2,
75
+ group: "Blocs de base",
76
+ keywords: ["heading", "titre", "h2"],
77
+ action: (e) => { try { e.tf.h2.toggle() } catch { /* */ } },
78
+ },
79
+ {
80
+ key: "h3",
81
+ label: "Titre 3",
82
+ description: "Titre de sous-section",
83
+ icon: Heading3,
84
+ group: "Blocs de base",
85
+ keywords: ["heading", "titre", "h3"],
86
+ action: (e) => { try { e.tf.h3.toggle() } catch { /* */ } },
87
+ },
88
+ {
89
+ key: "bulletList",
90
+ label: "Liste a puces",
91
+ description: "Liste non ordonnee",
92
+ icon: List,
93
+ group: "Blocs de base",
94
+ keywords: ["bullet", "list", "unordered", "puces"],
95
+ action: (e) => { try { e.chain().focus().toggleBulletList().run() } catch { /* */ } },
96
+ },
97
+ {
98
+ key: "numberedList",
99
+ label: "Liste numerotee",
100
+ description: "Liste ordonnee",
101
+ icon: ListOrdered,
102
+ group: "Blocs de base",
103
+ keywords: ["numbered", "list", "ordered", "numerotee"],
104
+ action: (e) => { try { e.chain().focus().toggleOrderedList().run() } catch { /* */ } },
105
+ },
106
+ {
107
+ key: "blockquote",
108
+ label: "Citation",
109
+ description: "Bloc de citation",
110
+ icon: Quote,
111
+ group: "Blocs de base",
112
+ keywords: ["quote", "citation", "blockquote"],
113
+ action: (e) => { try { e.tf.blockquote.toggle() } catch { /* */ } },
114
+ },
115
+ // Advanced blocks
116
+ {
117
+ key: "table",
118
+ label: "Tableau",
119
+ description: "Inserer un tableau 3x3",
120
+ icon: Table,
121
+ group: "Blocs avances",
122
+ keywords: ["table", "tableau", "grid"],
123
+ action: (e) => { try { e.tf.table.insert({ rows: 3, cols: 3 }) } catch { /* */ } },
124
+ },
125
+ {
126
+ key: "columns",
127
+ label: "Colonnes",
128
+ description: "Mise en page multi-colonnes",
129
+ icon: Columns2,
130
+ group: "Blocs avances",
131
+ keywords: ["column", "colonne", "layout", "mise en page"],
132
+ action: (e) => { try { e.tf.column.insert() } catch { /* */ } },
133
+ },
134
+ {
135
+ key: "callout",
136
+ label: "Callout",
137
+ description: "Encart d'information colore",
138
+ icon: MessageSquarePlus,
139
+ group: "Blocs avances",
140
+ keywords: ["callout", "alert", "info", "encart"],
141
+ action: (e) => { try { e.tf.callout.insert() } catch { /* */ } },
142
+ },
143
+ {
144
+ key: "codeBlock",
145
+ label: "Bloc de code",
146
+ description: "Code source avec coloration",
147
+ icon: Code,
148
+ group: "Blocs avances",
149
+ keywords: ["code", "pre", "source"],
150
+ action: (e) => { try { e.chain().focus().toggleCodeBlock().run() } catch { /* */ } },
151
+ },
152
+ // Insertions
153
+ {
154
+ key: "link",
155
+ label: "Lien",
156
+ description: "Inserer un lien hypertexte",
157
+ icon: Link,
158
+ group: "Insertions",
159
+ keywords: ["link", "lien", "url", "href"],
160
+ action: (e) => { try { e.tf.link.toggle() } catch { /* */ } },
161
+ },
162
+ {
163
+ key: "image",
164
+ label: "Image",
165
+ description: "Inserer une image",
166
+ icon: Image,
167
+ group: "Insertions",
168
+ keywords: ["image", "photo", "picture", "img"],
169
+ action: (e) => { try { e.tf.image.insert({ url: "" }) } catch { /* */ } },
170
+ },
171
+ {
172
+ key: "divider",
173
+ label: "Separateur",
174
+ description: "Ligne horizontale de separation",
175
+ icon: Minus,
176
+ group: "Insertions",
177
+ keywords: ["divider", "separator", "hr", "line"],
178
+ action: (e) => { try { e.chain().focus().insertBreak().run() } catch { /* */ } },
179
+ },
180
+ // AI
181
+ {
182
+ key: "ai",
183
+ label: "Demander a l'IA",
184
+ description: "Generer du contenu avec l'assistant IA",
185
+ icon: Sparkles,
186
+ group: "IA",
187
+ keywords: ["ai", "ia", "assistant", "generate", "generer"],
188
+ action: () => {
189
+ // AI action is typically handled by the AI plugin
190
+ // This acts as a trigger for the AIChatPlugin
191
+ },
192
+ },
193
+ ]
194
+
195
+ // ─── Slash menu rendering component ─────────────────────────────────────────
196
+
197
+ /**
198
+ * WakaSlashMenu -- Configurable slash command palette.
199
+ *
200
+ * This component is designed to be used **inside** a Plate editor context
201
+ * as the combobox renderer for the `@platejs/combobox` SlashPlugin,
202
+ * or standalone as a command registry.
203
+ *
204
+ * When used standalone, provide `onSelect` to receive the selected command key.
205
+ * The consuming app is responsible for connecting it to the editor.
206
+ *
207
+ * @example
208
+ * ```tsx
209
+ * <WakaSlashMenu
210
+ * commands={[...DEFAULT_SLASH_COMMANDS, myCustomCommand]}
211
+ * onSelect={(key) => console.log("Selected:", key)}
212
+ * />
213
+ * ```
214
+ */
215
+ export function WakaSlashMenu({
216
+ commands = DEFAULT_SLASH_COMMANDS,
217
+ className,
218
+ onSelect,
219
+ }: WakaSlashMenuProps) {
220
+ const [search, setSearch] = React.useState("")
221
+ const [activeIndex, setActiveIndex] = React.useState(0)
222
+ const listRef = React.useRef<HTMLDivElement>(null)
223
+
224
+ // Filter commands
225
+ const filtered = React.useMemo(() => {
226
+ if (!search) return commands
227
+ const q = search.toLowerCase()
228
+ return commands.filter(
229
+ (cmd) =>
230
+ cmd.label.toLowerCase().includes(q) ||
231
+ cmd.key.toLowerCase().includes(q) ||
232
+ cmd.keywords?.some((kw) => kw.toLowerCase().includes(q))
233
+ )
234
+ }, [commands, search])
235
+
236
+ // Group commands
237
+ const groups = React.useMemo(() => {
238
+ const map = new Map<string, SlashCommand[]>()
239
+ for (const cmd of filtered) {
240
+ const group = cmd.group || "Autre"
241
+ if (!map.has(group)) map.set(group, [])
242
+ map.get(group)!.push(cmd)
243
+ }
244
+ return map
245
+ }, [filtered])
246
+
247
+ // Flatten for keyboard nav
248
+ const flatItems = React.useMemo(() => filtered, [filtered])
249
+
250
+ // Keyboard navigation
251
+ const handleKeyDown = React.useCallback(
252
+ (e: React.KeyboardEvent) => {
253
+ if (e.key === "ArrowDown") {
254
+ e.preventDefault()
255
+ setActiveIndex((i) => Math.min(i + 1, flatItems.length - 1))
256
+ } else if (e.key === "ArrowUp") {
257
+ e.preventDefault()
258
+ setActiveIndex((i) => Math.max(i - 1, 0))
259
+ } else if (e.key === "Enter" && flatItems[activeIndex]) {
260
+ e.preventDefault()
261
+ onSelect?.(flatItems[activeIndex].key)
262
+ }
263
+ },
264
+ [flatItems, activeIndex, onSelect]
265
+ )
266
+
267
+ // Reset active index on search change
268
+ React.useEffect(() => setActiveIndex(0), [search])
269
+
270
+ return (
271
+ <div
272
+ ref={listRef}
273
+ className={cn(
274
+ "w-72 max-h-80 overflow-y-auto",
275
+ "rounded-lg border bg-popover shadow-lg",
276
+ "p-1",
277
+ className
278
+ )}
279
+ role="listbox"
280
+ onKeyDown={handleKeyDown}
281
+ >
282
+ {/* Search input */}
283
+ <div className="px-2 pb-1.5">
284
+ <input
285
+ type="text"
286
+ value={search}
287
+ onChange={(e) => setSearch(e.target.value)}
288
+ placeholder="Rechercher..."
289
+ className={cn(
290
+ "w-full rounded-md border bg-background px-2 py-1.5",
291
+ "text-sm placeholder:text-muted-foreground",
292
+ "focus:outline-none focus:ring-1 focus:ring-ring"
293
+ )}
294
+ autoFocus
295
+ />
296
+ </div>
297
+
298
+ {/* Commands */}
299
+ {flatItems.length === 0 && (
300
+ <div className="px-2 py-4 text-center text-sm text-muted-foreground">
301
+ Aucune commande trouvee
302
+ </div>
303
+ )}
304
+
305
+ {Array.from(groups.entries()).map(([groupName, cmds]) => (
306
+ <div key={groupName}>
307
+ <div className="px-2 py-1 text-xs font-semibold text-muted-foreground uppercase tracking-wider">
308
+ {groupName}
309
+ </div>
310
+ {cmds.map((cmd) => {
311
+ const globalIndex = flatItems.indexOf(cmd)
312
+ const Icon = cmd.icon
313
+ return (
314
+ <button
315
+ key={cmd.key}
316
+ role="option"
317
+ aria-selected={globalIndex === activeIndex}
318
+ className={cn(
319
+ "flex w-full items-center gap-3 rounded-md px-2 py-1.5 text-left",
320
+ "text-sm transition-colors cursor-pointer",
321
+ globalIndex === activeIndex
322
+ ? "bg-accent text-accent-foreground"
323
+ : "hover:bg-accent/50"
324
+ )}
325
+ onMouseEnter={() => setActiveIndex(globalIndex)}
326
+ onClick={() => onSelect?.(cmd.key)}
327
+ >
328
+ <div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-md border bg-background">
329
+ <Icon className="h-4 w-4" />
330
+ </div>
331
+ <div className="min-w-0 flex-1">
332
+ <div className="font-medium">{cmd.label}</div>
333
+ {cmd.description && (
334
+ <div className="text-xs text-muted-foreground truncate">
335
+ {cmd.description}
336
+ </div>
337
+ )}
338
+ </div>
339
+ </button>
340
+ )
341
+ })}
342
+ </div>
343
+ ))}
344
+ </div>
345
+ )
346
+ }
347
+
348
+ WakaSlashMenu.displayName = "WakaSlashMenu"
349
+
350
+ // ─── Plugin helper ───────────────────────────────────────────────────────────
351
+
352
+ /**
353
+ * Creates slash command plugin configuration for Plate.
354
+ * Call this function and spread the result into your plugins array.
355
+ *
356
+ * @example
357
+ * ```ts
358
+ * import { createSlashCommandPlugins } from "@wakastellar/ui/editor"
359
+ *
360
+ * const editor = usePlateEditor({
361
+ * plugins: [
362
+ * ...otherPlugins,
363
+ * ...await createSlashCommandPlugins(),
364
+ * ],
365
+ * })
366
+ * ```
367
+ */
368
+ export async function createSlashCommandPlugins(
369
+ commands: SlashCommand[] = DEFAULT_SLASH_COMMANDS
370
+ ) {
371
+ try {
372
+ const { SlashPlugin, SlashInputPlugin } = await import("@platejs/combobox/react")
373
+ return [
374
+ SlashPlugin.configure({
375
+ options: {
376
+ trigger: "/",
377
+ triggerPreviousCharPattern: /^$|^[\s"']$/,
378
+ rules: commands.map((cmd) => ({
379
+ key: cmd.key,
380
+ text: cmd.label,
381
+ keywords: cmd.keywords,
382
+ onSelect: cmd.action,
383
+ })),
384
+ },
385
+ }),
386
+ SlashInputPlugin,
387
+ ]
388
+ } catch {
389
+ console.warn("[WakaSlashMenu] @platejs/combobox not installed, slash commands disabled")
390
+ return []
391
+ }
392
+ }