@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,331 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { cn } from "../../../utils/cn"
|
|
5
|
+
import type { PlateElementProps } from "../waka-editor-elements"
|
|
6
|
+
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Types
|
|
9
|
+
// ============================================================================
|
|
10
|
+
|
|
11
|
+
export const CODE_PLAYGROUND_BLOCK_TYPE = "code_playground" as const
|
|
12
|
+
|
|
13
|
+
/** Supported programming languages */
|
|
14
|
+
export type PlaygroundLanguage = "javascript" | "typescript" | "python" | "html" | "css" | "json" | "sql" | "bash" | "go" | "rust"
|
|
15
|
+
|
|
16
|
+
/** Execution status */
|
|
17
|
+
export type ExecutionStatus = "idle" | "running" | "success" | "error"
|
|
18
|
+
|
|
19
|
+
/** Slate node for code playground blocks */
|
|
20
|
+
export interface CodePlaygroundElement {
|
|
21
|
+
type: typeof CODE_PLAYGROUND_BLOCK_TYPE
|
|
22
|
+
/** Source code */
|
|
23
|
+
code: string
|
|
24
|
+
/** Programming language */
|
|
25
|
+
language: PlaygroundLanguage
|
|
26
|
+
/** Title / filename */
|
|
27
|
+
title?: string
|
|
28
|
+
/** Last execution output */
|
|
29
|
+
output?: string
|
|
30
|
+
/** Last execution error */
|
|
31
|
+
errorOutput?: string
|
|
32
|
+
/** Execution status */
|
|
33
|
+
executionStatus: ExecutionStatus
|
|
34
|
+
/** Whether to show line numbers */
|
|
35
|
+
showLineNumbers: boolean
|
|
36
|
+
/** Whether the code is read-only */
|
|
37
|
+
readOnly: boolean
|
|
38
|
+
children: Array<{ text: string }>
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface WakaCodePlaygroundBlockProps extends PlateElementProps {
|
|
42
|
+
element?: CodePlaygroundElement & Record<string, unknown>
|
|
43
|
+
/** Callback to execute code. Returns stdout output or throws with stderr. */
|
|
44
|
+
onExecute?: (code: string, language: PlaygroundLanguage) => Promise<string>
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ============================================================================
|
|
48
|
+
// Language Config
|
|
49
|
+
// ============================================================================
|
|
50
|
+
|
|
51
|
+
const LANGUAGE_CONFIG: Record<PlaygroundLanguage, { label: string; color: string; extension: string }> = {
|
|
52
|
+
javascript: { label: "JavaScript", color: "text-yellow-500", extension: ".js" },
|
|
53
|
+
typescript: { label: "TypeScript", color: "text-blue-500", extension: ".ts" },
|
|
54
|
+
python: { label: "Python", color: "text-green-500", extension: ".py" },
|
|
55
|
+
html: { label: "HTML", color: "text-orange-500", extension: ".html" },
|
|
56
|
+
css: { label: "CSS", color: "text-pink-500", extension: ".css" },
|
|
57
|
+
json: { label: "JSON", color: "text-gray-400", extension: ".json" },
|
|
58
|
+
sql: { label: "SQL", color: "text-cyan-500", extension: ".sql" },
|
|
59
|
+
bash: { label: "Bash", color: "text-emerald-500", extension: ".sh" },
|
|
60
|
+
go: { label: "Go", color: "text-cyan-600", extension: ".go" },
|
|
61
|
+
rust: { label: "Rust", color: "text-orange-600", extension: ".rs" },
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ============================================================================
|
|
65
|
+
// Simple syntax highlighting (keyword-based)
|
|
66
|
+
// ============================================================================
|
|
67
|
+
|
|
68
|
+
const KEYWORD_PATTERNS: Record<string, RegExp> = {
|
|
69
|
+
keyword: /\b(const|let|var|function|return|if|else|for|while|do|switch|case|break|continue|import|export|from|default|class|extends|new|this|typeof|instanceof|try|catch|finally|throw|async|await|yield|in|of|with|as|is|type|interface|enum|namespace|module|declare|readonly|abstract|implements|package|private|protected|public|static|super|void|null|undefined|true|false|NaN|Infinity|def|print|range|len|self|None|True|False|SELECT|FROM|WHERE|INSERT|UPDATE|DELETE|CREATE|TABLE|INDEX|JOIN|ON|AND|OR|NOT|ORDER|BY|GROUP|HAVING|LIMIT|OFFSET|fn|pub|mod|use|struct|impl|trait|mut|ref|match|loop|break|continue|move|dyn|Box|Vec|String|Option|Result|Some|None|Ok|Err|func|go|chan|defer|fallthrough|select|map|make|append|package|import)\b/g,
|
|
70
|
+
string: /(["'`])(?:(?!\1|\\).|\\.)*?\1/g,
|
|
71
|
+
comment: /\/\/.*$|\/\*[\s\S]*?\*\/|#.*$/gm,
|
|
72
|
+
number: /\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/gi,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function highlightCode(code: string): React.ReactNode[] {
|
|
76
|
+
// Simple token-based highlighting (not a full parser)
|
|
77
|
+
// For production, integrate with Prism.js or Shiki
|
|
78
|
+
const lines = code.split("\n")
|
|
79
|
+
return lines.map((line, i) => (
|
|
80
|
+
<div key={i} className="table-row">
|
|
81
|
+
<span className="table-cell w-8 text-right pr-3 text-muted-foreground/40 select-none text-[11px]">
|
|
82
|
+
{i + 1}
|
|
83
|
+
</span>
|
|
84
|
+
<span className="table-cell">
|
|
85
|
+
{line || " "}
|
|
86
|
+
</span>
|
|
87
|
+
</div>
|
|
88
|
+
))
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ============================================================================
|
|
92
|
+
// Element Component
|
|
93
|
+
// ============================================================================
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* WakaCodePlaygroundBlock - A Plate.js block with an inline code editor and
|
|
97
|
+
* sandboxed execution. Users write code and can run it, seeing output in
|
|
98
|
+
* an integrated console panel.
|
|
99
|
+
*
|
|
100
|
+
* Execution is sandboxed via the `onExecute` callback provided by the
|
|
101
|
+
* consuming application (e.g. using Web Workers, iframes, or server-side eval).
|
|
102
|
+
*
|
|
103
|
+
* Register in Plate editor:
|
|
104
|
+
* ```ts
|
|
105
|
+
* components: {
|
|
106
|
+
* [CODE_PLAYGROUND_BLOCK_TYPE]: (props) => (
|
|
107
|
+
* <WakaCodePlaygroundBlock {...props} onExecute={myExecutor} />
|
|
108
|
+
* ),
|
|
109
|
+
* }
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
export function WakaCodePlaygroundBlock({
|
|
113
|
+
attributes,
|
|
114
|
+
children,
|
|
115
|
+
element,
|
|
116
|
+
className,
|
|
117
|
+
onExecute,
|
|
118
|
+
}: WakaCodePlaygroundBlockProps) {
|
|
119
|
+
const el = element as CodePlaygroundElement | undefined
|
|
120
|
+
const code = el?.code || ""
|
|
121
|
+
const language = el?.language || "javascript"
|
|
122
|
+
const langConfig = LANGUAGE_CONFIG[language]
|
|
123
|
+
const output = el?.output || ""
|
|
124
|
+
const errorOutput = el?.errorOutput || ""
|
|
125
|
+
const executionStatus = el?.executionStatus || "idle"
|
|
126
|
+
const title = el?.title || `main${langConfig.extension}`
|
|
127
|
+
|
|
128
|
+
const [showOutput, setShowOutput] = React.useState(!!output || !!errorOutput)
|
|
129
|
+
const [localStatus, setLocalStatus] = React.useState(executionStatus)
|
|
130
|
+
|
|
131
|
+
const handleRun = React.useCallback(async () => {
|
|
132
|
+
if (!onExecute) return
|
|
133
|
+
setLocalStatus("running")
|
|
134
|
+
setShowOutput(true)
|
|
135
|
+
try {
|
|
136
|
+
await onExecute(code, language)
|
|
137
|
+
setLocalStatus("success")
|
|
138
|
+
} catch {
|
|
139
|
+
setLocalStatus("error")
|
|
140
|
+
}
|
|
141
|
+
}, [code, language, onExecute])
|
|
142
|
+
|
|
143
|
+
const displayStatus = localStatus !== executionStatus ? localStatus : executionStatus
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<div
|
|
147
|
+
{...attributes}
|
|
148
|
+
contentEditable={false}
|
|
149
|
+
className={cn(
|
|
150
|
+
"my-4 rounded-lg overflow-hidden border border-gray-800 dark:border-gray-700 shadow-lg",
|
|
151
|
+
className
|
|
152
|
+
)}
|
|
153
|
+
>
|
|
154
|
+
{/* Title bar (VS Code-like) */}
|
|
155
|
+
<div className="flex items-center justify-between px-3 py-1.5 bg-gray-900 dark:bg-gray-800 border-b border-gray-800 dark:border-gray-700">
|
|
156
|
+
<div className="flex items-center gap-3">
|
|
157
|
+
{/* Traffic lights */}
|
|
158
|
+
<div className="flex items-center gap-1.5" aria-hidden="true">
|
|
159
|
+
<div className="h-3 w-3 rounded-full bg-red-500/80" />
|
|
160
|
+
<div className="h-3 w-3 rounded-full bg-yellow-500/80" />
|
|
161
|
+
<div className="h-3 w-3 rounded-full bg-green-500/80" />
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
{/* File name tab */}
|
|
165
|
+
<div className="flex items-center gap-1.5 px-2 py-0.5 rounded bg-gray-800 dark:bg-gray-700">
|
|
166
|
+
<span className={cn("text-[10px] font-bold", langConfig.color)}>
|
|
167
|
+
{langConfig.label}
|
|
168
|
+
</span>
|
|
169
|
+
<span className="text-xs font-mono text-gray-300">{title}</span>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
<div className="flex items-center gap-2">
|
|
174
|
+
{/* Status indicator */}
|
|
175
|
+
{displayStatus !== "idle" && (
|
|
176
|
+
<div className="flex items-center gap-1">
|
|
177
|
+
{displayStatus === "running" && (
|
|
178
|
+
<svg className="h-3 w-3 animate-spin text-amber-400" viewBox="0 0 24 24" fill="none">
|
|
179
|
+
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
|
|
180
|
+
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
|
|
181
|
+
</svg>
|
|
182
|
+
)}
|
|
183
|
+
{displayStatus === "success" && (
|
|
184
|
+
<svg className="h-3 w-3 text-emerald-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2}>
|
|
185
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
|
|
186
|
+
</svg>
|
|
187
|
+
)}
|
|
188
|
+
{displayStatus === "error" && (
|
|
189
|
+
<svg className="h-3 w-3 text-red-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2}>
|
|
190
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
191
|
+
</svg>
|
|
192
|
+
)}
|
|
193
|
+
</div>
|
|
194
|
+
)}
|
|
195
|
+
|
|
196
|
+
{/* Run button */}
|
|
197
|
+
{onExecute && (
|
|
198
|
+
<button
|
|
199
|
+
type="button"
|
|
200
|
+
onClick={handleRun}
|
|
201
|
+
disabled={displayStatus === "running" || !code.trim()}
|
|
202
|
+
className={cn(
|
|
203
|
+
"flex items-center gap-1 px-2.5 py-1 rounded text-[10px] font-semibold transition-all",
|
|
204
|
+
displayStatus === "running"
|
|
205
|
+
? "bg-amber-500/20 text-amber-300 cursor-wait"
|
|
206
|
+
: "bg-emerald-500/20 text-emerald-300 hover:bg-emerald-500/30",
|
|
207
|
+
!code.trim() && "opacity-40 cursor-not-allowed"
|
|
208
|
+
)}
|
|
209
|
+
aria-label="Run code"
|
|
210
|
+
>
|
|
211
|
+
<svg className="h-3 w-3" viewBox="0 0 24 24" fill="currentColor">
|
|
212
|
+
<path d="M8 5v14l11-7z" />
|
|
213
|
+
</svg>
|
|
214
|
+
{displayStatus === "running" ? "Running..." : "Run"}
|
|
215
|
+
</button>
|
|
216
|
+
)}
|
|
217
|
+
|
|
218
|
+
{/* Toggle output */}
|
|
219
|
+
<button
|
|
220
|
+
type="button"
|
|
221
|
+
onClick={() => setShowOutput(!showOutput)}
|
|
222
|
+
className={cn(
|
|
223
|
+
"px-2 py-1 rounded text-[10px] font-medium transition-colors",
|
|
224
|
+
showOutput
|
|
225
|
+
? "bg-gray-700 text-gray-200"
|
|
226
|
+
: "text-gray-400 hover:text-gray-200"
|
|
227
|
+
)}
|
|
228
|
+
>
|
|
229
|
+
Console
|
|
230
|
+
</button>
|
|
231
|
+
</div>
|
|
232
|
+
</div>
|
|
233
|
+
|
|
234
|
+
{/* Code area */}
|
|
235
|
+
<div className="bg-gray-950 dark:bg-[#0d1117] overflow-x-auto">
|
|
236
|
+
<pre className="p-4 text-[12px] font-mono leading-relaxed text-gray-100 min-h-[80px]">
|
|
237
|
+
<code className="table w-full">
|
|
238
|
+
{highlightCode(code)}
|
|
239
|
+
</code>
|
|
240
|
+
</pre>
|
|
241
|
+
</div>
|
|
242
|
+
|
|
243
|
+
{/* Output console */}
|
|
244
|
+
{showOutput && (output || errorOutput || displayStatus === "running") && (
|
|
245
|
+
<div className="border-t border-gray-800 dark:border-gray-700">
|
|
246
|
+
<div className="flex items-center justify-between px-3 py-1 bg-gray-900 dark:bg-gray-800">
|
|
247
|
+
<span className="text-[10px] font-semibold uppercase tracking-wider text-gray-400">
|
|
248
|
+
Output
|
|
249
|
+
</span>
|
|
250
|
+
{(output || errorOutput) && (
|
|
251
|
+
<button
|
|
252
|
+
type="button"
|
|
253
|
+
className="text-[10px] text-gray-500 hover:text-gray-300 transition-colors"
|
|
254
|
+
aria-label="Clear output"
|
|
255
|
+
>
|
|
256
|
+
Clear
|
|
257
|
+
</button>
|
|
258
|
+
)}
|
|
259
|
+
</div>
|
|
260
|
+
<div className="bg-gray-950 dark:bg-[#0d1117] p-3 max-h-[200px] overflow-y-auto">
|
|
261
|
+
{displayStatus === "running" ? (
|
|
262
|
+
<div className="flex items-center gap-2 text-amber-400 text-xs font-mono">
|
|
263
|
+
<svg className="h-3 w-3 animate-spin" viewBox="0 0 24 24" fill="none">
|
|
264
|
+
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
|
|
265
|
+
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
|
|
266
|
+
</svg>
|
|
267
|
+
Executing...
|
|
268
|
+
</div>
|
|
269
|
+
) : errorOutput ? (
|
|
270
|
+
<pre className="text-[12px] font-mono text-red-400 whitespace-pre-wrap">{errorOutput}</pre>
|
|
271
|
+
) : output ? (
|
|
272
|
+
<pre className="text-[12px] font-mono text-gray-200 whitespace-pre-wrap">{output}</pre>
|
|
273
|
+
) : null}
|
|
274
|
+
</div>
|
|
275
|
+
</div>
|
|
276
|
+
)}
|
|
277
|
+
|
|
278
|
+
{/* Hidden Slate children */}
|
|
279
|
+
<div className="hidden">{children}</div>
|
|
280
|
+
</div>
|
|
281
|
+
)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
WakaCodePlaygroundBlock.displayName = "WakaCodePlaygroundBlock"
|
|
285
|
+
|
|
286
|
+
// ============================================================================
|
|
287
|
+
// Node Factory
|
|
288
|
+
// ============================================================================
|
|
289
|
+
|
|
290
|
+
export function createCodePlaygroundNodes(options?: {
|
|
291
|
+
code?: string
|
|
292
|
+
language?: PlaygroundLanguage
|
|
293
|
+
title?: string
|
|
294
|
+
}): CodePlaygroundElement[] {
|
|
295
|
+
return [
|
|
296
|
+
{
|
|
297
|
+
type: CODE_PLAYGROUND_BLOCK_TYPE,
|
|
298
|
+
code: options?.code || '// Write your code here\nconsole.log("Hello, WakaStart!");',
|
|
299
|
+
language: options?.language || "javascript",
|
|
300
|
+
title: options?.title,
|
|
301
|
+
output: "",
|
|
302
|
+
errorOutput: "",
|
|
303
|
+
executionStatus: "idle",
|
|
304
|
+
showLineNumbers: true,
|
|
305
|
+
readOnly: false,
|
|
306
|
+
children: [{ text: "" }],
|
|
307
|
+
},
|
|
308
|
+
]
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export async function createCodePlaygroundPlugin(
|
|
312
|
+
onExecute?: (code: string, language: PlaygroundLanguage) => Promise<string>
|
|
313
|
+
) {
|
|
314
|
+
try {
|
|
315
|
+
const { createPlatePlugin } = await import("platejs/react")
|
|
316
|
+
return createPlatePlugin({
|
|
317
|
+
key: CODE_PLAYGROUND_BLOCK_TYPE,
|
|
318
|
+
node: {
|
|
319
|
+
isElement: true,
|
|
320
|
+
isVoid: true,
|
|
321
|
+
type: CODE_PLAYGROUND_BLOCK_TYPE,
|
|
322
|
+
component: (props: WakaCodePlaygroundBlockProps) => (
|
|
323
|
+
<WakaCodePlaygroundBlock {...props} onExecute={onExecute} />
|
|
324
|
+
),
|
|
325
|
+
},
|
|
326
|
+
})
|
|
327
|
+
} catch {
|
|
328
|
+
console.warn("[WakaCodePlaygroundBlock] platejs not installed")
|
|
329
|
+
return null
|
|
330
|
+
}
|
|
331
|
+
}
|