@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.
- package/dist/badge-BbwO7QeZ.js +1 -0
- package/dist/badge-BfiocODp.mjs +23 -0
- package/dist/charts.cjs.js +1 -1
- package/dist/charts.es.js +1 -1
- package/dist/chunk-14q5BKub.js +1 -0
- package/dist/{chunk-BH6uBOac.mjs → chunk-Cr9pTUWm.mjs} +5 -5
- package/dist/cn-DEtaFQsA.js +1 -0
- package/dist/cn-DUn6aSIQ.mjs +24 -0
- package/dist/doc.cjs.js +2 -2
- package/dist/doc.es.js +19 -19
- package/dist/editor.cjs.js +48 -0
- package/dist/editor.d.ts +1 -0
- package/dist/editor.es.js +6551 -0
- package/dist/{exceljs.min-DG9M8IZ1.mjs → exceljs.min-DL1XYDll.mjs} +1 -1
- package/dist/{exceljs.min-BuefmDRS.js → exceljs.min-qeIfSCbF.js} +1 -1
- package/dist/export.cjs.js +1 -1
- package/dist/export.es.js +1 -1
- package/dist/index.cjs.js +150 -150
- package/dist/index.es.js +26782 -27591
- package/dist/input-BfaSAGVw.js +1 -0
- package/dist/input-DVr_Qkl8.mjs +14 -0
- package/dist/rich-text.cjs.js +1 -1
- package/dist/rich-text.es.js +1 -1
- package/dist/security-CyBpuklN.mjs +122 -0
- package/dist/security-bFWwDrlg.js +1 -0
- package/dist/separator-NrkltulH.js +1 -0
- package/dist/separator-ibN2mycs.mjs +51 -0
- package/dist/src/components/editor/blocks/index.d.ts +51 -0
- package/dist/src/components/editor/blocks/waka-acceptance-criteria-block.d.ts +60 -0
- package/dist/src/components/editor/blocks/waka-ai-assist-block.d.ts +58 -0
- package/dist/src/components/editor/blocks/waka-api-endpoint-block.d.ts +63 -0
- package/dist/src/components/editor/blocks/waka-code-playground-block.d.ts +61 -0
- package/dist/src/components/editor/blocks/waka-comment-thread-block.d.ts +85 -0
- package/dist/src/components/editor/blocks/waka-diagram-block.d.ts +52 -0
- package/dist/src/components/editor/blocks/waka-embed-block.d.ts +58 -0
- package/dist/src/components/editor/blocks/waka-slash-menu-block.d.ts +67 -0
- package/dist/src/components/editor/blocks/waka-user-story-block.d.ts +79 -0
- package/dist/src/components/editor/blocks/waka-version-diff-block.d.ts +73 -0
- package/dist/src/components/editor/index.d.ts +66 -0
- package/dist/src/components/editor/waka-ai-writer.d.ts +80 -0
- package/dist/src/components/editor/waka-collaborative-editor.d.ts +93 -0
- package/dist/src/components/editor/waka-diff-viewer.d.ts +71 -0
- package/dist/src/components/editor/waka-dnd-editor.d.ts +64 -0
- package/dist/src/components/editor/waka-document-editor.d.ts +92 -0
- package/dist/src/components/editor/waka-editor-elements.d.ts +79 -0
- package/dist/src/components/editor/waka-editor-leaves.d.ts +39 -0
- package/dist/src/components/editor/waka-editor-plugins.d.ts +41 -0
- package/dist/src/components/editor/waka-editor-toolbar.d.ts +20 -0
- package/dist/src/components/editor/waka-editor.d.ts +59 -0
- package/dist/src/components/editor/waka-floating-toolbar.d.ts +47 -0
- package/dist/src/components/editor/waka-markdown-editor.d.ts +60 -0
- package/dist/src/components/editor/waka-mention-editor.d.ts +125 -0
- package/dist/src/components/editor/waka-slash-menu.d.ts +70 -0
- package/dist/src/components/editor/waka-spec-editor.d.ts +88 -0
- package/dist/src/components/index.d.ts +1 -15
- package/dist/src/editor.d.ts +26 -0
- package/dist/textarea-CdQWggYG.js +1 -0
- package/dist/textarea-DJDXJ3nd.mjs +23 -0
- package/dist/types-C2St0wOW.js +1 -0
- package/dist/{types-B6GVaSIP.mjs → types-JnqoLyuv.mjs} +214 -211
- package/dist/{useDataTableImport-BPvfo--2.mjs → useDataTableImport-BWUFesPi.mjs} +3 -3
- package/dist/{useDataTableImport-Cm_pCKnO.js → useDataTableImport-T7ddpN5k.js} +3 -3
- package/dist/waka-doc-renderer-CTxC7Trf.js +3 -0
- package/dist/{waka-doc-renderer-BkIvas3z.mjs → waka-doc-renderer-Cw-Xnyen.mjs} +264 -281
- package/dist/waka-editor-plugins-DR6tpsUC.mjs +135 -0
- package/dist/waka-editor-plugins-sGSh9hn2.js +1 -0
- package/dist/waka-rich-text-editor-BlIdtknG.js +1 -0
- package/dist/waka-rich-text-editor-D1uA3zbB.js +1 -0
- package/dist/waka-rich-text-editor-DgSWiXMW.mjs +342 -0
- package/dist/waka-rich-text-editor-DndVJuDw.mjs +2 -0
- package/package.json +87 -2
- package/src/blocks/footer/index.tsx +1 -6
- package/src/blocks/login/index.tsx +1 -7
- package/src/blocks/profile/index.tsx +3 -5
- package/src/components/editor/blocks/index.ts +182 -0
- package/src/components/editor/blocks/waka-acceptance-criteria-block.tsx +326 -0
- package/src/components/editor/blocks/waka-ai-assist-block.tsx +284 -0
- package/src/components/editor/blocks/waka-api-endpoint-block.tsx +382 -0
- package/src/components/editor/blocks/waka-code-playground-block.tsx +331 -0
- package/src/components/editor/blocks/waka-comment-thread-block.tsx +448 -0
- package/src/components/editor/blocks/waka-diagram-block.tsx +293 -0
- package/src/components/editor/blocks/waka-embed-block.tsx +416 -0
- package/src/components/editor/blocks/waka-slash-menu-block.tsx +432 -0
- package/src/components/editor/blocks/waka-user-story-block.tsx +295 -0
- package/src/components/editor/blocks/waka-version-diff-block.tsx +426 -0
- package/src/components/editor/index.ts +279 -0
- package/src/components/editor/waka-ai-writer.tsx +434 -0
- package/src/components/editor/waka-collaborative-editor.tsx +426 -0
- package/src/components/editor/waka-diff-viewer.tsx +352 -0
- package/src/components/editor/waka-dnd-editor.tsx +284 -0
- package/src/components/editor/waka-document-editor.tsx +502 -0
- package/src/components/editor/waka-editor-elements.tsx +312 -0
- package/src/components/editor/waka-editor-leaves.tsx +101 -0
- package/src/components/editor/waka-editor-plugins.ts +207 -0
- package/src/components/editor/waka-editor-toolbar.tsx +358 -0
- package/src/components/editor/waka-editor.tsx +431 -0
- package/src/components/editor/waka-floating-toolbar.tsx +268 -0
- package/src/components/editor/waka-markdown-editor.tsx +395 -0
- package/src/components/editor/waka-mention-editor.tsx +459 -0
- package/src/components/editor/waka-slash-menu.tsx +392 -0
- package/src/components/editor/waka-spec-editor.tsx +657 -0
- package/src/components/index.ts +1 -18
- package/dist/chunk-BDDJmn7V.js +0 -1
- package/dist/cn-DnPbmOCy.js +0 -1
- package/dist/cn-DpLcAzrf.mjs +0 -22
- package/dist/separator-BDReXBvI.mjs +0 -59
- package/dist/separator-BKjNl9sI.js +0 -1
- package/dist/src/components/waka-actor-badge/index.d.ts +0 -8
- package/dist/src/components/waka-actors-list/index.d.ts +0 -18
- package/dist/src/components/waka-ai-assistant-button/index.d.ts +0 -8
- package/dist/src/components/waka-document-flyover/index.d.ts +0 -10
- package/dist/src/components/waka-document-preview-popup/index.d.ts +0 -26
- package/dist/src/components/waka-hour-balance-badge/index.d.ts +0 -8
- package/dist/src/components/waka-hour-consumption-table/index.d.ts +0 -15
- package/dist/src/components/waka-hour-pack-dialog/index.d.ts +0 -8
- package/dist/src/components/waka-project-stats-header/index.d.ts +0 -15
- package/dist/src/components/waka-step-comment-bubble/index.d.ts +0 -13
- package/dist/src/components/waka-step-comment-panel/index.d.ts +0 -20
- package/dist/src/components/waka-step-permission-matrix/index.d.ts +0 -12
- package/dist/src/components/waka-time-entry-dialog/index.d.ts +0 -16
- package/dist/src/components/waka-time-tracking-flyover/index.d.ts +0 -11
- package/dist/types-BH9cQRqZ.js +0 -1
- package/dist/waka-doc-renderer-BZ2-SqyT.js +0 -3
- package/dist/waka-rich-text-editor-BJGlQgpq.js +0 -1
- package/dist/waka-rich-text-editor-BJzzxeP1.mjs +0 -361
- package/dist/waka-rich-text-editor-wnXLwvUo.js +0 -1
- package/src/components/waka-actor-badge/index.tsx +0 -34
- package/src/components/waka-actors-list/index.tsx +0 -125
- package/src/components/waka-ai-assistant-button/index.tsx +0 -31
- package/src/components/waka-document-flyover/index.tsx +0 -36
- package/src/components/waka-document-preview-popup/index.tsx +0 -103
- package/src/components/waka-hour-balance-badge/index.tsx +0 -43
- package/src/components/waka-hour-consumption-table/index.tsx +0 -72
- package/src/components/waka-hour-pack-dialog/index.tsx +0 -72
- package/src/components/waka-project-stats-header/index.tsx +0 -69
- package/src/components/waka-step-comment-bubble/index.tsx +0 -71
- package/src/components/waka-step-comment-panel/index.tsx +0 -106
- package/src/components/waka-step-permission-matrix/index.tsx +0 -65
- package/src/components/waka-time-entry-dialog/index.tsx +0 -131
- package/src/components/waka-time-tracking-flyover/index.tsx +0 -41
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { cn } from "../../../utils/cn"
|
|
5
|
+
import type { SlashCommand } from "../waka-slash-menu"
|
|
6
|
+
import { DEFAULT_SLASH_COMMANDS } from "../waka-slash-menu"
|
|
7
|
+
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Types
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
/** Extended slash command with WakaStart-specific blocks */
|
|
13
|
+
export interface WakaSlashBlockCommand extends SlashCommand {
|
|
14
|
+
/** Preview/thumbnail for the block */
|
|
15
|
+
preview?: React.ReactNode
|
|
16
|
+
/** Whether this is a WakaStart custom block (vs standard Plate block) */
|
|
17
|
+
isCustom?: boolean
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface WakaSlashMenuBlockProps {
|
|
21
|
+
/** All available commands (merges with defaults) */
|
|
22
|
+
commands?: WakaSlashBlockCommand[]
|
|
23
|
+
/** Filter query (typed after "/") */
|
|
24
|
+
query?: string
|
|
25
|
+
/** Called when a command is selected */
|
|
26
|
+
onSelect?: (command: WakaSlashBlockCommand) => void
|
|
27
|
+
/** Active (highlighted) index */
|
|
28
|
+
activeIndex?: number
|
|
29
|
+
/** Callback when active index changes */
|
|
30
|
+
onActiveIndexChange?: (index: number) => void
|
|
31
|
+
/** CSS class */
|
|
32
|
+
className?: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ============================================================================
|
|
36
|
+
// Default WakaStart Block Commands
|
|
37
|
+
// ============================================================================
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* WakaStart-specific slash commands for custom blocks.
|
|
41
|
+
* These extend the standard Plate slash commands with blocks specific
|
|
42
|
+
* to the WakaStart specification and project management workflow.
|
|
43
|
+
*/
|
|
44
|
+
export const WAKA_BLOCK_COMMANDS: WakaSlashBlockCommand[] = [
|
|
45
|
+
// User Story
|
|
46
|
+
{
|
|
47
|
+
key: "user-story",
|
|
48
|
+
label: "User Story",
|
|
49
|
+
description: "Structured user story (As a / I want / So that)",
|
|
50
|
+
icon: (() => {
|
|
51
|
+
// Lazy icon - we use a simple component approach
|
|
52
|
+
const { Users } = require("lucide-react") as { Users: React.ComponentType<{ className?: string }> }
|
|
53
|
+
return Users
|
|
54
|
+
})(),
|
|
55
|
+
group: "WakaStart Blocks",
|
|
56
|
+
keywords: ["user", "story", "agile", "requirement", "persona"],
|
|
57
|
+
isCustom: true,
|
|
58
|
+
action: (editor) => {
|
|
59
|
+
try {
|
|
60
|
+
const { createUserStoryNodes } = require("./waka-user-story-block")
|
|
61
|
+
editor.insertNodes(createUserStoryNodes())
|
|
62
|
+
} catch { /* */ }
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
// Acceptance Criteria
|
|
66
|
+
{
|
|
67
|
+
key: "acceptance-criteria",
|
|
68
|
+
label: "Acceptance Criteria",
|
|
69
|
+
description: "Gherkin-format criteria (Given / When / Then)",
|
|
70
|
+
icon: (() => {
|
|
71
|
+
const { CheckCircle2 } = require("lucide-react") as { CheckCircle2: React.ComponentType<{ className?: string }> }
|
|
72
|
+
return CheckCircle2
|
|
73
|
+
})(),
|
|
74
|
+
group: "WakaStart Blocks",
|
|
75
|
+
keywords: ["acceptance", "criteria", "gherkin", "given", "when", "then", "test"],
|
|
76
|
+
isCustom: true,
|
|
77
|
+
action: (editor) => {
|
|
78
|
+
try {
|
|
79
|
+
const { createAcceptanceCriteriaNodes } = require("./waka-acceptance-criteria-block")
|
|
80
|
+
editor.insertNodes(createAcceptanceCriteriaNodes())
|
|
81
|
+
} catch { /* */ }
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
// API Endpoint
|
|
85
|
+
{
|
|
86
|
+
key: "api-endpoint",
|
|
87
|
+
label: "API Endpoint",
|
|
88
|
+
description: "REST API documentation (method, route, body, response)",
|
|
89
|
+
icon: (() => {
|
|
90
|
+
const { Zap } = require("lucide-react") as { Zap: React.ComponentType<{ className?: string }> }
|
|
91
|
+
return Zap
|
|
92
|
+
})(),
|
|
93
|
+
group: "WakaStart Blocks",
|
|
94
|
+
keywords: ["api", "endpoint", "rest", "route", "get", "post", "put", "delete"],
|
|
95
|
+
isCustom: true,
|
|
96
|
+
action: (editor) => {
|
|
97
|
+
try {
|
|
98
|
+
const { createApiEndpointNodes } = require("./waka-api-endpoint-block")
|
|
99
|
+
editor.insertNodes(createApiEndpointNodes())
|
|
100
|
+
} catch { /* */ }
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
// Diagram
|
|
104
|
+
{
|
|
105
|
+
key: "diagram",
|
|
106
|
+
label: "Diagram",
|
|
107
|
+
description: "Mermaid / PlantUML diagram with live preview",
|
|
108
|
+
icon: (() => {
|
|
109
|
+
const { GitBranch } = require("lucide-react") as { GitBranch: React.ComponentType<{ className?: string }> }
|
|
110
|
+
return GitBranch
|
|
111
|
+
})(),
|
|
112
|
+
group: "WakaStart Blocks",
|
|
113
|
+
keywords: ["diagram", "mermaid", "plantuml", "flowchart", "sequence", "graph"],
|
|
114
|
+
isCustom: true,
|
|
115
|
+
action: (editor) => {
|
|
116
|
+
try {
|
|
117
|
+
const { createDiagramNodes } = require("./waka-diagram-block")
|
|
118
|
+
editor.insertNodes(createDiagramNodes())
|
|
119
|
+
} catch { /* */ }
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
// AI Assist
|
|
123
|
+
{
|
|
124
|
+
key: "ai-assist",
|
|
125
|
+
label: "AI Assistant",
|
|
126
|
+
description: "Ask the AI to generate content for your document",
|
|
127
|
+
icon: (() => {
|
|
128
|
+
const { Sparkles } = require("lucide-react") as { Sparkles: React.ComponentType<{ className?: string }> }
|
|
129
|
+
return Sparkles
|
|
130
|
+
})(),
|
|
131
|
+
group: "WakaStart Blocks",
|
|
132
|
+
keywords: ["ai", "ia", "assistant", "generate", "write", "help", "claude"],
|
|
133
|
+
isCustom: true,
|
|
134
|
+
action: (editor) => {
|
|
135
|
+
try {
|
|
136
|
+
const { createAiAssistNodes } = require("./waka-ai-assist-block")
|
|
137
|
+
editor.insertNodes(createAiAssistNodes())
|
|
138
|
+
} catch { /* */ }
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
// Version Diff
|
|
142
|
+
{
|
|
143
|
+
key: "version-diff",
|
|
144
|
+
label: "Version Diff",
|
|
145
|
+
description: "Compare two versions of text with visual diff",
|
|
146
|
+
icon: (() => {
|
|
147
|
+
const { GitCompare } = require("lucide-react") as { GitCompare: React.ComponentType<{ className?: string }> }
|
|
148
|
+
return GitCompare
|
|
149
|
+
})(),
|
|
150
|
+
group: "WakaStart Blocks",
|
|
151
|
+
keywords: ["diff", "version", "compare", "change", "history", "git"],
|
|
152
|
+
isCustom: true,
|
|
153
|
+
action: (editor) => {
|
|
154
|
+
try {
|
|
155
|
+
const { createVersionDiffNodes } = require("./waka-version-diff-block")
|
|
156
|
+
editor.insertNodes(createVersionDiffNodes())
|
|
157
|
+
} catch { /* */ }
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
// Embed
|
|
161
|
+
{
|
|
162
|
+
key: "embed",
|
|
163
|
+
label: "Embed",
|
|
164
|
+
description: "Embed external content (Figma, YouTube, CodeSandbox...)",
|
|
165
|
+
icon: (() => {
|
|
166
|
+
const { ExternalLink } = require("lucide-react") as { ExternalLink: React.ComponentType<{ className?: string }> }
|
|
167
|
+
return ExternalLink
|
|
168
|
+
})(),
|
|
169
|
+
group: "WakaStart Blocks",
|
|
170
|
+
keywords: ["embed", "figma", "youtube", "codesandbox", "loom", "iframe", "video"],
|
|
171
|
+
isCustom: true,
|
|
172
|
+
action: (editor) => {
|
|
173
|
+
try {
|
|
174
|
+
const { createEmbedNodes } = require("./waka-embed-block")
|
|
175
|
+
editor.insertNodes(createEmbedNodes(""))
|
|
176
|
+
} catch { /* */ }
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
// Code Playground
|
|
180
|
+
{
|
|
181
|
+
key: "code-playground",
|
|
182
|
+
label: "Code Playground",
|
|
183
|
+
description: "Executable code block with console output",
|
|
184
|
+
icon: (() => {
|
|
185
|
+
const { Terminal } = require("lucide-react") as { Terminal: React.ComponentType<{ className?: string }> }
|
|
186
|
+
return Terminal
|
|
187
|
+
})(),
|
|
188
|
+
group: "WakaStart Blocks",
|
|
189
|
+
keywords: ["code", "playground", "run", "execute", "terminal", "console", "sandbox"],
|
|
190
|
+
isCustom: true,
|
|
191
|
+
action: (editor) => {
|
|
192
|
+
try {
|
|
193
|
+
const { createCodePlaygroundNodes } = require("./waka-code-playground-block")
|
|
194
|
+
editor.insertNodes(createCodePlaygroundNodes())
|
|
195
|
+
} catch { /* */ }
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
// Comment Thread
|
|
199
|
+
{
|
|
200
|
+
key: "comment-thread",
|
|
201
|
+
label: "Comment Thread",
|
|
202
|
+
description: "Inline discussion thread (like Google Docs)",
|
|
203
|
+
icon: (() => {
|
|
204
|
+
const { MessageSquare } = require("lucide-react") as { MessageSquare: React.ComponentType<{ className?: string }> }
|
|
205
|
+
return MessageSquare
|
|
206
|
+
})(),
|
|
207
|
+
group: "WakaStart Blocks",
|
|
208
|
+
keywords: ["comment", "thread", "discussion", "review", "feedback", "note"],
|
|
209
|
+
isCustom: true,
|
|
210
|
+
action: (editor) => {
|
|
211
|
+
try {
|
|
212
|
+
const { createCommentThreadNodes } = require("./waka-comment-thread-block")
|
|
213
|
+
editor.insertNodes(createCommentThreadNodes())
|
|
214
|
+
} catch { /* */ }
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
]
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Get all slash commands: standard Plate commands + WakaStart custom blocks.
|
|
221
|
+
*/
|
|
222
|
+
export function getAllWakaSlashCommands(extraCommands?: WakaSlashBlockCommand[]): WakaSlashBlockCommand[] {
|
|
223
|
+
return [
|
|
224
|
+
...DEFAULT_SLASH_COMMANDS.map((cmd) => ({ ...cmd, isCustom: false })),
|
|
225
|
+
...WAKA_BLOCK_COMMANDS,
|
|
226
|
+
...(extraCommands || []),
|
|
227
|
+
]
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ============================================================================
|
|
231
|
+
// Component
|
|
232
|
+
// ============================================================================
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* WakaSlashMenuBlock - An enhanced slash menu that includes all standard Plate
|
|
236
|
+
* blocks PLUS WakaStart-specific custom blocks (User Story, API Endpoint,
|
|
237
|
+
* Diagram, AI Assist, Code Playground, etc.).
|
|
238
|
+
*
|
|
239
|
+
* This replaces the standard WakaSlashMenu when you want the full WakaStart
|
|
240
|
+
* block vocabulary available in your editor.
|
|
241
|
+
*
|
|
242
|
+
* @example
|
|
243
|
+
* ```tsx
|
|
244
|
+
* // In your Plate slash plugin configuration:
|
|
245
|
+
* const slashPlugins = await createWakaSlashPlugins()
|
|
246
|
+
*
|
|
247
|
+
* // Or use the menu component standalone:
|
|
248
|
+
* <WakaSlashMenuBlock
|
|
249
|
+
* query={searchQuery}
|
|
250
|
+
* onSelect={(cmd) => cmd.action(editor)}
|
|
251
|
+
* />
|
|
252
|
+
* ```
|
|
253
|
+
*/
|
|
254
|
+
export function WakaSlashMenuBlock({
|
|
255
|
+
commands,
|
|
256
|
+
query = "",
|
|
257
|
+
onSelect,
|
|
258
|
+
activeIndex: controlledIndex,
|
|
259
|
+
onActiveIndexChange,
|
|
260
|
+
className,
|
|
261
|
+
}: WakaSlashMenuBlockProps) {
|
|
262
|
+
const allCommands = React.useMemo(
|
|
263
|
+
() => commands || getAllWakaSlashCommands(),
|
|
264
|
+
[commands]
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
const [internalIndex, setInternalIndex] = React.useState(0)
|
|
268
|
+
const activeIndex = controlledIndex ?? internalIndex
|
|
269
|
+
const setActiveIndex = onActiveIndexChange || setInternalIndex
|
|
270
|
+
|
|
271
|
+
// Filter commands
|
|
272
|
+
const filtered = React.useMemo(() => {
|
|
273
|
+
if (!query) return allCommands
|
|
274
|
+
const q = query.toLowerCase()
|
|
275
|
+
return allCommands.filter(
|
|
276
|
+
(cmd) =>
|
|
277
|
+
cmd.label.toLowerCase().includes(q) ||
|
|
278
|
+
cmd.key.toLowerCase().includes(q) ||
|
|
279
|
+
cmd.description?.toLowerCase().includes(q) ||
|
|
280
|
+
cmd.keywords?.some((kw) => kw.toLowerCase().includes(q))
|
|
281
|
+
)
|
|
282
|
+
}, [allCommands, query])
|
|
283
|
+
|
|
284
|
+
// Group commands
|
|
285
|
+
const groups = React.useMemo(() => {
|
|
286
|
+
const map = new Map<string, WakaSlashBlockCommand[]>()
|
|
287
|
+
for (const cmd of filtered) {
|
|
288
|
+
const group = cmd.group || "Other"
|
|
289
|
+
if (!map.has(group)) map.set(group, [])
|
|
290
|
+
map.get(group)!.push(cmd)
|
|
291
|
+
}
|
|
292
|
+
return map
|
|
293
|
+
}, [filtered])
|
|
294
|
+
|
|
295
|
+
// Reset active index on filter change
|
|
296
|
+
React.useEffect(() => setActiveIndex(0), [query])
|
|
297
|
+
|
|
298
|
+
// Keyboard navigation
|
|
299
|
+
const handleKeyDown = React.useCallback(
|
|
300
|
+
(e: React.KeyboardEvent) => {
|
|
301
|
+
if (e.key === "ArrowDown") {
|
|
302
|
+
e.preventDefault()
|
|
303
|
+
setActiveIndex(Math.min(activeIndex + 1, filtered.length - 1))
|
|
304
|
+
} else if (e.key === "ArrowUp") {
|
|
305
|
+
e.preventDefault()
|
|
306
|
+
setActiveIndex(Math.max(activeIndex - 1, 0))
|
|
307
|
+
} else if (e.key === "Enter" && filtered[activeIndex]) {
|
|
308
|
+
e.preventDefault()
|
|
309
|
+
onSelect?.(filtered[activeIndex])
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
[filtered, activeIndex, onSelect, setActiveIndex]
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
return (
|
|
316
|
+
<div
|
|
317
|
+
className={cn(
|
|
318
|
+
"w-80 max-h-96 overflow-y-auto",
|
|
319
|
+
"rounded-lg border bg-popover shadow-xl",
|
|
320
|
+
"p-1",
|
|
321
|
+
className
|
|
322
|
+
)}
|
|
323
|
+
role="listbox"
|
|
324
|
+
onKeyDown={handleKeyDown}
|
|
325
|
+
aria-label="Slash commands"
|
|
326
|
+
>
|
|
327
|
+
{filtered.length === 0 && (
|
|
328
|
+
<div className="px-3 py-6 text-center text-sm text-muted-foreground">
|
|
329
|
+
No blocks found for "{query}"
|
|
330
|
+
</div>
|
|
331
|
+
)}
|
|
332
|
+
|
|
333
|
+
{Array.from(groups.entries()).map(([groupName, cmds]) => (
|
|
334
|
+
<div key={groupName} className="mb-1">
|
|
335
|
+
<div className="px-2 py-1.5 text-[10px] font-bold uppercase tracking-wider text-muted-foreground/70 flex items-center gap-2">
|
|
336
|
+
{groupName}
|
|
337
|
+
{groupName === "WakaStart Blocks" && (
|
|
338
|
+
<span className="px-1 py-0.5 rounded bg-primary/10 text-primary text-[8px] font-bold normal-case tracking-normal">
|
|
339
|
+
Custom
|
|
340
|
+
</span>
|
|
341
|
+
)}
|
|
342
|
+
</div>
|
|
343
|
+
{cmds.map((cmd) => {
|
|
344
|
+
const globalIndex = filtered.indexOf(cmd)
|
|
345
|
+
const Icon = cmd.icon as React.ComponentType<{ className?: string }>
|
|
346
|
+
return (
|
|
347
|
+
<button
|
|
348
|
+
key={cmd.key}
|
|
349
|
+
role="option"
|
|
350
|
+
aria-selected={globalIndex === activeIndex}
|
|
351
|
+
className={cn(
|
|
352
|
+
"flex w-full items-center gap-3 rounded-md px-2 py-2 text-left",
|
|
353
|
+
"text-sm transition-colors cursor-pointer",
|
|
354
|
+
globalIndex === activeIndex
|
|
355
|
+
? "bg-accent text-accent-foreground"
|
|
356
|
+
: "hover:bg-accent/50"
|
|
357
|
+
)}
|
|
358
|
+
onMouseEnter={() => setActiveIndex(globalIndex)}
|
|
359
|
+
onClick={() => onSelect?.(cmd)}
|
|
360
|
+
>
|
|
361
|
+
<div className={cn(
|
|
362
|
+
"flex h-8 w-8 shrink-0 items-center justify-center rounded-md border",
|
|
363
|
+
cmd.isCustom
|
|
364
|
+
? "border-primary/20 bg-primary/5"
|
|
365
|
+
: "border-border bg-background"
|
|
366
|
+
)}>
|
|
367
|
+
{typeof Icon === "function" ? <Icon className="h-4 w-4" /> : Icon}
|
|
368
|
+
</div>
|
|
369
|
+
<div className="min-w-0 flex-1">
|
|
370
|
+
<div className="flex items-center gap-1.5">
|
|
371
|
+
<span className="font-medium text-sm">{cmd.label}</span>
|
|
372
|
+
{cmd.isCustom && (
|
|
373
|
+
<span className="h-1.5 w-1.5 rounded-full bg-primary flex-shrink-0" />
|
|
374
|
+
)}
|
|
375
|
+
</div>
|
|
376
|
+
{cmd.description && (
|
|
377
|
+
<div className="text-[11px] text-muted-foreground truncate mt-0.5">
|
|
378
|
+
{cmd.description}
|
|
379
|
+
</div>
|
|
380
|
+
)}
|
|
381
|
+
</div>
|
|
382
|
+
</button>
|
|
383
|
+
)
|
|
384
|
+
})}
|
|
385
|
+
</div>
|
|
386
|
+
))}
|
|
387
|
+
</div>
|
|
388
|
+
)
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
WakaSlashMenuBlock.displayName = "WakaSlashMenuBlock"
|
|
392
|
+
|
|
393
|
+
// ============================================================================
|
|
394
|
+
// Plugin Helper
|
|
395
|
+
// ============================================================================
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Creates slash command plugin configuration with all WakaStart blocks registered.
|
|
399
|
+
*
|
|
400
|
+
* @example
|
|
401
|
+
* ```ts
|
|
402
|
+
* const plugins = await createWakaSlashPlugins()
|
|
403
|
+
* // Add to editor plugins array
|
|
404
|
+
* ```
|
|
405
|
+
*/
|
|
406
|
+
export async function createWakaSlashPlugins(
|
|
407
|
+
extraCommands?: WakaSlashBlockCommand[]
|
|
408
|
+
) {
|
|
409
|
+
try {
|
|
410
|
+
const { SlashPlugin, SlashInputPlugin } = await import("@platejs/combobox/react")
|
|
411
|
+
const allCommands = getAllWakaSlashCommands(extraCommands)
|
|
412
|
+
|
|
413
|
+
return [
|
|
414
|
+
SlashPlugin.configure({
|
|
415
|
+
options: {
|
|
416
|
+
trigger: "/",
|
|
417
|
+
triggerPreviousCharPattern: /^$|^[\s"']$/,
|
|
418
|
+
rules: allCommands.map((cmd) => ({
|
|
419
|
+
key: cmd.key,
|
|
420
|
+
text: cmd.label,
|
|
421
|
+
keywords: cmd.keywords,
|
|
422
|
+
onSelect: cmd.action,
|
|
423
|
+
})),
|
|
424
|
+
},
|
|
425
|
+
}),
|
|
426
|
+
SlashInputPlugin,
|
|
427
|
+
]
|
|
428
|
+
} catch {
|
|
429
|
+
console.warn("[WakaSlashMenuBlock] @platejs/combobox not installed, slash commands disabled")
|
|
430
|
+
return []
|
|
431
|
+
}
|
|
432
|
+
}
|