@aster-ui/prefixed 0.12.86 → 0.12.87
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/components/CodeEditor.js +75 -75
- package/dist/components/CodeEditor.js.map +1 -1
- package/dist/components/ContextMenu.d.ts +5 -4
- package/dist/components/ContextMenu.js +112 -119
- package/dist/components/ContextMenu.js.map +1 -1
- package/dist/components/Terminal.js +47 -44
- package/dist/components/Terminal.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
import { jsx as N } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef as O, useRef as d, useEffect as g } from "react";
|
|
3
3
|
import { EditorState as b } from "@codemirror/state";
|
|
4
|
-
import { EditorView as
|
|
5
|
-
import { indentOnInput as Y, syntaxHighlighting as Z, defaultHighlightStyle as
|
|
6
|
-
import { history as
|
|
7
|
-
import { highlightSelectionMatches as
|
|
8
|
-
import { closeBracketsKeymap as
|
|
9
|
-
import { lintKeymap as
|
|
4
|
+
import { EditorView as t, highlightSpecialChars as W, drawSelection as D, dropCursor as I, rectangularSelection as J, crosshairCursor as P, keymap as R, lineNumbers as Q, highlightActiveLine as U, highlightActiveLineGutter as X } from "@codemirror/view";
|
|
5
|
+
import { indentOnInput as Y, syntaxHighlighting as Z, defaultHighlightStyle as ee, foldKeymap as te, foldGutter as oe, bracketMatching as re } from "@codemirror/language";
|
|
6
|
+
import { history as ie, defaultKeymap as ne, historyKeymap as ce, indentWithTab as se } from "@codemirror/commands";
|
|
7
|
+
import { highlightSelectionMatches as le, searchKeymap as ae } from "@codemirror/search";
|
|
8
|
+
import { closeBracketsKeymap as ue, completionKeymap as me, closeBrackets as fe, autocompletion as pe } from "@codemirror/autocomplete";
|
|
9
|
+
import { lintKeymap as he } from "@codemirror/lint";
|
|
10
10
|
import { javascript as s } from "@codemirror/lang-javascript";
|
|
11
|
-
import { python as
|
|
12
|
-
import { html as
|
|
13
|
-
import { css as
|
|
14
|
-
import { json as
|
|
15
|
-
import { markdown as
|
|
16
|
-
import { sql as
|
|
17
|
-
import { xml as
|
|
18
|
-
import { useConfig as
|
|
19
|
-
const
|
|
11
|
+
import { python as de } from "@codemirror/lang-python";
|
|
12
|
+
import { html as ge } from "@codemirror/lang-html";
|
|
13
|
+
import { css as be } from "@codemirror/lang-css";
|
|
14
|
+
import { json as ve } from "@codemirror/lang-json";
|
|
15
|
+
import { markdown as ye } from "@codemirror/lang-markdown";
|
|
16
|
+
import { sql as Se } from "@codemirror/lang-sql";
|
|
17
|
+
import { xml as ke } from "@codemirror/lang-xml";
|
|
18
|
+
import { useConfig as xe } from "../providers/ConfigProvider.js";
|
|
19
|
+
const Ce = {
|
|
20
20
|
xs: { fontSize: "11px", lineHeight: "1.4" },
|
|
21
21
|
sm: { fontSize: "12px", lineHeight: "1.5" },
|
|
22
22
|
md: { fontSize: "14px", lineHeight: "1.6" },
|
|
23
23
|
lg: { fontSize: "16px", lineHeight: "1.6" },
|
|
24
24
|
xl: { fontSize: "18px", lineHeight: "1.7" }
|
|
25
25
|
};
|
|
26
|
-
function
|
|
26
|
+
function we(o) {
|
|
27
27
|
switch (o) {
|
|
28
28
|
case "javascript":
|
|
29
29
|
return s();
|
|
@@ -34,24 +34,24 @@ function zt(o) {
|
|
|
34
34
|
case "tsx":
|
|
35
35
|
return s({ jsx: !0, typescript: !0 });
|
|
36
36
|
case "python":
|
|
37
|
-
return
|
|
37
|
+
return de();
|
|
38
38
|
case "html":
|
|
39
|
-
return
|
|
39
|
+
return ge();
|
|
40
40
|
case "css":
|
|
41
|
-
return
|
|
41
|
+
return be();
|
|
42
42
|
case "json":
|
|
43
|
-
return
|
|
43
|
+
return ve();
|
|
44
44
|
case "markdown":
|
|
45
|
-
return
|
|
45
|
+
return ye();
|
|
46
46
|
case "sql":
|
|
47
|
-
return
|
|
47
|
+
return Se();
|
|
48
48
|
case "xml":
|
|
49
|
-
return
|
|
49
|
+
return ke();
|
|
50
50
|
default:
|
|
51
51
|
return null;
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
-
const
|
|
54
|
+
const ze = t.theme({
|
|
55
55
|
"&": {
|
|
56
56
|
backgroundColor: "oklch(var(--b1))",
|
|
57
57
|
color: "oklch(var(--bc))"
|
|
@@ -90,19 +90,19 @@ const wt = e.theme({
|
|
|
90
90
|
color: "oklch(var(--bc) / 0.4)",
|
|
91
91
|
fontStyle: "italic"
|
|
92
92
|
}
|
|
93
|
-
}),
|
|
93
|
+
}), je = O(
|
|
94
94
|
({
|
|
95
95
|
value: o = "",
|
|
96
96
|
onChange: l,
|
|
97
|
-
language:
|
|
97
|
+
language: v = "plaintext",
|
|
98
98
|
placeholder: i,
|
|
99
|
-
readOnly:
|
|
99
|
+
readOnly: y = !1,
|
|
100
100
|
autoFocus: S = !1,
|
|
101
101
|
lineNumbers: k = !0,
|
|
102
102
|
foldGutter: x = !0,
|
|
103
103
|
highlightActiveLine: C = !0,
|
|
104
|
-
bracketMatching:
|
|
105
|
-
closeBrackets:
|
|
104
|
+
bracketMatching: w = !0,
|
|
105
|
+
closeBrackets: z = !0,
|
|
106
106
|
autocompletion: j = !0,
|
|
107
107
|
indentWithTab: E = !0,
|
|
108
108
|
minHeight: a = 200,
|
|
@@ -111,41 +111,41 @@ const wt = e.theme({
|
|
|
111
111
|
bordered: G = !0,
|
|
112
112
|
extensions: H = [],
|
|
113
113
|
onEditorReady: u,
|
|
114
|
-
className:
|
|
115
|
-
"data-testid":
|
|
116
|
-
...
|
|
117
|
-
},
|
|
118
|
-
const { componentSize:
|
|
114
|
+
className: _ = "",
|
|
115
|
+
"data-testid": B = "code-editor",
|
|
116
|
+
...q
|
|
117
|
+
}, A) => {
|
|
118
|
+
const { componentSize: T } = xe(), K = $ ?? T ?? "md", m = d(null), f = d(null), p = d(l);
|
|
119
119
|
g(() => {
|
|
120
120
|
p.current = l;
|
|
121
121
|
}, [l]), g(() => {
|
|
122
122
|
if (!m.current) return;
|
|
123
|
-
const
|
|
124
|
-
|
|
123
|
+
const e = [
|
|
124
|
+
ze,
|
|
125
125
|
W(),
|
|
126
|
-
|
|
126
|
+
ie(),
|
|
127
127
|
D(),
|
|
128
128
|
I(),
|
|
129
129
|
b.allowMultipleSelections.of(!0),
|
|
130
130
|
Y(),
|
|
131
|
-
Z(
|
|
131
|
+
Z(ee, { fallback: !0 }),
|
|
132
132
|
J(),
|
|
133
133
|
P(),
|
|
134
|
-
|
|
134
|
+
le(),
|
|
135
135
|
R.of([
|
|
136
|
-
...
|
|
137
|
-
...
|
|
138
|
-
...
|
|
139
|
-
...
|
|
140
|
-
...
|
|
141
|
-
...
|
|
142
|
-
...
|
|
136
|
+
...ue,
|
|
137
|
+
...ne,
|
|
138
|
+
...ae,
|
|
139
|
+
...ce,
|
|
140
|
+
...te,
|
|
141
|
+
...me,
|
|
142
|
+
...he
|
|
143
143
|
])
|
|
144
144
|
];
|
|
145
|
-
k &&
|
|
146
|
-
const r =
|
|
147
|
-
r &&
|
|
148
|
-
|
|
145
|
+
k && e.push(Q()), x && e.push(oe()), C && (e.push(U()), e.push(X())), w && e.push(re()), z && e.push(fe()), j && e.push(pe()), E && e.push(R.of([se]));
|
|
146
|
+
const r = we(v);
|
|
147
|
+
r && e.push(r), i && (e.push(t.contentAttributes.of({ "aria-placeholder": i })), e.push(
|
|
148
|
+
t.theme({
|
|
149
149
|
".cm-content:has(.cm-line:only-child:empty)::before": {
|
|
150
150
|
content: `"${i}"`,
|
|
151
151
|
position: "absolute",
|
|
@@ -154,14 +154,14 @@ const wt = e.theme({
|
|
|
154
154
|
pointerEvents: "none"
|
|
155
155
|
}
|
|
156
156
|
})
|
|
157
|
-
)),
|
|
158
|
-
|
|
157
|
+
)), y && (e.push(b.readOnly.of(!0)), e.push(t.editable.of(!1))), e.push(
|
|
158
|
+
t.updateListener.of((M) => {
|
|
159
159
|
M.docChanged && p.current && p.current(M.state.doc.toString());
|
|
160
160
|
})
|
|
161
161
|
);
|
|
162
|
-
const h =
|
|
163
|
-
|
|
164
|
-
|
|
162
|
+
const h = Ce[K];
|
|
163
|
+
e.push(
|
|
164
|
+
t.theme({
|
|
165
165
|
".cm-content": {
|
|
166
166
|
fontSize: h.fontSize,
|
|
167
167
|
lineHeight: h.lineHeight
|
|
@@ -170,11 +170,11 @@ const wt = e.theme({
|
|
|
170
170
|
fontSize: h.fontSize
|
|
171
171
|
}
|
|
172
172
|
})
|
|
173
|
-
),
|
|
173
|
+
), e.push(...H);
|
|
174
174
|
const F = b.create({
|
|
175
175
|
doc: o,
|
|
176
|
-
extensions:
|
|
177
|
-
}), c = new
|
|
176
|
+
extensions: e
|
|
177
|
+
}), c = new t({
|
|
178
178
|
state: F,
|
|
179
179
|
parent: m.current
|
|
180
180
|
});
|
|
@@ -182,14 +182,14 @@ const wt = e.theme({
|
|
|
182
182
|
c.destroy(), f.current = null;
|
|
183
183
|
};
|
|
184
184
|
}, [
|
|
185
|
-
y,
|
|
186
|
-
i,
|
|
187
185
|
v,
|
|
186
|
+
i,
|
|
187
|
+
y,
|
|
188
188
|
k,
|
|
189
189
|
x,
|
|
190
190
|
C,
|
|
191
|
-
z,
|
|
192
191
|
w,
|
|
192
|
+
z,
|
|
193
193
|
j,
|
|
194
194
|
E,
|
|
195
195
|
K,
|
|
@@ -197,10 +197,10 @@ const wt = e.theme({
|
|
|
197
197
|
H,
|
|
198
198
|
u
|
|
199
199
|
]), g(() => {
|
|
200
|
-
const
|
|
201
|
-
if (!
|
|
202
|
-
const r =
|
|
203
|
-
o !== r &&
|
|
200
|
+
const e = f.current;
|
|
201
|
+
if (!e) return;
|
|
202
|
+
const r = e.state.doc.toString();
|
|
203
|
+
o !== r && e.dispatch({
|
|
204
204
|
changes: {
|
|
205
205
|
from: 0,
|
|
206
206
|
to: r.length,
|
|
@@ -208,25 +208,25 @@ const wt = e.theme({
|
|
|
208
208
|
}
|
|
209
209
|
});
|
|
210
210
|
}, [o]);
|
|
211
|
-
const
|
|
211
|
+
const V = typeof a == "number" ? `${a}px` : a, L = n ? typeof n == "number" ? `${n}px` : n : void 0;
|
|
212
212
|
return /* @__PURE__ */ N(
|
|
213
213
|
"div",
|
|
214
214
|
{
|
|
215
|
-
ref:
|
|
215
|
+
ref: A,
|
|
216
216
|
className: `
|
|
217
217
|
bg-base-100 rounded-lg overflow-hidden
|
|
218
218
|
${G ? "border border-base-300" : ""}
|
|
219
|
-
${
|
|
219
|
+
${_}
|
|
220
220
|
`,
|
|
221
|
-
"data-testid":
|
|
222
|
-
...
|
|
221
|
+
"data-testid": B,
|
|
222
|
+
...q,
|
|
223
223
|
children: /* @__PURE__ */ N(
|
|
224
224
|
"div",
|
|
225
225
|
{
|
|
226
226
|
ref: m,
|
|
227
|
-
className: "h-full [&_.cm-editor]:outline-none [&_.cm-editor]:h-full",
|
|
227
|
+
className: "h-full [&_.cm-editor]:outline-none [&_.cm-editor]:h-full [&_.cm-editor]:!overflow-hidden [&_.cm-scroller]:!overflow-auto",
|
|
228
228
|
style: {
|
|
229
|
-
minHeight:
|
|
229
|
+
minHeight: V,
|
|
230
230
|
maxHeight: L,
|
|
231
231
|
overflow: L ? "auto" : void 0
|
|
232
232
|
}
|
|
@@ -236,9 +236,9 @@ const wt = e.theme({
|
|
|
236
236
|
);
|
|
237
237
|
}
|
|
238
238
|
);
|
|
239
|
-
|
|
239
|
+
je.displayName = "CodeEditor";
|
|
240
240
|
export {
|
|
241
|
-
|
|
242
|
-
|
|
241
|
+
je as CodeEditor,
|
|
242
|
+
je as default
|
|
243
243
|
};
|
|
244
244
|
//# sourceMappingURL=CodeEditor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodeEditor.js","sources":["../../src/components/CodeEditor.tsx"],"sourcesContent":["import React, { forwardRef, useEffect, useRef } from 'react'\nimport { EditorState, type Extension } from '@codemirror/state'\nimport { EditorView, keymap, lineNumbers, highlightActiveLine, highlightActiveLineGutter, drawSelection, dropCursor, rectangularSelection, crosshairCursor, highlightSpecialChars } from '@codemirror/view'\nimport { defaultHighlightStyle, syntaxHighlighting, indentOnInput, bracketMatching, foldGutter, foldKeymap } from '@codemirror/language'\nimport { defaultKeymap, history, historyKeymap, indentWithTab } from '@codemirror/commands'\nimport { searchKeymap, highlightSelectionMatches } from '@codemirror/search'\nimport { autocompletion, completionKeymap, closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete'\nimport { lintKeymap } from '@codemirror/lint'\nimport { javascript } from '@codemirror/lang-javascript'\nimport { python } from '@codemirror/lang-python'\nimport { html } from '@codemirror/lang-html'\nimport { css } from '@codemirror/lang-css'\nimport { json } from '@codemirror/lang-json'\nimport { markdown } from '@codemirror/lang-markdown'\nimport { sql } from '@codemirror/lang-sql'\nimport { xml } from '@codemirror/lang-xml'\nimport { useConfig } from '../providers/ConfigProvider'\n\nexport type CodeEditorLanguage =\n | 'javascript'\n | 'typescript'\n | 'jsx'\n | 'tsx'\n | 'python'\n | 'html'\n | 'css'\n | 'json'\n | 'markdown'\n | 'sql'\n | 'xml'\n | 'plaintext'\n\nexport interface CodeEditorProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {\n /** Code content */\n value?: string\n /** Callback when content changes */\n onChange?: (value: string) => void\n /** Programming language for syntax highlighting */\n language?: CodeEditorLanguage\n /** Placeholder text when empty */\n placeholder?: string\n /** Make editor read-only */\n readOnly?: boolean\n /** Auto focus on mount */\n autoFocus?: boolean\n /** Show line numbers */\n lineNumbers?: boolean\n /** Enable code folding */\n foldGutter?: boolean\n /** Highlight active line */\n highlightActiveLine?: boolean\n /** Enable bracket matching */\n bracketMatching?: boolean\n /** Enable auto-closing brackets */\n closeBrackets?: boolean\n /** Enable autocompletion */\n autocompletion?: boolean\n /** Allow tab key for indentation */\n indentWithTab?: boolean\n /** Minimum height of the editor */\n minHeight?: string | number\n /** Maximum height of the editor (enables scrolling) */\n maxHeight?: string | number\n /** Editor size variant */\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n /** Show border around editor */\n bordered?: boolean\n /** Additional CodeMirror extensions */\n extensions?: Extension[]\n /** Callback with editor view instance */\n onEditorReady?: (view: EditorView) => void\n 'data-testid'?: string\n}\n\nconst sizeClasses = {\n xs: { fontSize: '11px', lineHeight: '1.4' },\n sm: { fontSize: '12px', lineHeight: '1.5' },\n md: { fontSize: '14px', lineHeight: '1.6' },\n lg: { fontSize: '16px', lineHeight: '1.6' },\n xl: { fontSize: '18px', lineHeight: '1.7' },\n}\n\nfunction getLanguageExtension(language: CodeEditorLanguage): Extension | null {\n switch (language) {\n case 'javascript':\n return javascript()\n case 'typescript':\n return javascript({ typescript: true })\n case 'jsx':\n return javascript({ jsx: true })\n case 'tsx':\n return javascript({ jsx: true, typescript: true })\n case 'python':\n return python()\n case 'html':\n return html()\n case 'css':\n return css()\n case 'json':\n return json()\n case 'markdown':\n return markdown()\n case 'sql':\n return sql()\n case 'xml':\n return xml()\n case 'plaintext':\n default:\n return null\n }\n}\n\nconst baseTheme = EditorView.theme({\n '&': {\n backgroundColor: 'oklch(var(--b1))',\n color: 'oklch(var(--bc))',\n },\n '.cm-content': {\n caretColor: 'oklch(var(--bc))',\n fontFamily: 'ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace',\n },\n '.cm-cursor, .cm-dropCursor': {\n borderLeftColor: 'oklch(var(--bc))',\n },\n '&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': {\n backgroundColor: 'oklch(var(--p) / 0.2)',\n },\n '.cm-activeLine': {\n backgroundColor: 'oklch(var(--bc) / 0.05)',\n },\n '.cm-activeLineGutter': {\n backgroundColor: 'oklch(var(--bc) / 0.05)',\n },\n '.cm-gutters': {\n backgroundColor: 'oklch(var(--b2))',\n color: 'oklch(var(--bc) / 0.5)',\n borderRight: '1px solid oklch(var(--b3))',\n },\n '.cm-lineNumbers .cm-gutterElement': {\n padding: '0 8px 0 16px',\n },\n '.cm-foldGutter .cm-gutterElement': {\n padding: '0 4px',\n },\n '.cm-scroller': {\n overflow: 'auto',\n },\n '.cm-placeholder': {\n color: 'oklch(var(--bc) / 0.4)',\n fontStyle: 'italic',\n },\n})\n\nexport const CodeEditor = forwardRef<HTMLDivElement, CodeEditorProps>(\n (\n {\n value = '',\n onChange,\n language = 'plaintext',\n placeholder,\n readOnly = false,\n autoFocus = false,\n lineNumbers: showLineNumbers = true,\n foldGutter: showFoldGutter = true,\n highlightActiveLine: showHighlightActiveLine = true,\n bracketMatching: enableBracketMatching = true,\n closeBrackets: enableCloseBrackets = true,\n autocompletion: enableAutocompletion = true,\n indentWithTab: enableIndentWithTab = true,\n minHeight = 200,\n maxHeight,\n size,\n bordered = true,\n extensions: additionalExtensions = [],\n onEditorReady,\n className = '',\n 'data-testid': testId = 'code-editor',\n ...rest\n },\n ref\n ) => {\n const { componentSize } = useConfig()\n const effectiveSize = size ?? componentSize ?? 'md'\n\n const containerRef = useRef<HTMLDivElement>(null)\n const viewRef = useRef<EditorView | null>(null)\n const onChangeRef = useRef(onChange)\n\n // Keep onChange ref updated\n useEffect(() => {\n onChangeRef.current = onChange\n }, [onChange])\n\n // Create and manage editor\n useEffect(() => {\n if (!containerRef.current) return\n\n const extensions: Extension[] = [\n baseTheme,\n highlightSpecialChars(),\n history(),\n drawSelection(),\n dropCursor(),\n EditorState.allowMultipleSelections.of(true),\n indentOnInput(),\n syntaxHighlighting(defaultHighlightStyle, { fallback: true }),\n rectangularSelection(),\n crosshairCursor(),\n highlightSelectionMatches(),\n keymap.of([\n ...closeBracketsKeymap,\n ...defaultKeymap,\n ...searchKeymap,\n ...historyKeymap,\n ...foldKeymap,\n ...completionKeymap,\n ...lintKeymap,\n ]),\n ]\n\n if (showLineNumbers) {\n extensions.push(lineNumbers())\n }\n\n if (showFoldGutter) {\n extensions.push(foldGutter())\n }\n\n if (showHighlightActiveLine) {\n extensions.push(highlightActiveLine())\n extensions.push(highlightActiveLineGutter())\n }\n\n if (enableBracketMatching) {\n extensions.push(bracketMatching())\n }\n\n if (enableCloseBrackets) {\n extensions.push(closeBrackets())\n }\n\n if (enableAutocompletion) {\n extensions.push(autocompletion())\n }\n\n if (enableIndentWithTab) {\n extensions.push(keymap.of([indentWithTab]))\n }\n\n const langExtension = getLanguageExtension(language)\n if (langExtension) {\n extensions.push(langExtension)\n }\n\n if (placeholder) {\n extensions.push(EditorView.contentAttributes.of({ 'aria-placeholder': placeholder }))\n extensions.push(\n EditorView.theme({\n '.cm-content:has(.cm-line:only-child:empty)::before': {\n content: `\"${placeholder}\"`,\n position: 'absolute',\n color: 'oklch(var(--bc) / 0.4)',\n fontStyle: 'italic',\n pointerEvents: 'none',\n },\n })\n )\n }\n\n if (readOnly) {\n extensions.push(EditorState.readOnly.of(true))\n extensions.push(EditorView.editable.of(false))\n }\n\n // Update listener\n extensions.push(\n EditorView.updateListener.of((update) => {\n if (update.docChanged && onChangeRef.current) {\n onChangeRef.current(update.state.doc.toString())\n }\n })\n )\n\n // Size styling\n const sizeStyle = sizeClasses[effectiveSize]\n extensions.push(\n EditorView.theme({\n '.cm-content': {\n fontSize: sizeStyle.fontSize,\n lineHeight: sizeStyle.lineHeight,\n },\n '.cm-gutters': {\n fontSize: sizeStyle.fontSize,\n },\n })\n )\n\n // Add user extensions\n extensions.push(...additionalExtensions)\n\n const state = EditorState.create({\n doc: value,\n extensions,\n })\n\n const view = new EditorView({\n state,\n parent: containerRef.current,\n })\n\n viewRef.current = view\n\n if (autoFocus) {\n view.focus()\n }\n\n if (onEditorReady) {\n onEditorReady(view)\n }\n\n return () => {\n view.destroy()\n viewRef.current = null\n }\n }, [\n language,\n placeholder,\n readOnly,\n showLineNumbers,\n showFoldGutter,\n showHighlightActiveLine,\n enableBracketMatching,\n enableCloseBrackets,\n enableAutocompletion,\n enableIndentWithTab,\n effectiveSize,\n autoFocus,\n additionalExtensions,\n onEditorReady,\n ])\n\n // Sync value prop changes\n useEffect(() => {\n const view = viewRef.current\n if (!view) return\n\n const currentValue = view.state.doc.toString()\n if (value !== currentValue) {\n view.dispatch({\n changes: {\n from: 0,\n to: currentValue.length,\n insert: value,\n },\n })\n }\n }, [value])\n\n const minHeightStyle = typeof minHeight === 'number' ? `${minHeight}px` : minHeight\n const maxHeightStyle = maxHeight\n ? typeof maxHeight === 'number'\n ? `${maxHeight}px`\n : maxHeight\n : undefined\n\n return (\n <div\n ref={ref}\n className={`\n bg-base-100 rounded-lg overflow-hidden\n ${bordered ? 'border border-base-300' : ''}\n ${className}\n `}\n data-testid={testId}\n {...rest}\n >\n <div\n ref={containerRef}\n className=\"h-full [&_.cm-editor]:outline-none [&_.cm-editor]:h-full\"\n style={{\n minHeight: minHeightStyle,\n maxHeight: maxHeightStyle,\n overflow: maxHeightStyle ? 'auto' : undefined,\n }}\n />\n </div>\n )\n }\n)\n\nCodeEditor.displayName = 'CodeEditor'\n\nexport default CodeEditor\n"],"names":["sizeClasses","getLanguageExtension","language","javascript","python","html","css","json","markdown","sql","xml","baseTheme","EditorView","CodeEditor","forwardRef","value","onChange","placeholder","readOnly","autoFocus","showLineNumbers","showFoldGutter","showHighlightActiveLine","enableBracketMatching","enableCloseBrackets","enableAutocompletion","enableIndentWithTab","minHeight","maxHeight","size","bordered","additionalExtensions","onEditorReady","className","testId","rest","ref","componentSize","useConfig","effectiveSize","containerRef","useRef","viewRef","onChangeRef","useEffect","extensions","highlightSpecialChars","history","drawSelection","dropCursor","EditorState","indentOnInput","syntaxHighlighting","defaultHighlightStyle","rectangularSelection","crosshairCursor","highlightSelectionMatches","keymap","closeBracketsKeymap","defaultKeymap","searchKeymap","historyKeymap","foldKeymap","completionKeymap","lintKeymap","lineNumbers","foldGutter","highlightActiveLine","highlightActiveLineGutter","bracketMatching","closeBrackets","autocompletion","indentWithTab","langExtension","update","sizeStyle","state","view","currentValue","minHeightStyle","maxHeightStyle","jsx"],"mappings":";;;;;;;;;;;;;;;;;;AA2EA,MAAMA,KAAc;AAAA,EAClB,IAAI,EAAE,UAAU,QAAQ,YAAY,MAAA;AAAA,EACpC,IAAI,EAAE,UAAU,QAAQ,YAAY,MAAA;AAAA,EACpC,IAAI,EAAE,UAAU,QAAQ,YAAY,MAAA;AAAA,EACpC,IAAI,EAAE,UAAU,QAAQ,YAAY,MAAA;AAAA,EACpC,IAAI,EAAE,UAAU,QAAQ,YAAY,MAAA;AACtC;AAEA,SAASC,GAAqBC,GAAgD;AAC5E,UAAQA,GAAA;AAAA,IACN,KAAK;AACH,aAAOC,EAAA;AAAA,IACT,KAAK;AACH,aAAOA,EAAW,EAAE,YAAY,IAAM;AAAA,IACxC,KAAK;AACH,aAAOA,EAAW,EAAE,KAAK,IAAM;AAAA,IACjC,KAAK;AACH,aAAOA,EAAW,EAAE,KAAK,IAAM,YAAY,IAAM;AAAA,IACnD,KAAK;AACH,aAAOC,GAAA;AAAA,IACT,KAAK;AACH,aAAOC,GAAA;AAAA,IACT,KAAK;AACH,aAAOC,GAAA;AAAA,IACT,KAAK;AACH,aAAOC,GAAA;AAAA,IACT,KAAK;AACH,aAAOC,GAAA;AAAA,IACT,KAAK;AACH,aAAOC,GAAA;AAAA,IACT,KAAK;AACH,aAAOC,GAAA;AAAA,IAET;AACE,aAAO;AAAA,EAAA;AAEb;AAEA,MAAMC,KAAYC,EAAW,MAAM;AAAA,EACjC,KAAK;AAAA,IACH,iBAAiB;AAAA,IACjB,OAAO;AAAA,EAAA;AAAA,EAET,eAAe;AAAA,IACb,YAAY;AAAA,IACZ,YAAY;AAAA,EAAA;AAAA,EAEd,8BAA8B;AAAA,IAC5B,iBAAiB;AAAA,EAAA;AAAA,EAEnB,0FAA0F;AAAA,IACxF,iBAAiB;AAAA,EAAA;AAAA,EAEnB,kBAAkB;AAAA,IAChB,iBAAiB;AAAA,EAAA;AAAA,EAEnB,wBAAwB;AAAA,IACtB,iBAAiB;AAAA,EAAA;AAAA,EAEnB,eAAe;AAAA,IACb,iBAAiB;AAAA,IACjB,OAAO;AAAA,IACP,aAAa;AAAA,EAAA;AAAA,EAEf,qCAAqC;AAAA,IACnC,SAAS;AAAA,EAAA;AAAA,EAEX,oCAAoC;AAAA,IAClC,SAAS;AAAA,EAAA;AAAA,EAEX,gBAAgB;AAAA,IACd,UAAU;AAAA,EAAA;AAAA,EAEZ,mBAAmB;AAAA,IACjB,OAAO;AAAA,IACP,WAAW;AAAA,EAAA;AAEf,CAAC,GAEYC,KAAaC;AAAA,EACxB,CACE;AAAA,IACE,OAAAC,IAAQ;AAAA,IACR,UAAAC;AAAA,IACA,UAAAd,IAAW;AAAA,IACX,aAAAe;AAAA,IACA,UAAAC,IAAW;AAAA,IACX,WAAAC,IAAY;AAAA,IACZ,aAAaC,IAAkB;AAAA,IAC/B,YAAYC,IAAiB;AAAA,IAC7B,qBAAqBC,IAA0B;AAAA,IAC/C,iBAAiBC,IAAwB;AAAA,IACzC,eAAeC,IAAsB;AAAA,IACrC,gBAAgBC,IAAuB;AAAA,IACvC,eAAeC,IAAsB;AAAA,IACrC,WAAAC,IAAY;AAAA,IACZ,WAAAC;AAAA,IACA,MAAAC;AAAA,IACA,UAAAC,IAAW;AAAA,IACX,YAAYC,IAAuB,CAAA;AAAA,IACnC,eAAAC;AAAA,IACA,WAAAC,IAAY;AAAA,IACZ,eAAeC,IAAS;AAAA,IACxB,GAAGC;AAAA,EAAA,GAELC,MACG;AACH,UAAM,EAAE,eAAAC,EAAA,IAAkBC,GAAA,GACpBC,IAAgBV,KAAQQ,KAAiB,MAEzCG,IAAeC,EAAuB,IAAI,GAC1CC,IAAUD,EAA0B,IAAI,GACxCE,IAAcF,EAAOzB,CAAQ;AAGnC,IAAA4B,EAAU,MAAM;AACd,MAAAD,EAAY,UAAU3B;AAAA,IACxB,GAAG,CAACA,CAAQ,CAAC,GAGb4B,EAAU,MAAM;AACd,UAAI,CAACJ,EAAa,QAAS;AAE3B,YAAMK,IAA0B;AAAA,QAC9BlC;AAAA,QACAmC,EAAA;AAAA,QACAC,GAAA;AAAA,QACAC,EAAA;AAAA,QACAC,EAAA;AAAA,QACAC,EAAY,wBAAwB,GAAG,EAAI;AAAA,QAC3CC,EAAA;AAAA,QACAC,EAAmBC,IAAuB,EAAE,UAAU,IAAM;AAAA,QAC5DC,EAAA;AAAA,QACAC,EAAA;AAAA,QACAC,GAAA;AAAA,QACAC,EAAO,GAAG;AAAA,UACR,GAAGC;AAAA,UACH,GAAGC;AAAA,UACH,GAAGC;AAAA,UACH,GAAGC;AAAA,UACH,GAAGC;AAAA,UACH,GAAGC;AAAA,UACH,GAAGC;AAAA,QAAA,CACJ;AAAA,MAAA;AAGH,MAAI5C,KACFyB,EAAW,KAAKoB,GAAa,GAG3B5C,KACFwB,EAAW,KAAKqB,IAAY,GAG1B5C,MACFuB,EAAW,KAAKsB,GAAqB,GACrCtB,EAAW,KAAKuB,GAA2B,IAGzC7C,KACFsB,EAAW,KAAKwB,IAAiB,GAG/B7C,KACFqB,EAAW,KAAKyB,IAAe,GAG7B7C,KACFoB,EAAW,KAAK0B,IAAgB,GAG9B7C,KACFmB,EAAW,KAAKY,EAAO,GAAG,CAACe,EAAa,CAAC,CAAC;AAG5C,YAAMC,IAAgBxE,GAAqBC,CAAQ;AACnD,MAAIuE,KACF5B,EAAW,KAAK4B,CAAa,GAG3BxD,MACF4B,EAAW,KAAKjC,EAAW,kBAAkB,GAAG,EAAE,oBAAoBK,EAAA,CAAa,CAAC,GACpF4B,EAAW;AAAA,QACTjC,EAAW,MAAM;AAAA,UACf,sDAAsD;AAAA,YACpD,SAAS,IAAIK,CAAW;AAAA,YACxB,UAAU;AAAA,YACV,OAAO;AAAA,YACP,WAAW;AAAA,YACX,eAAe;AAAA,UAAA;AAAA,QACjB,CACD;AAAA,MAAA,IAIDC,MACF2B,EAAW,KAAKK,EAAY,SAAS,GAAG,EAAI,CAAC,GAC7CL,EAAW,KAAKjC,EAAW,SAAS,GAAG,EAAK,CAAC,IAI/CiC,EAAW;AAAA,QACTjC,EAAW,eAAe,GAAG,CAAC8D,MAAW;AACvC,UAAIA,EAAO,cAAc/B,EAAY,WACnCA,EAAY,QAAQ+B,EAAO,MAAM,IAAI,UAAU;AAAA,QAEnD,CAAC;AAAA,MAAA;AAIH,YAAMC,IAAY3E,GAAYuC,CAAa;AAC3C,MAAAM,EAAW;AAAA,QACTjC,EAAW,MAAM;AAAA,UACf,eAAe;AAAA,YACb,UAAU+D,EAAU;AAAA,YACpB,YAAYA,EAAU;AAAA,UAAA;AAAA,UAExB,eAAe;AAAA,YACb,UAAUA,EAAU;AAAA,UAAA;AAAA,QACtB,CACD;AAAA,MAAA,GAIH9B,EAAW,KAAK,GAAGd,CAAoB;AAEvC,YAAM6C,IAAQ1B,EAAY,OAAO;AAAA,QAC/B,KAAKnC;AAAA,QACL,YAAA8B;AAAA,MAAA,CACD,GAEKgC,IAAO,IAAIjE,EAAW;AAAA,QAC1B,OAAAgE;AAAA,QACA,QAAQpC,EAAa;AAAA,MAAA,CACtB;AAED,aAAAE,EAAQ,UAAUmC,GAEd1D,KACF0D,EAAK,MAAA,GAGH7C,KACFA,EAAc6C,CAAI,GAGb,MAAM;AACX,QAAAA,EAAK,QAAA,GACLnC,EAAQ,UAAU;AAAA,MACpB;AAAA,IACF,GAAG;AAAA,MACDxC;AAAA,MACAe;AAAA,MACAC;AAAA,MACAE;AAAA,MACAC;AAAA,MACAC;AAAA,MACAC;AAAA,MACAC;AAAA,MACAC;AAAA,MACAC;AAAA,MACAa;AAAA,MACApB;AAAA,MACAY;AAAA,MACAC;AAAA,IAAA,CACD,GAGDY,EAAU,MAAM;AACd,YAAMiC,IAAOnC,EAAQ;AACrB,UAAI,CAACmC,EAAM;AAEX,YAAMC,IAAeD,EAAK,MAAM,IAAI,SAAA;AACpC,MAAI9D,MAAU+D,KACZD,EAAK,SAAS;AAAA,QACZ,SAAS;AAAA,UACP,MAAM;AAAA,UACN,IAAIC,EAAa;AAAA,UACjB,QAAQ/D;AAAA,QAAA;AAAA,MACV,CACD;AAAA,IAEL,GAAG,CAACA,CAAK,CAAC;AAEV,UAAMgE,IAAiB,OAAOpD,KAAc,WAAW,GAAGA,CAAS,OAAOA,GACpEqD,IAAiBpD,IACnB,OAAOA,KAAc,WACnB,GAAGA,CAAS,OACZA,IACF;AAEJ,WACE,gBAAAqD;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAA7C;AAAA,QACA,WAAW;AAAA;AAAA,YAEPN,IAAW,2BAA2B,EAAE;AAAA,YACxCG,CAAS;AAAA;AAAA,QAEb,eAAaC;AAAA,QACZ,GAAGC;AAAA,QAEJ,UAAA,gBAAA8C;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,KAAKzC;AAAA,YACL,WAAU;AAAA,YACV,OAAO;AAAA,cACL,WAAWuC;AAAA,cACX,WAAWC;AAAA,cACX,UAAUA,IAAiB,SAAS;AAAA,YAAA;AAAA,UACtC;AAAA,QAAA;AAAA,MACF;AAAA,IAAA;AAAA,EAGN;AACF;AAEAnE,GAAW,cAAc;"}
|
|
1
|
+
{"version":3,"file":"CodeEditor.js","sources":["../../src/components/CodeEditor.tsx"],"sourcesContent":["import React, { forwardRef, useEffect, useRef } from 'react'\nimport { EditorState, type Extension } from '@codemirror/state'\nimport { EditorView, keymap, lineNumbers, highlightActiveLine, highlightActiveLineGutter, drawSelection, dropCursor, rectangularSelection, crosshairCursor, highlightSpecialChars } from '@codemirror/view'\nimport { defaultHighlightStyle, syntaxHighlighting, indentOnInput, bracketMatching, foldGutter, foldKeymap } from '@codemirror/language'\nimport { defaultKeymap, history, historyKeymap, indentWithTab } from '@codemirror/commands'\nimport { searchKeymap, highlightSelectionMatches } from '@codemirror/search'\nimport { autocompletion, completionKeymap, closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete'\nimport { lintKeymap } from '@codemirror/lint'\nimport { javascript } from '@codemirror/lang-javascript'\nimport { python } from '@codemirror/lang-python'\nimport { html } from '@codemirror/lang-html'\nimport { css } from '@codemirror/lang-css'\nimport { json } from '@codemirror/lang-json'\nimport { markdown } from '@codemirror/lang-markdown'\nimport { sql } from '@codemirror/lang-sql'\nimport { xml } from '@codemirror/lang-xml'\nimport { useConfig } from '../providers/ConfigProvider'\n\nexport type CodeEditorLanguage =\n | 'javascript'\n | 'typescript'\n | 'jsx'\n | 'tsx'\n | 'python'\n | 'html'\n | 'css'\n | 'json'\n | 'markdown'\n | 'sql'\n | 'xml'\n | 'plaintext'\n\nexport interface CodeEditorProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {\n /** Code content */\n value?: string\n /** Callback when content changes */\n onChange?: (value: string) => void\n /** Programming language for syntax highlighting */\n language?: CodeEditorLanguage\n /** Placeholder text when empty */\n placeholder?: string\n /** Make editor read-only */\n readOnly?: boolean\n /** Auto focus on mount */\n autoFocus?: boolean\n /** Show line numbers */\n lineNumbers?: boolean\n /** Enable code folding */\n foldGutter?: boolean\n /** Highlight active line */\n highlightActiveLine?: boolean\n /** Enable bracket matching */\n bracketMatching?: boolean\n /** Enable auto-closing brackets */\n closeBrackets?: boolean\n /** Enable autocompletion */\n autocompletion?: boolean\n /** Allow tab key for indentation */\n indentWithTab?: boolean\n /** Minimum height of the editor */\n minHeight?: string | number\n /** Maximum height of the editor (enables scrolling) */\n maxHeight?: string | number\n /** Editor size variant */\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n /** Show border around editor */\n bordered?: boolean\n /** Additional CodeMirror extensions */\n extensions?: Extension[]\n /** Callback with editor view instance */\n onEditorReady?: (view: EditorView) => void\n 'data-testid'?: string\n}\n\nconst sizeClasses = {\n xs: { fontSize: '11px', lineHeight: '1.4' },\n sm: { fontSize: '12px', lineHeight: '1.5' },\n md: { fontSize: '14px', lineHeight: '1.6' },\n lg: { fontSize: '16px', lineHeight: '1.6' },\n xl: { fontSize: '18px', lineHeight: '1.7' },\n}\n\nfunction getLanguageExtension(language: CodeEditorLanguage): Extension | null {\n switch (language) {\n case 'javascript':\n return javascript()\n case 'typescript':\n return javascript({ typescript: true })\n case 'jsx':\n return javascript({ jsx: true })\n case 'tsx':\n return javascript({ jsx: true, typescript: true })\n case 'python':\n return python()\n case 'html':\n return html()\n case 'css':\n return css()\n case 'json':\n return json()\n case 'markdown':\n return markdown()\n case 'sql':\n return sql()\n case 'xml':\n return xml()\n case 'plaintext':\n default:\n return null\n }\n}\n\nconst baseTheme = EditorView.theme({\n '&': {\n backgroundColor: 'oklch(var(--b1))',\n color: 'oklch(var(--bc))',\n },\n '.cm-content': {\n caretColor: 'oklch(var(--bc))',\n fontFamily: 'ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace',\n },\n '.cm-cursor, .cm-dropCursor': {\n borderLeftColor: 'oklch(var(--bc))',\n },\n '&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': {\n backgroundColor: 'oklch(var(--p) / 0.2)',\n },\n '.cm-activeLine': {\n backgroundColor: 'oklch(var(--bc) / 0.05)',\n },\n '.cm-activeLineGutter': {\n backgroundColor: 'oklch(var(--bc) / 0.05)',\n },\n '.cm-gutters': {\n backgroundColor: 'oklch(var(--b2))',\n color: 'oklch(var(--bc) / 0.5)',\n borderRight: '1px solid oklch(var(--b3))',\n },\n '.cm-lineNumbers .cm-gutterElement': {\n padding: '0 8px 0 16px',\n },\n '.cm-foldGutter .cm-gutterElement': {\n padding: '0 4px',\n },\n '.cm-scroller': {\n overflow: 'auto',\n },\n '.cm-placeholder': {\n color: 'oklch(var(--bc) / 0.4)',\n fontStyle: 'italic',\n },\n})\n\nexport const CodeEditor = forwardRef<HTMLDivElement, CodeEditorProps>(\n (\n {\n value = '',\n onChange,\n language = 'plaintext',\n placeholder,\n readOnly = false,\n autoFocus = false,\n lineNumbers: showLineNumbers = true,\n foldGutter: showFoldGutter = true,\n highlightActiveLine: showHighlightActiveLine = true,\n bracketMatching: enableBracketMatching = true,\n closeBrackets: enableCloseBrackets = true,\n autocompletion: enableAutocompletion = true,\n indentWithTab: enableIndentWithTab = true,\n minHeight = 200,\n maxHeight,\n size,\n bordered = true,\n extensions: additionalExtensions = [],\n onEditorReady,\n className = '',\n 'data-testid': testId = 'code-editor',\n ...rest\n },\n ref\n ) => {\n const { componentSize } = useConfig()\n const effectiveSize = size ?? componentSize ?? 'md'\n\n const containerRef = useRef<HTMLDivElement>(null)\n const viewRef = useRef<EditorView | null>(null)\n const onChangeRef = useRef(onChange)\n\n // Keep onChange ref updated\n useEffect(() => {\n onChangeRef.current = onChange\n }, [onChange])\n\n // Create and manage editor\n useEffect(() => {\n if (!containerRef.current) return\n\n const extensions: Extension[] = [\n baseTheme,\n highlightSpecialChars(),\n history(),\n drawSelection(),\n dropCursor(),\n EditorState.allowMultipleSelections.of(true),\n indentOnInput(),\n syntaxHighlighting(defaultHighlightStyle, { fallback: true }),\n rectangularSelection(),\n crosshairCursor(),\n highlightSelectionMatches(),\n keymap.of([\n ...closeBracketsKeymap,\n ...defaultKeymap,\n ...searchKeymap,\n ...historyKeymap,\n ...foldKeymap,\n ...completionKeymap,\n ...lintKeymap,\n ]),\n ]\n\n if (showLineNumbers) {\n extensions.push(lineNumbers())\n }\n\n if (showFoldGutter) {\n extensions.push(foldGutter())\n }\n\n if (showHighlightActiveLine) {\n extensions.push(highlightActiveLine())\n extensions.push(highlightActiveLineGutter())\n }\n\n if (enableBracketMatching) {\n extensions.push(bracketMatching())\n }\n\n if (enableCloseBrackets) {\n extensions.push(closeBrackets())\n }\n\n if (enableAutocompletion) {\n extensions.push(autocompletion())\n }\n\n if (enableIndentWithTab) {\n extensions.push(keymap.of([indentWithTab]))\n }\n\n const langExtension = getLanguageExtension(language)\n if (langExtension) {\n extensions.push(langExtension)\n }\n\n if (placeholder) {\n extensions.push(EditorView.contentAttributes.of({ 'aria-placeholder': placeholder }))\n extensions.push(\n EditorView.theme({\n '.cm-content:has(.cm-line:only-child:empty)::before': {\n content: `\"${placeholder}\"`,\n position: 'absolute',\n color: 'oklch(var(--bc) / 0.4)',\n fontStyle: 'italic',\n pointerEvents: 'none',\n },\n })\n )\n }\n\n if (readOnly) {\n extensions.push(EditorState.readOnly.of(true))\n extensions.push(EditorView.editable.of(false))\n }\n\n // Update listener\n extensions.push(\n EditorView.updateListener.of((update) => {\n if (update.docChanged && onChangeRef.current) {\n onChangeRef.current(update.state.doc.toString())\n }\n })\n )\n\n // Size styling\n const sizeStyle = sizeClasses[effectiveSize]\n extensions.push(\n EditorView.theme({\n '.cm-content': {\n fontSize: sizeStyle.fontSize,\n lineHeight: sizeStyle.lineHeight,\n },\n '.cm-gutters': {\n fontSize: sizeStyle.fontSize,\n },\n })\n )\n\n // Add user extensions\n extensions.push(...additionalExtensions)\n\n const state = EditorState.create({\n doc: value,\n extensions,\n })\n\n const view = new EditorView({\n state,\n parent: containerRef.current,\n })\n\n viewRef.current = view\n\n if (autoFocus) {\n view.focus()\n }\n\n if (onEditorReady) {\n onEditorReady(view)\n }\n\n return () => {\n view.destroy()\n viewRef.current = null\n }\n }, [\n language,\n placeholder,\n readOnly,\n showLineNumbers,\n showFoldGutter,\n showHighlightActiveLine,\n enableBracketMatching,\n enableCloseBrackets,\n enableAutocompletion,\n enableIndentWithTab,\n effectiveSize,\n autoFocus,\n additionalExtensions,\n onEditorReady,\n ])\n\n // Sync value prop changes\n useEffect(() => {\n const view = viewRef.current\n if (!view) return\n\n const currentValue = view.state.doc.toString()\n if (value !== currentValue) {\n view.dispatch({\n changes: {\n from: 0,\n to: currentValue.length,\n insert: value,\n },\n })\n }\n }, [value])\n\n const minHeightStyle = typeof minHeight === 'number' ? `${minHeight}px` : minHeight\n const maxHeightStyle = maxHeight\n ? typeof maxHeight === 'number'\n ? `${maxHeight}px`\n : maxHeight\n : undefined\n\n return (\n <div\n ref={ref}\n className={`\n bg-base-100 rounded-lg overflow-hidden\n ${bordered ? 'border border-base-300' : ''}\n ${className}\n `}\n data-testid={testId}\n {...rest}\n >\n <div\n ref={containerRef}\n className=\"h-full [&_.cm-editor]:outline-none [&_.cm-editor]:h-full [&_.cm-editor]:!overflow-hidden [&_.cm-scroller]:!overflow-auto\"\n style={{\n minHeight: minHeightStyle,\n maxHeight: maxHeightStyle,\n overflow: maxHeightStyle ? 'auto' : undefined,\n }}\n />\n </div>\n )\n }\n)\n\nCodeEditor.displayName = 'CodeEditor'\n\nexport default CodeEditor\n"],"names":["sizeClasses","getLanguageExtension","language","javascript","python","html","css","json","markdown","sql","xml","baseTheme","EditorView","CodeEditor","forwardRef","value","onChange","placeholder","readOnly","autoFocus","showLineNumbers","showFoldGutter","showHighlightActiveLine","enableBracketMatching","enableCloseBrackets","enableAutocompletion","enableIndentWithTab","minHeight","maxHeight","size","bordered","additionalExtensions","onEditorReady","className","testId","rest","ref","componentSize","useConfig","effectiveSize","containerRef","useRef","viewRef","onChangeRef","useEffect","extensions","highlightSpecialChars","history","drawSelection","dropCursor","EditorState","indentOnInput","syntaxHighlighting","defaultHighlightStyle","rectangularSelection","crosshairCursor","highlightSelectionMatches","keymap","closeBracketsKeymap","defaultKeymap","searchKeymap","historyKeymap","foldKeymap","completionKeymap","lintKeymap","lineNumbers","foldGutter","highlightActiveLine","highlightActiveLineGutter","bracketMatching","closeBrackets","autocompletion","indentWithTab","langExtension","update","sizeStyle","state","view","currentValue","minHeightStyle","maxHeightStyle","jsx"],"mappings":"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"}
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
2
|
export interface ContextMenuItem {
|
|
3
3
|
key: string;
|
|
4
|
-
label
|
|
4
|
+
label?: React.ReactNode;
|
|
5
5
|
icon?: React.ReactNode;
|
|
6
6
|
disabled?: boolean;
|
|
7
7
|
danger?: boolean;
|
|
8
8
|
divider?: boolean;
|
|
9
9
|
children?: ContextMenuItem[];
|
|
10
|
+
'data-testid'?: string;
|
|
10
11
|
}
|
|
11
12
|
export interface ContextMenuProps {
|
|
12
|
-
/** Element that triggers the context menu on right-click */
|
|
13
|
+
/** Element that triggers the context menu on right-click (first child) */
|
|
13
14
|
children: React.ReactNode;
|
|
14
|
-
/** Menu items (data-driven pattern) */
|
|
15
|
-
items?: ContextMenuItem[];
|
|
15
|
+
/** Menu items (data-driven pattern). Can be a static array or a function that receives the mouse event for dynamic items. */
|
|
16
|
+
items?: ContextMenuItem[] | ((e: React.MouseEvent) => ContextMenuItem[]);
|
|
16
17
|
/** Callback when an item is selected */
|
|
17
18
|
onSelect?: (key: string) => void;
|
|
18
19
|
/** Whether the context menu is disabled */
|
|
@@ -1,66 +1,66 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import
|
|
3
|
-
import { createPortal as
|
|
4
|
-
import { useConfig as
|
|
5
|
-
const
|
|
6
|
-
const e =
|
|
1
|
+
import { jsxs as w, Fragment as F, jsx as n } from "react/jsx-runtime";
|
|
2
|
+
import C, { useState as y, useRef as N, useCallback as E, useEffect as R, createContext as X, useContext as Y } from "react";
|
|
3
|
+
import { createPortal as q } from "react-dom";
|
|
4
|
+
import { useConfig as G } from "../providers/ConfigProvider.js";
|
|
5
|
+
const L = "d-menu", W = X(null), j = () => {
|
|
6
|
+
const e = Y(W);
|
|
7
7
|
if (!e)
|
|
8
8
|
throw new Error("ContextMenu compound components must be used within a ContextMenu");
|
|
9
9
|
return e;
|
|
10
|
-
},
|
|
10
|
+
}, P = ({
|
|
11
11
|
children: e,
|
|
12
|
-
icon:
|
|
12
|
+
icon: s,
|
|
13
13
|
disabled: a = !1,
|
|
14
14
|
danger: c = !1,
|
|
15
|
-
className:
|
|
16
|
-
_key:
|
|
15
|
+
className: p = "",
|
|
16
|
+
_key: l,
|
|
17
17
|
"data-testid": m
|
|
18
18
|
}) => {
|
|
19
|
-
const { onSelect: i, onClose: h, getTestId: o } =
|
|
20
|
-
return /* @__PURE__ */
|
|
19
|
+
const { onSelect: i, onClose: h, getTestId: o } = j(), u = m ?? (l ? o?.(`item-${l}`) : void 0);
|
|
20
|
+
return /* @__PURE__ */ n("li", { className: p, role: "none", children: /* @__PURE__ */ w(
|
|
21
21
|
"button",
|
|
22
22
|
{
|
|
23
23
|
onClick: () => {
|
|
24
|
-
a || !
|
|
24
|
+
a || !l || (i(l), h());
|
|
25
25
|
},
|
|
26
26
|
disabled: a,
|
|
27
27
|
role: "menuitem",
|
|
28
28
|
"aria-disabled": a,
|
|
29
|
-
"data-testid":
|
|
29
|
+
"data-testid": u,
|
|
30
30
|
className: `
|
|
31
31
|
flex items-center gap-2 w-full px-4 py-2 text-left text-sm
|
|
32
32
|
${a ? "opacity-50 cursor-not-allowed" : "hover:bg-base-200"}
|
|
33
33
|
${c ? "text-error hover:bg-error/10" : ""}
|
|
34
34
|
`,
|
|
35
35
|
children: [
|
|
36
|
-
|
|
37
|
-
/* @__PURE__ */
|
|
36
|
+
s && /* @__PURE__ */ n("span", { className: "w-4 h-4", "aria-hidden": "true", children: s }),
|
|
37
|
+
/* @__PURE__ */ n("span", { className: "flex-1", children: e })
|
|
38
38
|
]
|
|
39
39
|
}
|
|
40
40
|
) });
|
|
41
|
-
},
|
|
41
|
+
}, J = ({ className: e = "", "data-testid": s }) => /* @__PURE__ */ n("hr", { className: `border-base-300 my-1 ${e}`, role: "separator", "data-testid": s }), D = ({
|
|
42
42
|
label: e,
|
|
43
|
-
icon:
|
|
43
|
+
icon: s,
|
|
44
44
|
disabled: a = !1,
|
|
45
45
|
children: c,
|
|
46
|
-
className:
|
|
47
|
-
_key:
|
|
46
|
+
className: p = "",
|
|
47
|
+
_key: l,
|
|
48
48
|
"data-testid": m
|
|
49
49
|
}) => {
|
|
50
|
-
const { getTestId: i } =
|
|
51
|
-
a || (
|
|
52
|
-
},
|
|
53
|
-
|
|
50
|
+
const { getTestId: i } = j(), [h, o] = y(!1), u = N(null), d = m ?? (l ? i?.(`submenu-${l}`) : void 0), f = () => {
|
|
51
|
+
a || (u.current && clearTimeout(u.current), o(!0));
|
|
52
|
+
}, x = () => {
|
|
53
|
+
u.current = setTimeout(() => o(!1), 100);
|
|
54
54
|
};
|
|
55
|
-
return /* @__PURE__ */
|
|
55
|
+
return /* @__PURE__ */ w(
|
|
56
56
|
"li",
|
|
57
57
|
{
|
|
58
|
-
onMouseEnter:
|
|
59
|
-
onMouseLeave:
|
|
60
|
-
className: `relative ${
|
|
58
|
+
onMouseEnter: f,
|
|
59
|
+
onMouseLeave: x,
|
|
60
|
+
className: `relative ${p}`,
|
|
61
61
|
role: "none",
|
|
62
62
|
children: [
|
|
63
|
-
/* @__PURE__ */
|
|
63
|
+
/* @__PURE__ */ w(
|
|
64
64
|
"button",
|
|
65
65
|
{
|
|
66
66
|
disabled: a,
|
|
@@ -68,59 +68,59 @@ const E = "d-menu", D = "d-divider", j = K(null), P = () => {
|
|
|
68
68
|
"aria-haspopup": "menu",
|
|
69
69
|
"aria-expanded": h,
|
|
70
70
|
"aria-disabled": a,
|
|
71
|
-
"data-testid":
|
|
71
|
+
"data-testid": d,
|
|
72
72
|
className: `
|
|
73
73
|
flex items-center gap-2 w-full px-4 py-2 text-left text-sm
|
|
74
74
|
${a ? "opacity-50 cursor-not-allowed" : "hover:bg-base-200"}
|
|
75
75
|
`,
|
|
76
76
|
children: [
|
|
77
|
-
|
|
78
|
-
/* @__PURE__ */
|
|
79
|
-
/* @__PURE__ */
|
|
77
|
+
s && /* @__PURE__ */ n("span", { className: "w-4 h-4", "aria-hidden": "true", children: s }),
|
|
78
|
+
/* @__PURE__ */ n("span", { className: "flex-1", children: e }),
|
|
79
|
+
/* @__PURE__ */ n("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ n("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) })
|
|
80
80
|
]
|
|
81
81
|
}
|
|
82
82
|
),
|
|
83
|
-
h && /* @__PURE__ */
|
|
83
|
+
h && /* @__PURE__ */ n(
|
|
84
84
|
"ul",
|
|
85
85
|
{
|
|
86
|
-
className: `${
|
|
87
|
-
onMouseEnter:
|
|
88
|
-
onMouseLeave:
|
|
86
|
+
className: `${L} bg-base-100 rounded-box shadow-lg border border-base-300 absolute left-full top-0 min-w-[160px] z-50 p-1`,
|
|
87
|
+
onMouseEnter: f,
|
|
88
|
+
onMouseLeave: x,
|
|
89
89
|
role: "menu",
|
|
90
|
-
"data-testid":
|
|
90
|
+
"data-testid": d ? `${d}-menu` : void 0,
|
|
91
91
|
children: c
|
|
92
92
|
}
|
|
93
93
|
)
|
|
94
94
|
]
|
|
95
95
|
}
|
|
96
96
|
);
|
|
97
|
-
},
|
|
98
|
-
const [
|
|
97
|
+
}, V = ({ item: e, onSelect: s, onClose: a, getTestId: c }) => {
|
|
98
|
+
const [p, l] = y(!1), m = N(null), i = e["data-testid"] ?? c?.(`item-${e.key}`);
|
|
99
99
|
if (e.divider)
|
|
100
|
-
return /* @__PURE__ */
|
|
100
|
+
return /* @__PURE__ */ n("hr", { className: "border-base-300 my-1", role: "separator", "data-testid": c?.(`separator-${e.key}`) });
|
|
101
101
|
const h = () => {
|
|
102
|
-
e.disabled || e.children && e.children.length > 0 || (
|
|
103
|
-
}, o = e.children && e.children.length > 0,
|
|
104
|
-
o && (m.current && clearTimeout(m.current),
|
|
105
|
-
},
|
|
106
|
-
o && (m.current = setTimeout(() =>
|
|
102
|
+
e.disabled || e.children && e.children.length > 0 || (s(e.key), a());
|
|
103
|
+
}, o = e.children && e.children.length > 0, u = () => {
|
|
104
|
+
o && (m.current && clearTimeout(m.current), l(!0));
|
|
105
|
+
}, d = () => {
|
|
106
|
+
o && (m.current = setTimeout(() => l(!1), 100));
|
|
107
107
|
};
|
|
108
|
-
return /* @__PURE__ */
|
|
108
|
+
return /* @__PURE__ */ w(
|
|
109
109
|
"li",
|
|
110
110
|
{
|
|
111
|
-
onMouseEnter:
|
|
112
|
-
onMouseLeave:
|
|
111
|
+
onMouseEnter: u,
|
|
112
|
+
onMouseLeave: d,
|
|
113
113
|
className: "relative",
|
|
114
114
|
role: "none",
|
|
115
115
|
children: [
|
|
116
|
-
/* @__PURE__ */
|
|
116
|
+
/* @__PURE__ */ w(
|
|
117
117
|
"button",
|
|
118
118
|
{
|
|
119
119
|
onClick: h,
|
|
120
120
|
disabled: e.disabled,
|
|
121
121
|
role: "menuitem",
|
|
122
122
|
"aria-haspopup": o ? "menu" : void 0,
|
|
123
|
-
"aria-expanded": o ?
|
|
123
|
+
"aria-expanded": o ? p : void 0,
|
|
124
124
|
"aria-disabled": e.disabled,
|
|
125
125
|
"data-testid": i,
|
|
126
126
|
className: `
|
|
@@ -129,114 +129,107 @@ const E = "d-menu", D = "d-divider", j = K(null), P = () => {
|
|
|
129
129
|
${e.danger ? "text-error hover:bg-error/10" : ""}
|
|
130
130
|
`,
|
|
131
131
|
children: [
|
|
132
|
-
e.icon && /* @__PURE__ */
|
|
133
|
-
/* @__PURE__ */
|
|
134
|
-
o && /* @__PURE__ */
|
|
132
|
+
e.icon && /* @__PURE__ */ n("span", { className: "w-4 h-4", "aria-hidden": "true", children: e.icon }),
|
|
133
|
+
/* @__PURE__ */ n("span", { className: "flex-1", children: e.label }),
|
|
134
|
+
o && /* @__PURE__ */ n("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ n("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) })
|
|
135
135
|
]
|
|
136
136
|
}
|
|
137
137
|
),
|
|
138
|
-
o &&
|
|
138
|
+
o && p && /* @__PURE__ */ n(
|
|
139
139
|
"ul",
|
|
140
140
|
{
|
|
141
|
-
className: `${
|
|
142
|
-
onMouseEnter:
|
|
143
|
-
onMouseLeave:
|
|
141
|
+
className: `${L} bg-base-100 rounded-box shadow-lg border border-base-300 absolute left-full top-0 min-w-[160px] z-50 p-1`,
|
|
142
|
+
onMouseEnter: u,
|
|
143
|
+
onMouseLeave: d,
|
|
144
144
|
role: "menu",
|
|
145
145
|
"data-testid": i ? `${i}-menu` : void 0,
|
|
146
|
-
children: e.children.map((
|
|
146
|
+
children: e.children.map((f) => /* @__PURE__ */ n(V, { item: f, onSelect: s, onClose: a, getTestId: c }, f.key))
|
|
147
147
|
}
|
|
148
148
|
)
|
|
149
149
|
]
|
|
150
150
|
}
|
|
151
151
|
);
|
|
152
|
-
},
|
|
152
|
+
}, Q = ({
|
|
153
153
|
children: e,
|
|
154
|
-
items:
|
|
154
|
+
items: s,
|
|
155
155
|
onSelect: a,
|
|
156
156
|
disabled: c = !1,
|
|
157
|
-
className:
|
|
158
|
-
"data-testid":
|
|
157
|
+
className: p = "",
|
|
158
|
+
"data-testid": l
|
|
159
159
|
}) => {
|
|
160
|
-
const { getPopupContainer: m } =
|
|
161
|
-
(
|
|
162
|
-
|
|
163
|
-
n.preventDefault(), n.stopPropagation();
|
|
164
|
-
let r = n.clientX, p = n.clientY;
|
|
165
|
-
d({ x: r, y: p }), h(!0);
|
|
160
|
+
const { getPopupContainer: m } = G(), [i, h] = y(!1), [o, u] = y({ x: 0, y: 0 }), [d, f] = y(void 0), x = N(null), z = E(
|
|
161
|
+
(t) => {
|
|
162
|
+
c || (t.preventDefault(), t.stopPropagation(), u({ x: t.clientX, y: t.clientY }), f(typeof s == "function" ? s(t) : s), h(!0));
|
|
166
163
|
},
|
|
167
|
-
[c]
|
|
168
|
-
),
|
|
164
|
+
[c, s]
|
|
165
|
+
), v = E(() => {
|
|
169
166
|
h(!1);
|
|
170
|
-
}, []),
|
|
171
|
-
(
|
|
172
|
-
a?.(
|
|
167
|
+
}, []), S = E(
|
|
168
|
+
(t) => {
|
|
169
|
+
a?.(t);
|
|
173
170
|
},
|
|
174
171
|
[a]
|
|
175
172
|
);
|
|
176
173
|
R(() => {
|
|
177
|
-
if (i &&
|
|
178
|
-
const r =
|
|
179
|
-
let { x:
|
|
180
|
-
|
|
174
|
+
if (i && x.current) {
|
|
175
|
+
const r = x.current.getBoundingClientRect(), b = window.innerWidth, g = window.innerHeight;
|
|
176
|
+
let { x: k, y: $ } = o;
|
|
177
|
+
k + r.width > b && (k = b - r.width - 8), $ + r.height > g && ($ = g - r.height - 8), (k !== o.x || $ !== o.y) && u({ x: k, y: $ });
|
|
181
178
|
}
|
|
182
179
|
}, [i, o]), R(() => {
|
|
183
180
|
if (!i) return;
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
}, r = (
|
|
187
|
-
|
|
188
|
-
},
|
|
189
|
-
|
|
181
|
+
const t = (g) => {
|
|
182
|
+
x.current && !x.current.contains(g.target) && v();
|
|
183
|
+
}, r = (g) => {
|
|
184
|
+
g.key === "Escape" && v();
|
|
185
|
+
}, b = () => {
|
|
186
|
+
v();
|
|
190
187
|
};
|
|
191
|
-
return document.addEventListener("mousedown",
|
|
192
|
-
document.removeEventListener("mousedown",
|
|
188
|
+
return document.addEventListener("mousedown", t), document.addEventListener("keydown", r), document.addEventListener("scroll", b, !0), () => {
|
|
189
|
+
document.removeEventListener("mousedown", t), document.removeEventListener("keydown", r), document.removeEventListener("scroll", b, !0);
|
|
193
190
|
};
|
|
194
|
-
}, [i,
|
|
195
|
-
const B = (
|
|
196
|
-
if (
|
|
197
|
-
const
|
|
198
|
-
if (r.type ===
|
|
199
|
-
return
|
|
191
|
+
}, [i, v]);
|
|
192
|
+
const B = (t) => C.Children.map(t, (r) => {
|
|
193
|
+
if (C.isValidElement(r)) {
|
|
194
|
+
const b = r.key != null ? String(r.key) : void 0;
|
|
195
|
+
if (r.type === P || r.type === D)
|
|
196
|
+
return C.cloneElement(r, { _key: b });
|
|
200
197
|
}
|
|
201
198
|
return r;
|
|
202
|
-
}),
|
|
203
|
-
onSelect:
|
|
204
|
-
onClose:
|
|
205
|
-
getTestId:
|
|
206
|
-
},
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
}
|
|
217
|
-
),
|
|
218
|
-
i && Y(
|
|
219
|
-
/* @__PURE__ */ t(j.Provider, { value: O, children: /* @__PURE__ */ t(
|
|
199
|
+
}), T = C.Children.toArray(e), M = T[0], H = B(T.slice(1)), A = d && d.length > 0, K = {
|
|
200
|
+
onSelect: S,
|
|
201
|
+
onClose: v,
|
|
202
|
+
getTestId: l ? (t) => `${l}-${t}` : void 0
|
|
203
|
+
}, I = (t) => l ? `${l}-${t}` : void 0, O = C.isValidElement(M) ? C.cloneElement(M, {
|
|
204
|
+
onContextMenu: (t) => {
|
|
205
|
+
const r = M.props.onContextMenu;
|
|
206
|
+
r && r(t), z(t);
|
|
207
|
+
}
|
|
208
|
+
}) : M;
|
|
209
|
+
return /* @__PURE__ */ w(F, { children: [
|
|
210
|
+
O,
|
|
211
|
+
i && q(
|
|
212
|
+
/* @__PURE__ */ n(W.Provider, { value: K, children: /* @__PURE__ */ n(
|
|
220
213
|
"ul",
|
|
221
214
|
{
|
|
222
|
-
ref:
|
|
223
|
-
className: `${
|
|
215
|
+
ref: x,
|
|
216
|
+
className: `${L} bg-base-100 rounded-box shadow-lg border border-base-300 min-w-[160px] p-1 fixed z-[9999] ${p}`,
|
|
224
217
|
style: { left: o.x, top: o.y },
|
|
225
218
|
role: "menu",
|
|
226
219
|
"aria-label": "Context menu",
|
|
227
|
-
"data-testid":
|
|
228
|
-
children:
|
|
220
|
+
"data-testid": I("menu"),
|
|
221
|
+
children: A ? d.map((t) => /* @__PURE__ */ n(V, { item: t, onSelect: S, onClose: v, getTestId: I }, t.key)) : H
|
|
229
222
|
}
|
|
230
223
|
) }),
|
|
231
224
|
m ? m(document.body) : document.body
|
|
232
225
|
)
|
|
233
226
|
] });
|
|
234
|
-
},
|
|
235
|
-
Item:
|
|
236
|
-
Divider:
|
|
237
|
-
SubMenu:
|
|
227
|
+
}, te = Object.assign(Q, {
|
|
228
|
+
Item: P,
|
|
229
|
+
Divider: J,
|
|
230
|
+
SubMenu: D
|
|
238
231
|
});
|
|
239
232
|
export {
|
|
240
|
-
|
|
233
|
+
te as ContextMenu
|
|
241
234
|
};
|
|
242
235
|
//# sourceMappingURL=ContextMenu.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContextMenu.js","sources":["../../src/components/ContextMenu.tsx"],"sourcesContent":["import React, { useState, useRef, useEffect, useCallback, createContext, useContext } from 'react'\nimport { createPortal } from 'react-dom'\nimport { useConfig } from '../providers/ConfigProvider'\n\n// DaisyUI classes\nconst dMenu = 'd-menu'\nconst dDivider = 'd-divider'\n\nexport interface ContextMenuItem {\n key: string\n label: React.ReactNode\n icon?: React.ReactNode\n disabled?: boolean\n danger?: boolean\n divider?: boolean\n children?: ContextMenuItem[]\n}\n\nexport interface ContextMenuProps {\n /** Element that triggers the context menu on right-click */\n children: React.ReactNode\n /** Menu items (data-driven pattern) */\n items?: ContextMenuItem[]\n /** Callback when an item is selected */\n onSelect?: (key: string) => void\n /** Whether the context menu is disabled */\n disabled?: boolean\n /** Additional CSS classes for the menu */\n className?: string\n 'data-testid'?: string\n}\n\nexport interface ContextMenuItemProps {\n /** Item content */\n children: React.ReactNode\n /** Icon to display before label */\n icon?: React.ReactNode\n /** Whether the item is disabled */\n disabled?: boolean\n /** Show as danger/destructive action */\n danger?: boolean\n /** Additional CSS classes */\n className?: string\n /** @internal */\n _key?: string\n 'data-testid'?: string\n}\n\nexport interface ContextMenuDividerProps {\n /** Additional CSS classes */\n className?: string\n 'data-testid'?: string\n}\n\nexport interface ContextMenuSubMenuProps {\n /** Submenu label */\n label: React.ReactNode\n /** Icon to display before label */\n icon?: React.ReactNode\n /** Whether the submenu is disabled */\n disabled?: boolean\n /** Submenu items */\n children: React.ReactNode\n /** Additional CSS classes */\n className?: string\n /** @internal */\n _key?: string\n 'data-testid'?: string\n}\n\ninterface ContextMenuContextValue {\n onSelect: (key: string) => void\n onClose: () => void\n getTestId?: (suffix: string) => string | undefined\n}\n\ninterface MenuPosition {\n x: number\n y: number\n}\n\nconst ContextMenuContext = createContext<ContextMenuContextValue | null>(null)\n\nconst useContextMenuContext = () => {\n const context = useContext(ContextMenuContext)\n if (!context) {\n throw new Error('ContextMenu compound components must be used within a ContextMenu')\n }\n return context\n}\n\n// Compound pattern components\nconst ContextMenuItemComponent: React.FC<ContextMenuItemProps> = ({\n children,\n icon,\n disabled = false,\n danger = false,\n className = '',\n _key,\n 'data-testid': testId,\n}) => {\n const { onSelect, onClose, getTestId } = useContextMenuContext()\n const itemTestId = testId ?? (_key ? getTestId?.(`item-${_key}`) : undefined)\n\n const handleClick = () => {\n if (disabled || !_key) return\n onSelect(_key)\n onClose()\n }\n\n return (\n <li className={className} role=\"none\">\n <button\n onClick={handleClick}\n disabled={disabled}\n role=\"menuitem\"\n aria-disabled={disabled}\n data-testid={itemTestId}\n className={`\n flex items-center gap-2 w-full px-4 py-2 text-left text-sm\n ${disabled ? 'opacity-50 cursor-not-allowed' : 'hover:bg-base-200'}\n ${danger ? 'text-error hover:bg-error/10' : ''}\n `}\n >\n {icon && <span className=\"w-4 h-4\" aria-hidden=\"true\">{icon}</span>}\n <span className=\"flex-1\">{children}</span>\n </button>\n </li>\n )\n}\n\nconst ContextMenuDividerComponent: React.FC<ContextMenuDividerProps> = ({ className = '', 'data-testid': testId }) => {\n return <li className={`${dDivider} my-1 ${className}`} role=\"separator\" data-testid={testId}></li>\n}\n\nconst ContextMenuSubMenuComponent: React.FC<ContextMenuSubMenuProps> = ({\n label,\n icon,\n disabled = false,\n children,\n className = '',\n _key: _unusedKey,\n 'data-testid': testId,\n}) => {\n const { getTestId } = useContextMenuContext()\n const [showSubmenu, setShowSubmenu] = useState(false)\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const submenuTestId = testId ?? (_unusedKey ? getTestId?.(`submenu-${_unusedKey}`) : undefined)\n\n const handleMouseEnter = () => {\n if (disabled) return\n if (timeoutRef.current) clearTimeout(timeoutRef.current)\n setShowSubmenu(true)\n }\n\n const handleMouseLeave = () => {\n timeoutRef.current = setTimeout(() => setShowSubmenu(false), 100)\n }\n\n return (\n <li\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n className={`relative ${className}`}\n role=\"none\"\n >\n <button\n disabled={disabled}\n role=\"menuitem\"\n aria-haspopup=\"menu\"\n aria-expanded={showSubmenu}\n aria-disabled={disabled}\n data-testid={submenuTestId}\n className={`\n flex items-center gap-2 w-full px-4 py-2 text-left text-sm\n ${disabled ? 'opacity-50 cursor-not-allowed' : 'hover:bg-base-200'}\n `}\n >\n {icon && <span className=\"w-4 h-4\" aria-hidden=\"true\">{icon}</span>}\n <span className=\"flex-1\">{label}</span>\n <svg className=\"w-4 h-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" aria-hidden=\"true\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n {showSubmenu && (\n <ul\n className={`${dMenu} bg-base-100 rounded-box shadow-lg border border-base-300 absolute left-full top-0 min-w-[160px] z-50 p-1`}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n role=\"menu\"\n data-testid={submenuTestId ? `${submenuTestId}-menu` : undefined}\n >\n {children}\n </ul>\n )}\n </li>\n )\n}\n\n// Data-driven pattern internal component\nconst MenuItem: React.FC<{\n item: ContextMenuItem\n onSelect: (key: string) => void\n onClose: () => void\n getTestId?: (suffix: string) => string | undefined\n}> = ({ item, onSelect, onClose, getTestId }) => {\n const [showSubmenu, setShowSubmenu] = useState(false)\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const itemTestId = getTestId?.(`item-${item.key}`)\n\n if (item.divider) {\n return <li className={`${dDivider} my-1`} role=\"separator\" data-testid={getTestId?.(`separator-${item.key}`)}></li>\n }\n\n const handleClick = () => {\n if (item.disabled) return\n if (item.children && item.children.length > 0) return\n onSelect(item.key)\n onClose()\n }\n\n const hasSubmenu = item.children && item.children.length > 0\n\n const handleMouseEnter = () => {\n if (!hasSubmenu) return\n if (timeoutRef.current) clearTimeout(timeoutRef.current)\n setShowSubmenu(true)\n }\n\n const handleMouseLeave = () => {\n if (!hasSubmenu) return\n timeoutRef.current = setTimeout(() => setShowSubmenu(false), 100)\n }\n\n return (\n <li\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n className=\"relative\"\n role=\"none\"\n >\n <button\n onClick={handleClick}\n disabled={item.disabled}\n role=\"menuitem\"\n aria-haspopup={hasSubmenu ? 'menu' : undefined}\n aria-expanded={hasSubmenu ? showSubmenu : undefined}\n aria-disabled={item.disabled}\n data-testid={itemTestId}\n className={`\n flex items-center gap-2 w-full px-4 py-2 text-left text-sm\n ${item.disabled ? 'opacity-50 cursor-not-allowed' : 'hover:bg-base-200'}\n ${item.danger ? 'text-error hover:bg-error/10' : ''}\n `}\n >\n {item.icon && <span className=\"w-4 h-4\" aria-hidden=\"true\">{item.icon}</span>}\n <span className=\"flex-1\">{item.label}</span>\n {hasSubmenu && (\n <svg className=\"w-4 h-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" aria-hidden=\"true\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M9 5l7 7-7 7\" />\n </svg>\n )}\n </button>\n {hasSubmenu && showSubmenu && (\n <ul\n className={`${dMenu} bg-base-100 rounded-box shadow-lg border border-base-300 absolute left-full top-0 min-w-[160px] z-50 p-1`}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n role=\"menu\"\n data-testid={itemTestId ? `${itemTestId}-menu` : undefined}\n >\n {item.children!.map((child) => (\n <MenuItem key={child.key} item={child} onSelect={onSelect} onClose={onClose} getTestId={getTestId} />\n ))}\n </ul>\n )}\n </li>\n )\n}\n\nconst ContextMenuRoot: React.FC<ContextMenuProps> = ({\n children,\n items,\n onSelect,\n disabled = false,\n className = '',\n 'data-testid': testId,\n}) => {\n const { getPopupContainer } = useConfig()\n const [visible, setVisible] = useState(false)\n const [position, setPosition] = useState<MenuPosition>({ x: 0, y: 0 })\n const menuRef = useRef<HTMLUListElement>(null)\n const triggerRef = useRef<HTMLDivElement>(null)\n\n const handleContextMenu = useCallback(\n (e: React.MouseEvent) => {\n if (disabled) return\n e.preventDefault()\n e.stopPropagation()\n\n // Calculate position, ensuring menu stays within viewport\n let x = e.clientX\n let y = e.clientY\n\n // We'll adjust after render when we know menu dimensions\n setPosition({ x, y })\n setVisible(true)\n },\n [disabled]\n )\n\n const handleClose = useCallback(() => {\n setVisible(false)\n }, [])\n\n const handleSelect = useCallback(\n (key: string) => {\n onSelect?.(key)\n },\n [onSelect]\n )\n\n // Adjust position after menu renders to keep it in viewport\n useEffect(() => {\n if (visible && menuRef.current) {\n const menu = menuRef.current\n const rect = menu.getBoundingClientRect()\n const viewportWidth = window.innerWidth\n const viewportHeight = window.innerHeight\n\n let { x, y } = position\n\n if (x + rect.width > viewportWidth) {\n x = viewportWidth - rect.width - 8\n }\n if (y + rect.height > viewportHeight) {\n y = viewportHeight - rect.height - 8\n }\n\n if (x !== position.x || y !== position.y) {\n setPosition({ x, y })\n }\n }\n }, [visible, position])\n\n // Close on click outside or escape\n useEffect(() => {\n if (!visible) return\n\n const handleClickOutside = (e: MouseEvent) => {\n if (menuRef.current && !menuRef.current.contains(e.target as Node)) {\n handleClose()\n }\n }\n\n const handleEscape = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n handleClose()\n }\n }\n\n const handleScroll = () => {\n handleClose()\n }\n\n document.addEventListener('mousedown', handleClickOutside)\n document.addEventListener('keydown', handleEscape)\n document.addEventListener('scroll', handleScroll, true)\n\n return () => {\n document.removeEventListener('mousedown', handleClickOutside)\n document.removeEventListener('keydown', handleEscape)\n document.removeEventListener('scroll', handleScroll, true)\n }\n }, [visible, handleClose])\n\n // Clone children to extract keys\n const cloneChildrenWithKeys = (children: React.ReactNode): React.ReactNode => {\n return React.Children.map(children, (child) => {\n if (React.isValidElement(child)) {\n const childKey = child.key != null ? String(child.key) : undefined\n if (child.type === ContextMenuItemComponent || child.type === ContextMenuSubMenuComponent) {\n return React.cloneElement(child as React.ReactElement<any>, { _key: childKey })\n }\n }\n return child\n })\n }\n\n // Determine if using data-driven or compound pattern\n // Find menu content children (not the trigger element)\n const childArray = React.Children.toArray(children)\n const triggerChild = childArray[0]\n const menuChildren = cloneChildrenWithKeys(childArray.slice(1))\n const useDataDriven = items && items.length > 0\n\n const contextValue: ContextMenuContextValue = {\n onSelect: handleSelect,\n onClose: handleClose,\n getTestId: testId ? (suffix: string) => `${testId}-${suffix}` : undefined,\n }\n const getTestId = (suffix: string) => (testId ? `${testId}-${suffix}` : undefined)\n\n return (\n <>\n <div\n ref={triggerRef}\n onContextMenu={handleContextMenu}\n className=\"inline-block\"\n data-testid={testId}\n >\n {triggerChild}\n </div>\n {visible &&\n createPortal(\n <ContextMenuContext.Provider value={contextValue}>\n <ul\n ref={menuRef}\n className={`${dMenu} bg-base-100 rounded-box shadow-lg border border-base-300 min-w-[160px] p-1 fixed z-[9999] ${className}`}\n style={{ left: position.x, top: position.y }}\n role=\"menu\"\n aria-label=\"Context menu\"\n data-testid={getTestId('menu')}\n >\n {useDataDriven\n ? items!.map((item) => (\n <MenuItem key={item.key} item={item} onSelect={handleSelect} onClose={handleClose} getTestId={getTestId} />\n ))\n : menuChildren}\n </ul>\n </ContextMenuContext.Provider>,\n getPopupContainer ? getPopupContainer(document.body) : document.body\n )}\n </>\n )\n}\n\n// Assign compound components\nexport const ContextMenu = Object.assign(ContextMenuRoot, {\n Item: ContextMenuItemComponent,\n Divider: ContextMenuDividerComponent,\n SubMenu: ContextMenuSubMenuComponent,\n})\n"],"names":["dMenu","dDivider","ContextMenuContext","createContext","useContextMenuContext","context","useContext","ContextMenuItemComponent","children","icon","disabled","danger","className","_key","testId","onSelect","onClose","getTestId","itemTestId","jsx","jsxs","ContextMenuDividerComponent","ContextMenuSubMenuComponent","label","_unusedKey","showSubmenu","setShowSubmenu","useState","timeoutRef","useRef","submenuTestId","handleMouseEnter","handleMouseLeave","MenuItem","item","handleClick","hasSubmenu","child","ContextMenuRoot","items","getPopupContainer","useConfig","visible","setVisible","position","setPosition","menuRef","triggerRef","handleContextMenu","useCallback","e","x","y","handleClose","handleSelect","key","useEffect","rect","viewportWidth","viewportHeight","handleClickOutside","handleEscape","handleScroll","cloneChildrenWithKeys","React","childKey","childArray","triggerChild","menuChildren","useDataDriven","contextValue","suffix","Fragment","createPortal","ContextMenu"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ContextMenu.js","sources":["../../src/components/ContextMenu.tsx"],"sourcesContent":["import React, { useState, useRef, useEffect, useCallback, createContext, useContext } from 'react'\nimport { createPortal } from 'react-dom'\nimport { useConfig } from '../providers/ConfigProvider'\n\n// DaisyUI classes\nconst dMenu = 'd-menu'\n\nexport interface ContextMenuItem {\n key: string\n label?: React.ReactNode\n icon?: React.ReactNode\n disabled?: boolean\n danger?: boolean\n divider?: boolean\n children?: ContextMenuItem[]\n 'data-testid'?: string\n}\n\nexport interface ContextMenuProps {\n /** Element that triggers the context menu on right-click (first child) */\n children: React.ReactNode\n /** Menu items (data-driven pattern). Can be a static array or a function that receives the mouse event for dynamic items. */\n items?: ContextMenuItem[] | ((e: React.MouseEvent) => ContextMenuItem[])\n /** Callback when an item is selected */\n onSelect?: (key: string) => void\n /** Whether the context menu is disabled */\n disabled?: boolean\n /** Additional CSS classes for the menu */\n className?: string\n 'data-testid'?: string\n}\n\nexport interface ContextMenuItemProps {\n /** Item content */\n children: React.ReactNode\n /** Icon to display before label */\n icon?: React.ReactNode\n /** Whether the item is disabled */\n disabled?: boolean\n /** Show as danger/destructive action */\n danger?: boolean\n /** Additional CSS classes */\n className?: string\n /** @internal */\n _key?: string\n 'data-testid'?: string\n}\n\nexport interface ContextMenuDividerProps {\n /** Additional CSS classes */\n className?: string\n 'data-testid'?: string\n}\n\nexport interface ContextMenuSubMenuProps {\n /** Submenu label */\n label: React.ReactNode\n /** Icon to display before label */\n icon?: React.ReactNode\n /** Whether the submenu is disabled */\n disabled?: boolean\n /** Submenu items */\n children: React.ReactNode\n /** Additional CSS classes */\n className?: string\n /** @internal */\n _key?: string\n 'data-testid'?: string\n}\n\ninterface ContextMenuContextValue {\n onSelect: (key: string) => void\n onClose: () => void\n getTestId?: (suffix: string) => string | undefined\n}\n\ninterface MenuPosition {\n x: number\n y: number\n}\n\nconst ContextMenuContext = createContext<ContextMenuContextValue | null>(null)\n\nconst useContextMenuContext = () => {\n const context = useContext(ContextMenuContext)\n if (!context) {\n throw new Error('ContextMenu compound components must be used within a ContextMenu')\n }\n return context\n}\n\n// Compound pattern components\nconst ContextMenuItemComponent: React.FC<ContextMenuItemProps> = ({\n children,\n icon,\n disabled = false,\n danger = false,\n className = '',\n _key,\n 'data-testid': testId,\n}) => {\n const { onSelect, onClose, getTestId } = useContextMenuContext()\n const itemTestId = testId ?? (_key ? getTestId?.(`item-${_key}`) : undefined)\n\n const handleClick = () => {\n if (disabled || !_key) return\n onSelect(_key)\n onClose()\n }\n\n return (\n <li className={className} role=\"none\">\n <button\n onClick={handleClick}\n disabled={disabled}\n role=\"menuitem\"\n aria-disabled={disabled}\n data-testid={itemTestId}\n className={`\n flex items-center gap-2 w-full px-4 py-2 text-left text-sm\n ${disabled ? 'opacity-50 cursor-not-allowed' : 'hover:bg-base-200'}\n ${danger ? 'text-error hover:bg-error/10' : ''}\n `}\n >\n {icon && <span className=\"w-4 h-4\" aria-hidden=\"true\">{icon}</span>}\n <span className=\"flex-1\">{children}</span>\n </button>\n </li>\n )\n}\n\nconst ContextMenuDividerComponent: React.FC<ContextMenuDividerProps> = ({ className = '', 'data-testid': testId }) => {\n return <hr className={`border-base-300 my-1 ${className}`} role=\"separator\" data-testid={testId} />\n}\n\nconst ContextMenuSubMenuComponent: React.FC<ContextMenuSubMenuProps> = ({\n label,\n icon,\n disabled = false,\n children,\n className = '',\n _key,\n 'data-testid': testId,\n}) => {\n const { getTestId } = useContextMenuContext()\n const [showSubmenu, setShowSubmenu] = useState(false)\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const submenuTestId = testId ?? (_key ? getTestId?.(`submenu-${_key}`) : undefined)\n\n const handleMouseEnter = () => {\n if (disabled) return\n if (timeoutRef.current) clearTimeout(timeoutRef.current)\n setShowSubmenu(true)\n }\n\n const handleMouseLeave = () => {\n timeoutRef.current = setTimeout(() => setShowSubmenu(false), 100)\n }\n\n return (\n <li\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n className={`relative ${className}`}\n role=\"none\"\n >\n <button\n disabled={disabled}\n role=\"menuitem\"\n aria-haspopup=\"menu\"\n aria-expanded={showSubmenu}\n aria-disabled={disabled}\n data-testid={submenuTestId}\n className={`\n flex items-center gap-2 w-full px-4 py-2 text-left text-sm\n ${disabled ? 'opacity-50 cursor-not-allowed' : 'hover:bg-base-200'}\n `}\n >\n {icon && <span className=\"w-4 h-4\" aria-hidden=\"true\">{icon}</span>}\n <span className=\"flex-1\">{label}</span>\n <svg className=\"w-4 h-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" aria-hidden=\"true\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n {showSubmenu && (\n <ul\n className={`${dMenu} bg-base-100 rounded-box shadow-lg border border-base-300 absolute left-full top-0 min-w-[160px] z-50 p-1`}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n role=\"menu\"\n data-testid={submenuTestId ? `${submenuTestId}-menu` : undefined}\n >\n {children}\n </ul>\n )}\n </li>\n )\n}\n\n// Data-driven pattern internal component\nconst MenuItem: React.FC<{\n item: ContextMenuItem\n onSelect: (key: string) => void\n onClose: () => void\n getTestId?: (suffix: string) => string | undefined\n}> = ({ item, onSelect, onClose, getTestId }) => {\n const [showSubmenu, setShowSubmenu] = useState(false)\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const itemTestId = item['data-testid'] ?? getTestId?.(`item-${item.key}`)\n\n if (item.divider) {\n return <hr className=\"border-base-300 my-1\" role=\"separator\" data-testid={getTestId?.(`separator-${item.key}`)} />\n }\n\n const handleClick = () => {\n if (item.disabled) return\n if (item.children && item.children.length > 0) return\n onSelect(item.key)\n onClose()\n }\n\n const hasSubmenu = item.children && item.children.length > 0\n\n const handleMouseEnter = () => {\n if (!hasSubmenu) return\n if (timeoutRef.current) clearTimeout(timeoutRef.current)\n setShowSubmenu(true)\n }\n\n const handleMouseLeave = () => {\n if (!hasSubmenu) return\n timeoutRef.current = setTimeout(() => setShowSubmenu(false), 100)\n }\n\n return (\n <li\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n className=\"relative\"\n role=\"none\"\n >\n <button\n onClick={handleClick}\n disabled={item.disabled}\n role=\"menuitem\"\n aria-haspopup={hasSubmenu ? 'menu' : undefined}\n aria-expanded={hasSubmenu ? showSubmenu : undefined}\n aria-disabled={item.disabled}\n data-testid={itemTestId}\n className={`\n flex items-center gap-2 w-full px-4 py-2 text-left text-sm\n ${item.disabled ? 'opacity-50 cursor-not-allowed' : 'hover:bg-base-200'}\n ${item.danger ? 'text-error hover:bg-error/10' : ''}\n `}\n >\n {item.icon && <span className=\"w-4 h-4\" aria-hidden=\"true\">{item.icon}</span>}\n <span className=\"flex-1\">{item.label}</span>\n {hasSubmenu && (\n <svg className=\"w-4 h-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" aria-hidden=\"true\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M9 5l7 7-7 7\" />\n </svg>\n )}\n </button>\n {hasSubmenu && showSubmenu && (\n <ul\n className={`${dMenu} bg-base-100 rounded-box shadow-lg border border-base-300 absolute left-full top-0 min-w-[160px] z-50 p-1`}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n role=\"menu\"\n data-testid={itemTestId ? `${itemTestId}-menu` : undefined}\n >\n {item.children!.map((child) => (\n <MenuItem key={child.key} item={child} onSelect={onSelect} onClose={onClose} getTestId={getTestId} />\n ))}\n </ul>\n )}\n </li>\n )\n}\n\nconst ContextMenuRoot: React.FC<ContextMenuProps> = ({\n children,\n items,\n onSelect,\n disabled = false,\n className = '',\n 'data-testid': testId,\n}) => {\n const { getPopupContainer } = useConfig()\n const [visible, setVisible] = useState(false)\n const [position, setPosition] = useState<MenuPosition>({ x: 0, y: 0 })\n const [resolvedItems, setResolvedItems] = useState<ContextMenuItem[] | undefined>(undefined)\n const menuRef = useRef<HTMLUListElement>(null)\n\n const handleContextMenu = useCallback(\n (e: React.MouseEvent) => {\n if (disabled) return\n e.preventDefault()\n e.stopPropagation()\n\n setPosition({ x: e.clientX, y: e.clientY })\n if (typeof items === 'function') {\n setResolvedItems(items(e))\n } else {\n setResolvedItems(items)\n }\n setVisible(true)\n },\n [disabled, items]\n )\n\n const handleClose = useCallback(() => {\n setVisible(false)\n }, [])\n\n const handleSelect = useCallback(\n (key: string) => {\n onSelect?.(key)\n },\n [onSelect]\n )\n\n // Adjust position after menu renders to keep it in viewport\n useEffect(() => {\n if (visible && menuRef.current) {\n const menu = menuRef.current\n const rect = menu.getBoundingClientRect()\n const viewportWidth = window.innerWidth\n const viewportHeight = window.innerHeight\n\n let { x, y } = position\n\n if (x + rect.width > viewportWidth) {\n x = viewportWidth - rect.width - 8\n }\n if (y + rect.height > viewportHeight) {\n y = viewportHeight - rect.height - 8\n }\n\n if (x !== position.x || y !== position.y) {\n setPosition({ x, y })\n }\n }\n }, [visible, position])\n\n // Close on click outside or escape\n useEffect(() => {\n if (!visible) return\n\n const handleClickOutside = (e: MouseEvent) => {\n if (menuRef.current && !menuRef.current.contains(e.target as Node)) {\n handleClose()\n }\n }\n\n const handleEscape = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n handleClose()\n }\n }\n\n const handleScroll = () => {\n handleClose()\n }\n\n document.addEventListener('mousedown', handleClickOutside)\n document.addEventListener('keydown', handleEscape)\n document.addEventListener('scroll', handleScroll, true)\n\n return () => {\n document.removeEventListener('mousedown', handleClickOutside)\n document.removeEventListener('keydown', handleEscape)\n document.removeEventListener('scroll', handleScroll, true)\n }\n }, [visible, handleClose])\n\n // Clone children to extract keys\n const cloneChildrenWithKeys = (children: React.ReactNode): React.ReactNode => {\n return React.Children.map(children, (child) => {\n if (React.isValidElement(child)) {\n const childKey = child.key != null ? String(child.key) : undefined\n if (child.type === ContextMenuItemComponent || child.type === ContextMenuSubMenuComponent) {\n return React.cloneElement(child as React.ReactElement<any>, { _key: childKey })\n }\n }\n return child\n })\n }\n\n // Separate trigger (first child) from menu content children\n const childArray = React.Children.toArray(children)\n const trigger = childArray[0]\n const menuChildren = cloneChildrenWithKeys(childArray.slice(1))\n const hasDataItems = resolvedItems && resolvedItems.length > 0\n\n const contextValue: ContextMenuContextValue = {\n onSelect: handleSelect,\n onClose: handleClose,\n getTestId: testId ? (suffix: string) => `${testId}-${suffix}` : undefined,\n }\n const getTestId = (suffix: string) => (testId ? `${testId}-${suffix}` : undefined)\n\n // Attach onContextMenu directly to trigger via cloneElement — no wrapper div\n const triggerWithHandler = React.isValidElement(trigger)\n ? React.cloneElement(trigger as React.ReactElement<any>, {\n onContextMenu: (e: React.MouseEvent) => {\n const existing = (trigger as React.ReactElement<any>).props.onContextMenu\n if (existing) existing(e)\n handleContextMenu(e)\n },\n })\n : trigger\n\n return (\n <>\n {triggerWithHandler}\n {visible &&\n createPortal(\n <ContextMenuContext.Provider value={contextValue}>\n <ul\n ref={menuRef}\n className={`${dMenu} bg-base-100 rounded-box shadow-lg border border-base-300 min-w-[160px] p-1 fixed z-[9999] ${className}`}\n style={{ left: position.x, top: position.y }}\n role=\"menu\"\n aria-label=\"Context menu\"\n data-testid={getTestId('menu')}\n >\n {hasDataItems\n ? resolvedItems.map((item) => (\n <MenuItem key={item.key} item={item} onSelect={handleSelect} onClose={handleClose} getTestId={getTestId} />\n ))\n : menuChildren}\n </ul>\n </ContextMenuContext.Provider>,\n getPopupContainer ? getPopupContainer(document.body) : document.body\n )}\n </>\n )\n}\n\n// Assign compound components\nexport const ContextMenu = Object.assign(ContextMenuRoot, {\n Item: ContextMenuItemComponent,\n Divider: ContextMenuDividerComponent,\n SubMenu: ContextMenuSubMenuComponent,\n})\n"],"names":["dMenu","ContextMenuContext","createContext","useContextMenuContext","context","useContext","ContextMenuItemComponent","children","icon","disabled","danger","className","_key","testId","onSelect","onClose","getTestId","itemTestId","jsx","jsxs","ContextMenuDividerComponent","ContextMenuSubMenuComponent","label","showSubmenu","setShowSubmenu","useState","timeoutRef","useRef","submenuTestId","handleMouseEnter","handleMouseLeave","MenuItem","item","handleClick","hasSubmenu","child","ContextMenuRoot","items","getPopupContainer","useConfig","visible","setVisible","position","setPosition","resolvedItems","setResolvedItems","menuRef","handleContextMenu","useCallback","e","handleClose","handleSelect","key","useEffect","rect","viewportWidth","viewportHeight","x","y","handleClickOutside","handleEscape","handleScroll","cloneChildrenWithKeys","React","childKey","childArray","trigger","menuChildren","hasDataItems","contextValue","suffix","triggerWithHandler","existing","Fragment","createPortal","ContextMenu"],"mappings":"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"}
|
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import { forwardRef as
|
|
1
|
+
import { jsx as H } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef as $, useRef as m, useImperativeHandle as j, useEffect as z } from "react";
|
|
3
3
|
import * as p from "@xterm/xterm";
|
|
4
4
|
import * as g from "@xterm/addon-fit";
|
|
5
|
-
import { useTheme as
|
|
6
|
-
const
|
|
7
|
-
let
|
|
8
|
-
function
|
|
9
|
-
if (
|
|
10
|
-
|
|
5
|
+
import { useTheme as X } from "../hooks/useTheme.js";
|
|
6
|
+
const E = p.Terminal ?? p.default?.Terminal ?? p, L = g.FitAddon ?? g.default?.FitAddon ?? g;
|
|
7
|
+
let I = !1;
|
|
8
|
+
function P() {
|
|
9
|
+
if (I || typeof document > "u") return;
|
|
10
|
+
I = !0;
|
|
11
11
|
const u = document.createElement("style");
|
|
12
12
|
u.setAttribute("data-xterm", ""), u.textContent = ".xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{user-select:text;white-space:pre}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:.5}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}", document.head.appendChild(u);
|
|
13
13
|
}
|
|
14
|
-
const
|
|
14
|
+
const W = $(({
|
|
15
15
|
onData: u,
|
|
16
|
-
onReady:
|
|
16
|
+
onReady: A,
|
|
17
17
|
readline: y = !1,
|
|
18
18
|
prompt: c = "$ ",
|
|
19
|
-
onLine:
|
|
20
|
-
convertEol:
|
|
21
|
-
options:
|
|
22
|
-
className:
|
|
23
|
-
style:
|
|
24
|
-
"data-testid":
|
|
25
|
-
},
|
|
26
|
-
const
|
|
19
|
+
onLine: F,
|
|
20
|
+
convertEol: C = !0,
|
|
21
|
+
options: w = {},
|
|
22
|
+
className: R = "",
|
|
23
|
+
style: T,
|
|
24
|
+
"data-testid": D
|
|
25
|
+
}, S) => {
|
|
26
|
+
const a = m(null), o = m(null), h = m(null), { isDark: f, colors: n } = X(), d = m({
|
|
27
27
|
buffer: "",
|
|
28
28
|
cursor: 0,
|
|
29
29
|
history: [],
|
|
30
30
|
historyIndex: -1,
|
|
31
31
|
savedBuffer: ""
|
|
32
|
-
}),
|
|
32
|
+
}), v = () => ({
|
|
33
33
|
background: n.background,
|
|
34
34
|
foreground: n.foreground,
|
|
35
|
-
cursor: n.
|
|
35
|
+
cursor: n.foreground,
|
|
36
36
|
cursorAccent: n.background,
|
|
37
37
|
selectionBackground: n.primary + "40",
|
|
38
38
|
selectionForeground: n.foreground,
|
|
@@ -53,7 +53,7 @@ const L = H(({
|
|
|
53
53
|
brightCyan: n.accent,
|
|
54
54
|
brightWhite: f ? "#eeeeec" : "#ffffff"
|
|
55
55
|
});
|
|
56
|
-
|
|
56
|
+
j(S, () => ({
|
|
57
57
|
terminal: o.current,
|
|
58
58
|
write: (r) => o.current?.write(r),
|
|
59
59
|
writeln: (r) => o.current?.writeln(r),
|
|
@@ -72,7 +72,7 @@ const L = H(({
|
|
|
72
72
|
r.write("\r\x1B[K" + c + t.buffer);
|
|
73
73
|
const e = t.buffer.length - t.cursor;
|
|
74
74
|
e > 0 && r.write(`\x1B[${e}D`);
|
|
75
|
-
},
|
|
75
|
+
}, M = (r) => {
|
|
76
76
|
const t = o.current;
|
|
77
77
|
if (!t) return;
|
|
78
78
|
const e = d.current;
|
|
@@ -109,46 +109,49 @@ const L = H(({
|
|
|
109
109
|
t.writeln("");
|
|
110
110
|
const i = e.buffer;
|
|
111
111
|
i.trim() && e.history.push(i), e.buffer = "", e.cursor = 0, e.historyIndex = -1, e.savedBuffer = "";
|
|
112
|
-
const s =
|
|
112
|
+
const s = F?.(i);
|
|
113
113
|
s && typeof s.then == "function" ? s.then(() => t.write(c)) : t.write(c);
|
|
114
114
|
} else r === "" || r === "\b" ? e.cursor > 0 && (e.buffer = e.buffer.slice(0, e.cursor - 1) + e.buffer.slice(e.cursor), e.cursor--, l()) : r === "" ? (t.writeln("^C"), e.buffer = "", e.cursor = 0, e.historyIndex = -1, t.write(c)) : (r >= " " || r === " ") && (e.buffer = e.buffer.slice(0, e.cursor) + r + e.buffer.slice(e.cursor), e.cursor += r.length, l());
|
|
115
115
|
};
|
|
116
|
-
return
|
|
117
|
-
if (!
|
|
118
|
-
|
|
119
|
-
const r =
|
|
116
|
+
return z(() => {
|
|
117
|
+
if (!a.current) return;
|
|
118
|
+
P();
|
|
119
|
+
const r = a.current;
|
|
120
120
|
let t = null, e = null, i = null, s = !1, b = !1;
|
|
121
|
-
const
|
|
121
|
+
const B = () => {
|
|
122
122
|
if (s || b || !r) return;
|
|
123
123
|
const x = r.getBoundingClientRect();
|
|
124
|
-
x.width === 0 || x.height === 0
|
|
125
|
-
|
|
124
|
+
if (x.width === 0 || x.height === 0) return;
|
|
125
|
+
s = !0;
|
|
126
|
+
const { theme: k, ...O } = w;
|
|
127
|
+
t = new E({
|
|
128
|
+
theme: { ...v(), ...k },
|
|
126
129
|
cursorBlink: !0,
|
|
127
|
-
convertEol:
|
|
130
|
+
convertEol: C,
|
|
128
131
|
fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace',
|
|
129
132
|
fontSize: 14,
|
|
130
|
-
...
|
|
131
|
-
}), e = new
|
|
133
|
+
...O
|
|
134
|
+
}), e = new L(), t.loadAddon(e), t.open(r), e.fit(), o.current = t, h.current = e, y ? t.onData(M) : u && t.onData(u), A?.(t), y && t.write(c);
|
|
132
135
|
};
|
|
133
136
|
return i = new ResizeObserver((x) => {
|
|
134
|
-
!x[0] || b || (s ? e && e.fit() :
|
|
135
|
-
}), i.observe(r), requestAnimationFrame(
|
|
137
|
+
!x[0] || b || (s ? e && e.fit() : B());
|
|
138
|
+
}), i.observe(r), requestAnimationFrame(B), () => {
|
|
136
139
|
b = !0, i?.disconnect(), t?.dispose(), o.current = null, h.current = null;
|
|
137
140
|
};
|
|
138
|
-
}, []),
|
|
139
|
-
o.current && (o.current.options.theme =
|
|
140
|
-
}, [f, n]), /* @__PURE__ */
|
|
141
|
+
}, []), z(() => {
|
|
142
|
+
o.current && (o.current.options.theme = { ...v(), ...w.theme });
|
|
143
|
+
}, [f, n]), /* @__PURE__ */ H(
|
|
141
144
|
"div",
|
|
142
145
|
{
|
|
143
|
-
ref:
|
|
144
|
-
className:
|
|
145
|
-
style: { width: "100%", height: "100%", ...
|
|
146
|
-
"data-testid":
|
|
146
|
+
ref: a,
|
|
147
|
+
className: R,
|
|
148
|
+
style: { width: "100%", height: "100%", ...T },
|
|
149
|
+
"data-testid": D
|
|
147
150
|
}
|
|
148
151
|
);
|
|
149
152
|
});
|
|
150
|
-
|
|
153
|
+
W.displayName = "Terminal";
|
|
151
154
|
export {
|
|
152
|
-
|
|
155
|
+
W as Terminal
|
|
153
156
|
};
|
|
154
157
|
//# sourceMappingURL=Terminal.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Terminal.js","sources":["../../src/components/Terminal.tsx"],"sourcesContent":["import React, { useEffect, useRef, useImperativeHandle, forwardRef } from 'react'\nimport * as XTermPkg from '@xterm/xterm'\nimport * as FitAddonPkg from '@xterm/addon-fit'\nimport type { Terminal as XTermType, ITerminalOptions, ITerminalInitOnlyOptions } from '@xterm/xterm'\nimport type { FitAddon as FitAddonType } from '@xterm/addon-fit'\nimport { useTheme } from '../hooks/useTheme'\n\n// Handle both ESM and CJS module formats\nconst XTerm = (XTermPkg as { Terminal?: typeof XTermType }).Terminal\n ?? (XTermPkg as { default?: { Terminal: typeof XTermType } }).default?.Terminal\n ?? (XTermPkg as unknown as typeof XTermType)\nconst FitAddon = (FitAddonPkg as { FitAddon?: typeof FitAddonType }).FitAddon\n ?? (FitAddonPkg as { default?: { FitAddon: typeof FitAddonType } }).default?.FitAddon\n ?? (FitAddonPkg as unknown as typeof FitAddonType)\n\n// Inject xterm.css once (inlined for reliability across bundlers)\nlet cssInjected = false\nfunction injectXtermCSS() {\n if (cssInjected || typeof document === 'undefined') return\n cssInjected = true\n\n const style = document.createElement('style')\n style.setAttribute('data-xterm', '')\n style.textContent = `.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{user-select:text;white-space:pre}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:.5}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}`\n document.head.appendChild(style)\n}\n\nexport interface TerminalRef {\n /** The underlying xterm.js Terminal instance */\n terminal: XTermType | null\n /** Write data to the terminal */\n write: (data: string) => void\n /** Write a line to the terminal (with newline) */\n writeln: (data: string) => void\n /** Clear the terminal */\n clear: () => void\n /** Focus the terminal */\n focus: () => void\n /** Fit the terminal to its container */\n fit: () => void\n /** Command history (only available with readline mode) */\n history: string[]\n /** Clear command history (only available with readline mode) */\n clearHistory: () => void\n}\n\nexport interface TerminalProps {\n /** Callback when user types in the terminal (raw input) */\n onData?: (data: string) => void\n /** Callback when terminal is ready */\n onReady?: (terminal: XTermType) => void\n /** Enable readline mode with line editing and history */\n readline?: boolean\n /** Prompt string for readline mode (supports ANSI colors) */\n prompt?: string\n /** Callback when user submits a line in readline mode. Return a Promise to defer the next prompt. */\n onLine?: (line: string) => void | Promise<void>\n /** Convert LF to CRLF for proper newline handling (default: true) */\n convertEol?: boolean\n /** xterm.js options (theme is auto-applied unless you override it) */\n options?: ITerminalOptions & ITerminalInitOnlyOptions\n /** Additional CSS classes for the container */\n className?: string\n /** Container style */\n style?: React.CSSProperties\n 'data-testid'?: string\n}\n\nexport const Terminal = forwardRef<TerminalRef, TerminalProps>(({\n onData,\n onReady,\n readline = false,\n prompt = '$ ',\n onLine,\n convertEol = true,\n options = {},\n className = '',\n style,\n 'data-testid': testId,\n}, ref) => {\n const containerRef = useRef<HTMLDivElement>(null)\n const terminalRef = useRef<XTermType | null>(null)\n const fitAddonRef = useRef<FitAddonType | null>(null)\n const { isDark, colors } = useTheme()\n\n // Readline state\n const readlineState = useRef({\n buffer: '',\n cursor: 0,\n history: [] as string[],\n historyIndex: -1,\n savedBuffer: '',\n })\n\n // Build theme from DaisyUI colors\n const getTheme = () => ({\n background: colors.background,\n foreground: colors.foreground,\n cursor: colors.primary,\n cursorAccent: colors.background,\n selectionBackground: colors.primary + '40',\n selectionForeground: colors.foreground,\n black: isDark ? '#000000' : '#2e3436',\n red: colors.error,\n green: colors.success,\n yellow: colors.warning,\n blue: colors.info,\n magenta: colors.secondary,\n cyan: colors.accent,\n white: isDark ? '#d3d7cf' : '#eeeeec',\n brightBlack: '#555753',\n brightRed: colors.error,\n brightGreen: colors.success,\n brightYellow: colors.warning,\n brightBlue: colors.info,\n brightMagenta: colors.secondary,\n brightCyan: colors.accent,\n brightWhite: isDark ? '#eeeeec' : '#ffffff',\n })\n\n useImperativeHandle(ref, () => ({\n terminal: terminalRef.current,\n write: (data: string) => terminalRef.current?.write(data),\n writeln: (data: string) => terminalRef.current?.writeln(data),\n clear: () => terminalRef.current?.clear(),\n focus: () => terminalRef.current?.focus(),\n fit: () => fitAddonRef.current?.fit(),\n history: readlineState.current.history,\n clearHistory: () => { readlineState.current.history = [] },\n }), [])\n\n // Redraw the current line in readline mode\n const redrawLine = () => {\n const term = terminalRef.current\n if (!term) return\n const s = readlineState.current\n // Move to start of line, clear line, write prompt + buffer, position cursor\n term.write('\\r\\x1b[K' + prompt + s.buffer)\n // Move cursor to correct position\n const moveBack = s.buffer.length - s.cursor\n if (moveBack > 0) {\n term.write(`\\x1b[${moveBack}D`)\n }\n }\n\n // Handle readline input\n const handleReadlineData = (data: string) => {\n const term = terminalRef.current\n if (!term) return\n const s = readlineState.current\n\n // Handle escape sequences (arrow keys)\n if (data === '\\x1b[A') {\n // Up arrow - history previous\n if (s.history.length > 0 && s.historyIndex < s.history.length - 1) {\n if (s.historyIndex === -1) s.savedBuffer = s.buffer\n s.historyIndex++\n s.buffer = s.history[s.history.length - 1 - s.historyIndex]\n s.cursor = s.buffer.length\n redrawLine()\n }\n return\n }\n if (data === '\\x1b[B') {\n // Down arrow - history next\n if (s.historyIndex > -1) {\n s.historyIndex--\n s.buffer = s.historyIndex === -1 ? s.savedBuffer : s.history[s.history.length - 1 - s.historyIndex]\n s.cursor = s.buffer.length\n redrawLine()\n }\n return\n }\n if (data === '\\x1b[C') {\n // Right arrow\n if (s.cursor < s.buffer.length) {\n s.cursor++\n term.write('\\x1b[C')\n }\n return\n }\n if (data === '\\x1b[D') {\n // Left arrow\n if (s.cursor > 0) {\n s.cursor--\n term.write('\\x1b[D')\n }\n return\n }\n if (data === '\\x1b[H' || data === '\\x1bOH' || data === '\\x1b[1~') {\n // Home - move to start of line\n if (s.cursor > 0) {\n term.write(`\\x1b[${s.cursor}D`)\n s.cursor = 0\n }\n return\n }\n if (data === '\\x1b[F' || data === '\\x1bOF' || data === '\\x1b[4~') {\n // End - move to end of line\n if (s.cursor < s.buffer.length) {\n term.write(`\\x1b[${s.buffer.length - s.cursor}C`)\n s.cursor = s.buffer.length\n }\n return\n }\n if (data === '\\x1b[3~') {\n // Delete key - delete character at cursor\n if (s.cursor < s.buffer.length) {\n s.buffer = s.buffer.slice(0, s.cursor) + s.buffer.slice(s.cursor + 1)\n redrawLine()\n }\n return\n }\n\n // Ignore other escape sequences\n if (data.startsWith('\\x1b')) return\n\n if (data === '\\r') {\n // Enter\n term.writeln('')\n const line = s.buffer\n if (line.trim()) {\n s.history.push(line)\n }\n s.buffer = ''\n s.cursor = 0\n s.historyIndex = -1\n s.savedBuffer = ''\n const result = onLine?.(line)\n if (result && typeof (result as any).then === 'function') {\n (result as any).then(() => term.write(prompt))\n } else {\n term.write(prompt)\n }\n } else if (data === '\\x7f' || data === '\\b') {\n // Backspace\n if (s.cursor > 0) {\n s.buffer = s.buffer.slice(0, s.cursor - 1) + s.buffer.slice(s.cursor)\n s.cursor--\n redrawLine()\n }\n } else if (data === '\\x03') {\n // Ctrl+C\n term.writeln('^C')\n s.buffer = ''\n s.cursor = 0\n s.historyIndex = -1\n term.write(prompt)\n } else if (data >= ' ' || data === '\\t') {\n // Printable characters\n s.buffer = s.buffer.slice(0, s.cursor) + data + s.buffer.slice(s.cursor)\n s.cursor += data.length\n redrawLine()\n }\n }\n\n // Initialize terminal\n useEffect(() => {\n if (!containerRef.current) return\n\n injectXtermCSS()\n\n const container = containerRef.current\n let terminal: XTermType | null = null\n let fitAddon: FitAddonType | null = null\n let resizeObserver: ResizeObserver | null = null\n let initialized = false\n let disposed = false\n\n const initTerminal = () => {\n if (initialized || disposed || !container) return\n\n // Check container has dimensions before opening\n const rect = container.getBoundingClientRect()\n if (rect.width === 0 || rect.height === 0) return\n\n initialized = true\n\n terminal = new XTerm({\n theme: getTheme(),\n cursorBlink: true,\n convertEol,\n fontFamily: 'ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace',\n fontSize: 14,\n ...options,\n })\n fitAddon = new FitAddon()\n\n terminal.loadAddon(fitAddon)\n terminal.open(container)\n fitAddon.fit()\n\n terminalRef.current = terminal\n fitAddonRef.current = fitAddon\n\n if (readline) {\n terminal.onData(handleReadlineData)\n } else if (onData) {\n terminal.onData(onData)\n }\n\n onReady?.(terminal)\n\n // Write initial prompt after onReady so welcome messages appear first\n if (readline) {\n terminal.write(prompt)\n }\n }\n\n // Use ResizeObserver to wait for container to have dimensions\n resizeObserver = new ResizeObserver((entries) => {\n const entry = entries[0]\n if (!entry || disposed) return\n\n if (!initialized) {\n initTerminal()\n } else if (fitAddon) {\n fitAddon.fit()\n }\n })\n resizeObserver.observe(container)\n\n // Also try immediately in case container already has dimensions\n requestAnimationFrame(initTerminal)\n\n return () => {\n disposed = true\n resizeObserver?.disconnect()\n terminal?.dispose()\n terminalRef.current = null\n fitAddonRef.current = null\n }\n }, []) // eslint-disable-line react-hooks/exhaustive-deps\n\n // Update theme when colors change\n useEffect(() => {\n if (!terminalRef.current) return\n terminalRef.current.options.theme = getTheme()\n }, [isDark, colors]) // eslint-disable-line react-hooks/exhaustive-deps\n\n return (\n <div\n ref={containerRef}\n className={className}\n style={{ width: '100%', height: '100%', ...style }}\n data-testid={testId}\n />\n )\n})\n\nTerminal.displayName = 'Terminal'\n"],"names":["XTerm","XTermPkg","FitAddon","FitAddonPkg","cssInjected","injectXtermCSS","style","Terminal","forwardRef","onData","onReady","readline","prompt","onLine","convertEol","options","className","testId","ref","containerRef","useRef","terminalRef","fitAddonRef","isDark","colors","useTheme","readlineState","getTheme","useImperativeHandle","data","redrawLine","term","s","moveBack","handleReadlineData","line","result","useEffect","container","terminal","fitAddon","resizeObserver","initialized","disposed","initTerminal","rect","entries","jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Terminal.js","sources":["../../src/components/Terminal.tsx"],"sourcesContent":["import React, { useEffect, useRef, useImperativeHandle, forwardRef } from 'react'\nimport * as XTermPkg from '@xterm/xterm'\nimport * as FitAddonPkg from '@xterm/addon-fit'\nimport type { Terminal as XTermType, ITerminalOptions, ITerminalInitOnlyOptions } from '@xterm/xterm'\nimport type { FitAddon as FitAddonType } from '@xterm/addon-fit'\nimport { useTheme } from '../hooks/useTheme'\n\n// Handle both ESM and CJS module formats\nconst XTerm = (XTermPkg as { Terminal?: typeof XTermType }).Terminal\n ?? (XTermPkg as { default?: { Terminal: typeof XTermType } }).default?.Terminal\n ?? (XTermPkg as unknown as typeof XTermType)\nconst FitAddon = (FitAddonPkg as { FitAddon?: typeof FitAddonType }).FitAddon\n ?? (FitAddonPkg as { default?: { FitAddon: typeof FitAddonType } }).default?.FitAddon\n ?? (FitAddonPkg as unknown as typeof FitAddonType)\n\n// Inject xterm.css once (inlined for reliability across bundlers)\nlet cssInjected = false\nfunction injectXtermCSS() {\n if (cssInjected || typeof document === 'undefined') return\n cssInjected = true\n\n const style = document.createElement('style')\n style.setAttribute('data-xterm', '')\n style.textContent = `.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{user-select:text;white-space:pre}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:.5}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}`\n document.head.appendChild(style)\n}\n\nexport interface TerminalRef {\n /** The underlying xterm.js Terminal instance */\n terminal: XTermType | null\n /** Write data to the terminal */\n write: (data: string) => void\n /** Write a line to the terminal (with newline) */\n writeln: (data: string) => void\n /** Clear the terminal */\n clear: () => void\n /** Focus the terminal */\n focus: () => void\n /** Fit the terminal to its container */\n fit: () => void\n /** Command history (only available with readline mode) */\n history: string[]\n /** Clear command history (only available with readline mode) */\n clearHistory: () => void\n}\n\nexport interface TerminalProps {\n /** Callback when user types in the terminal (raw input) */\n onData?: (data: string) => void\n /** Callback when terminal is ready */\n onReady?: (terminal: XTermType) => void\n /** Enable readline mode with line editing and history */\n readline?: boolean\n /** Prompt string for readline mode (supports ANSI colors) */\n prompt?: string\n /** Callback when user submits a line in readline mode. Return a Promise to defer the next prompt. */\n onLine?: (line: string) => void | Promise<void>\n /** Convert LF to CRLF for proper newline handling (default: true) */\n convertEol?: boolean\n /** xterm.js options (theme is auto-applied unless you override it) */\n options?: ITerminalOptions & ITerminalInitOnlyOptions\n /** Additional CSS classes for the container */\n className?: string\n /** Container style */\n style?: React.CSSProperties\n 'data-testid'?: string\n}\n\nexport const Terminal = forwardRef<TerminalRef, TerminalProps>(({\n onData,\n onReady,\n readline = false,\n prompt = '$ ',\n onLine,\n convertEol = true,\n options = {},\n className = '',\n style,\n 'data-testid': testId,\n}, ref) => {\n const containerRef = useRef<HTMLDivElement>(null)\n const terminalRef = useRef<XTermType | null>(null)\n const fitAddonRef = useRef<FitAddonType | null>(null)\n const { isDark, colors } = useTheme()\n\n // Readline state\n const readlineState = useRef({\n buffer: '',\n cursor: 0,\n history: [] as string[],\n historyIndex: -1,\n savedBuffer: '',\n })\n\n // Build theme from DaisyUI colors\n const getTheme = () => ({\n background: colors.background,\n foreground: colors.foreground,\n cursor: colors.foreground,\n cursorAccent: colors.background,\n selectionBackground: colors.primary + '40',\n selectionForeground: colors.foreground,\n black: isDark ? '#000000' : '#2e3436',\n red: colors.error,\n green: colors.success,\n yellow: colors.warning,\n blue: colors.info,\n magenta: colors.secondary,\n cyan: colors.accent,\n white: isDark ? '#d3d7cf' : '#eeeeec',\n brightBlack: '#555753',\n brightRed: colors.error,\n brightGreen: colors.success,\n brightYellow: colors.warning,\n brightBlue: colors.info,\n brightMagenta: colors.secondary,\n brightCyan: colors.accent,\n brightWhite: isDark ? '#eeeeec' : '#ffffff',\n })\n\n useImperativeHandle(ref, () => ({\n terminal: terminalRef.current,\n write: (data: string) => terminalRef.current?.write(data),\n writeln: (data: string) => terminalRef.current?.writeln(data),\n clear: () => terminalRef.current?.clear(),\n focus: () => terminalRef.current?.focus(),\n fit: () => fitAddonRef.current?.fit(),\n history: readlineState.current.history,\n clearHistory: () => { readlineState.current.history = [] },\n }), [])\n\n // Redraw the current line in readline mode\n const redrawLine = () => {\n const term = terminalRef.current\n if (!term) return\n const s = readlineState.current\n // Move to start of line, clear line, write prompt + buffer, position cursor\n term.write('\\r\\x1b[K' + prompt + s.buffer)\n // Move cursor to correct position\n const moveBack = s.buffer.length - s.cursor\n if (moveBack > 0) {\n term.write(`\\x1b[${moveBack}D`)\n }\n }\n\n // Handle readline input\n const handleReadlineData = (data: string) => {\n const term = terminalRef.current\n if (!term) return\n const s = readlineState.current\n\n // Handle escape sequences (arrow keys)\n if (data === '\\x1b[A') {\n // Up arrow - history previous\n if (s.history.length > 0 && s.historyIndex < s.history.length - 1) {\n if (s.historyIndex === -1) s.savedBuffer = s.buffer\n s.historyIndex++\n s.buffer = s.history[s.history.length - 1 - s.historyIndex]\n s.cursor = s.buffer.length\n redrawLine()\n }\n return\n }\n if (data === '\\x1b[B') {\n // Down arrow - history next\n if (s.historyIndex > -1) {\n s.historyIndex--\n s.buffer = s.historyIndex === -1 ? s.savedBuffer : s.history[s.history.length - 1 - s.historyIndex]\n s.cursor = s.buffer.length\n redrawLine()\n }\n return\n }\n if (data === '\\x1b[C') {\n // Right arrow\n if (s.cursor < s.buffer.length) {\n s.cursor++\n term.write('\\x1b[C')\n }\n return\n }\n if (data === '\\x1b[D') {\n // Left arrow\n if (s.cursor > 0) {\n s.cursor--\n term.write('\\x1b[D')\n }\n return\n }\n if (data === '\\x1b[H' || data === '\\x1bOH' || data === '\\x1b[1~') {\n // Home - move to start of line\n if (s.cursor > 0) {\n term.write(`\\x1b[${s.cursor}D`)\n s.cursor = 0\n }\n return\n }\n if (data === '\\x1b[F' || data === '\\x1bOF' || data === '\\x1b[4~') {\n // End - move to end of line\n if (s.cursor < s.buffer.length) {\n term.write(`\\x1b[${s.buffer.length - s.cursor}C`)\n s.cursor = s.buffer.length\n }\n return\n }\n if (data === '\\x1b[3~') {\n // Delete key - delete character at cursor\n if (s.cursor < s.buffer.length) {\n s.buffer = s.buffer.slice(0, s.cursor) + s.buffer.slice(s.cursor + 1)\n redrawLine()\n }\n return\n }\n\n // Ignore other escape sequences\n if (data.startsWith('\\x1b')) return\n\n if (data === '\\r') {\n // Enter\n term.writeln('')\n const line = s.buffer\n if (line.trim()) {\n s.history.push(line)\n }\n s.buffer = ''\n s.cursor = 0\n s.historyIndex = -1\n s.savedBuffer = ''\n const result = onLine?.(line)\n if (result && typeof (result as any).then === 'function') {\n (result as any).then(() => term.write(prompt))\n } else {\n term.write(prompt)\n }\n } else if (data === '\\x7f' || data === '\\b') {\n // Backspace\n if (s.cursor > 0) {\n s.buffer = s.buffer.slice(0, s.cursor - 1) + s.buffer.slice(s.cursor)\n s.cursor--\n redrawLine()\n }\n } else if (data === '\\x03') {\n // Ctrl+C\n term.writeln('^C')\n s.buffer = ''\n s.cursor = 0\n s.historyIndex = -1\n term.write(prompt)\n } else if (data >= ' ' || data === '\\t') {\n // Printable characters\n s.buffer = s.buffer.slice(0, s.cursor) + data + s.buffer.slice(s.cursor)\n s.cursor += data.length\n redrawLine()\n }\n }\n\n // Initialize terminal\n useEffect(() => {\n if (!containerRef.current) return\n\n injectXtermCSS()\n\n const container = containerRef.current\n let terminal: XTermType | null = null\n let fitAddon: FitAddonType | null = null\n let resizeObserver: ResizeObserver | null = null\n let initialized = false\n let disposed = false\n\n const initTerminal = () => {\n if (initialized || disposed || !container) return\n\n // Check container has dimensions before opening\n const rect = container.getBoundingClientRect()\n if (rect.width === 0 || rect.height === 0) return\n\n initialized = true\n\n const { theme: userTheme, ...restOptions } = options\n terminal = new XTerm({\n theme: { ...getTheme(), ...userTheme },\n cursorBlink: true,\n convertEol,\n fontFamily: 'ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace',\n fontSize: 14,\n ...restOptions,\n })\n fitAddon = new FitAddon()\n\n terminal.loadAddon(fitAddon)\n terminal.open(container)\n fitAddon.fit()\n\n terminalRef.current = terminal\n fitAddonRef.current = fitAddon\n\n if (readline) {\n terminal.onData(handleReadlineData)\n } else if (onData) {\n terminal.onData(onData)\n }\n\n onReady?.(terminal)\n\n // Write initial prompt after onReady so welcome messages appear first\n if (readline) {\n terminal.write(prompt)\n }\n }\n\n // Use ResizeObserver to wait for container to have dimensions\n resizeObserver = new ResizeObserver((entries) => {\n const entry = entries[0]\n if (!entry || disposed) return\n\n if (!initialized) {\n initTerminal()\n } else if (fitAddon) {\n fitAddon.fit()\n }\n })\n resizeObserver.observe(container)\n\n // Also try immediately in case container already has dimensions\n requestAnimationFrame(initTerminal)\n\n return () => {\n disposed = true\n resizeObserver?.disconnect()\n terminal?.dispose()\n terminalRef.current = null\n fitAddonRef.current = null\n }\n }, []) // eslint-disable-line react-hooks/exhaustive-deps\n\n // Update theme when colors change\n useEffect(() => {\n if (!terminalRef.current) return\n terminalRef.current.options.theme = { ...getTheme(), ...options.theme }\n }, [isDark, colors]) // eslint-disable-line react-hooks/exhaustive-deps\n\n return (\n <div\n ref={containerRef}\n className={className}\n style={{ width: '100%', height: '100%', ...style }}\n data-testid={testId}\n />\n )\n})\n\nTerminal.displayName = 'Terminal'\n"],"names":["XTerm","XTermPkg","FitAddon","FitAddonPkg","cssInjected","injectXtermCSS","style","Terminal","forwardRef","onData","onReady","readline","prompt","onLine","convertEol","options","className","testId","ref","containerRef","useRef","terminalRef","fitAddonRef","isDark","colors","useTheme","readlineState","getTheme","useImperativeHandle","data","redrawLine","term","s","moveBack","handleReadlineData","line","result","useEffect","container","terminal","fitAddon","resizeObserver","initialized","disposed","initTerminal","rect","userTheme","restOptions","entries","jsx"],"mappings":"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"}
|