@aster-ui/prefixed 0.12.84 → 0.12.86

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.
@@ -1,7 +1,7 @@
1
1
  import { jsx as N } from "react/jsx-runtime";
2
- import { forwardRef as W, useRef as d, useEffect as g } from "react";
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 e, highlightSpecialChars as _, 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";
4
+ import { EditorView as e, 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
5
  import { indentOnInput as Y, syntaxHighlighting as Z, defaultHighlightStyle as tt, foldKeymap as et, foldGutter as ot, bracketMatching as rt } from "@codemirror/language";
6
6
  import { history as it, defaultKeymap as nt, historyKeymap as ct, indentWithTab as st } from "@codemirror/commands";
7
7
  import { highlightSelectionMatches as lt, searchKeymap as at } from "@codemirror/search";
@@ -90,7 +90,7 @@ const wt = e.theme({
90
90
  color: "oklch(var(--bc) / 0.4)",
91
91
  fontStyle: "italic"
92
92
  }
93
- }), jt = W(
93
+ }), jt = O(
94
94
  ({
95
95
  value: o = "",
96
96
  onChange: l,
@@ -122,7 +122,7 @@ const wt = e.theme({
122
122
  if (!m.current) return;
123
123
  const t = [
124
124
  wt,
125
- _(),
125
+ W(),
126
126
  it(),
127
127
  D(),
128
128
  I(),
@@ -171,11 +171,11 @@ const wt = e.theme({
171
171
  }
172
172
  })
173
173
  ), t.push(...H);
174
- const O = b.create({
174
+ const F = b.create({
175
175
  doc: o,
176
176
  extensions: t
177
177
  }), c = new e({
178
- state: O,
178
+ state: F,
179
179
  parent: m.current
180
180
  });
181
181
  return f.current = c, S && c.focus(), u && u(c), () => {
@@ -208,7 +208,7 @@ const wt = e.theme({
208
208
  }
209
209
  });
210
210
  }, [o]);
211
- const F = typeof a == "number" ? `${a}px` : a, L = n ? typeof n == "number" ? `${n}px` : n : void 0;
211
+ const _ = 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
  {
@@ -224,9 +224,9 @@ const wt = e.theme({
224
224
  "div",
225
225
  {
226
226
  ref: m,
227
- className: "[&_.cm-editor]:outline-none",
227
+ className: "h-full [&_.cm-editor]:outline-none [&_.cm-editor]:h-full",
228
228
  style: {
229
- minHeight: F,
229
+ minHeight: _,
230
230
  maxHeight: L,
231
231
  overflow: L ? "auto" : void 0
232
232
  }
@@ -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=\"[&_.cm-editor]:outline-none\"\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
+ {"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":"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"}
@@ -27,8 +27,8 @@ export interface TerminalProps {
27
27
  readline?: boolean;
28
28
  /** Prompt string for readline mode (supports ANSI colors) */
29
29
  prompt?: string;
30
- /** Callback when user submits a line in readline mode */
31
- onLine?: (line: string) => void;
30
+ /** Callback when user submits a line in readline mode. Return a Promise to defer the next prompt. */
31
+ onLine?: (line: string) => void | Promise<void>;
32
32
  /** Convert LF to CRLF for proper newline handling (default: true) */
33
33
  convertEol?: boolean;
34
34
  /** xterm.js options (theme is auto-applied unless you override it) */
@@ -1,5 +1,5 @@
1
1
  import { jsx as M } from "react/jsx-runtime";
2
- import { forwardRef as H, useRef as x, useImperativeHandle as O, useEffect as B } from "react";
2
+ import { forwardRef as H, useRef as a, useImperativeHandle as O, useEffect as B } from "react";
3
3
  import * as p from "@xterm/xterm";
4
4
  import * as g from "@xterm/addon-fit";
5
5
  import { useTheme as $ } from "../hooks/useTheme.js";
@@ -8,11 +8,11 @@ let k = !1;
8
8
  function E() {
9
9
  if (k || typeof document > "u") return;
10
10
  k = !0;
11
- const s = document.createElement("style");
12
- s.setAttribute("data-xterm", ""), s.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(s);
11
+ const u = document.createElement("style");
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
14
  const L = H(({
15
- onData: s,
15
+ onData: u,
16
16
  onReady: z,
17
17
  readline: y = !1,
18
18
  prompt: c = "$ ",
@@ -23,7 +23,7 @@ const L = H(({
23
23
  style: R,
24
24
  "data-testid": T
25
25
  }, D) => {
26
- const a = x(null), o = x(null), m = x(null), { isDark: l, colors: n } = $(), f = x({
26
+ const m = a(null), o = a(null), h = a(null), { isDark: f, colors: n } = $(), d = a({
27
27
  buffer: "",
28
28
  cursor: 0,
29
29
  history: [],
@@ -36,14 +36,14 @@ const L = H(({
36
36
  cursorAccent: n.background,
37
37
  selectionBackground: n.primary + "40",
38
38
  selectionForeground: n.foreground,
39
- black: l ? "#000000" : "#2e3436",
39
+ black: f ? "#000000" : "#2e3436",
40
40
  red: n.error,
41
41
  green: n.success,
42
42
  yellow: n.warning,
43
43
  blue: n.info,
44
44
  magenta: n.secondary,
45
45
  cyan: n.accent,
46
- white: l ? "#d3d7cf" : "#eeeeec",
46
+ white: f ? "#d3d7cf" : "#eeeeec",
47
47
  brightBlack: "#555753",
48
48
  brightRed: n.error,
49
49
  brightGreen: n.success,
@@ -51,7 +51,7 @@ const L = H(({
51
51
  brightBlue: n.info,
52
52
  brightMagenta: n.secondary,
53
53
  brightCyan: n.accent,
54
- brightWhite: l ? "#eeeeec" : "#ffffff"
54
+ brightWhite: f ? "#eeeeec" : "#ffffff"
55
55
  });
56
56
  O(D, () => ({
57
57
  terminal: o.current,
@@ -59,29 +59,29 @@ const L = H(({
59
59
  writeln: (r) => o.current?.writeln(r),
60
60
  clear: () => o.current?.clear(),
61
61
  focus: () => o.current?.focus(),
62
- fit: () => m.current?.fit(),
63
- history: f.current.history,
62
+ fit: () => h.current?.fit(),
63
+ history: d.current.history,
64
64
  clearHistory: () => {
65
- f.current.history = [];
65
+ d.current.history = [];
66
66
  }
67
67
  }), []);
68
- const u = () => {
68
+ const l = () => {
69
69
  const r = o.current;
70
70
  if (!r) return;
71
- const t = f.current;
71
+ const t = d.current;
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
  }, S = (r) => {
76
76
  const t = o.current;
77
77
  if (!t) return;
78
- const e = f.current;
78
+ const e = d.current;
79
79
  if (r === "\x1B[A") {
80
- e.history.length > 0 && e.historyIndex < e.history.length - 1 && (e.historyIndex === -1 && (e.savedBuffer = e.buffer), e.historyIndex++, e.buffer = e.history[e.history.length - 1 - e.historyIndex], e.cursor = e.buffer.length, u());
80
+ e.history.length > 0 && e.historyIndex < e.history.length - 1 && (e.historyIndex === -1 && (e.savedBuffer = e.buffer), e.historyIndex++, e.buffer = e.history[e.history.length - 1 - e.historyIndex], e.cursor = e.buffer.length, l());
81
81
  return;
82
82
  }
83
83
  if (r === "\x1B[B") {
84
- e.historyIndex > -1 && (e.historyIndex--, e.buffer = e.historyIndex === -1 ? e.savedBuffer : e.history[e.history.length - 1 - e.historyIndex], e.cursor = e.buffer.length, u());
84
+ e.historyIndex > -1 && (e.historyIndex--, e.buffer = e.historyIndex === -1 ? e.savedBuffer : e.history[e.history.length - 1 - e.historyIndex], e.cursor = e.buffer.length, l());
85
85
  return;
86
86
  }
87
87
  if (r === "\x1B[C") {
@@ -101,44 +101,46 @@ const L = H(({
101
101
  return;
102
102
  }
103
103
  if (r === "\x1B[3~") {
104
- e.cursor < e.buffer.length && (e.buffer = e.buffer.slice(0, e.cursor) + e.buffer.slice(e.cursor + 1), u());
104
+ e.cursor < e.buffer.length && (e.buffer = e.buffer.slice(0, e.cursor) + e.buffer.slice(e.cursor + 1), l());
105
105
  return;
106
106
  }
107
107
  if (!r.startsWith("\x1B"))
108
108
  if (r === "\r") {
109
109
  t.writeln("");
110
110
  const i = e.buffer;
111
- i.trim() && e.history.push(i), e.buffer = "", e.cursor = 0, e.historyIndex = -1, e.savedBuffer = "", I?.(i), t.write(c);
112
- } else r === "" || r === "\b" ? e.cursor > 0 && (e.buffer = e.buffer.slice(0, e.cursor - 1) + e.buffer.slice(e.cursor), e.cursor--, u()) : 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, u());
111
+ i.trim() && e.history.push(i), e.buffer = "", e.cursor = 0, e.historyIndex = -1, e.savedBuffer = "";
112
+ const s = I?.(i);
113
+ s && typeof s.then == "function" ? s.then(() => t.write(c)) : t.write(c);
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());
113
115
  };
114
116
  return B(() => {
115
- if (!a.current) return;
117
+ if (!m.current) return;
116
118
  E();
117
- const r = a.current;
118
- let t = null, e = null, i = null, h = !1, b = !1;
119
+ const r = m.current;
120
+ let t = null, e = null, i = null, s = !1, b = !1;
119
121
  const v = () => {
120
- if (h || b || !r) return;
121
- const d = r.getBoundingClientRect();
122
- d.width === 0 || d.height === 0 || (h = !0, t = new j({
122
+ if (s || b || !r) return;
123
+ const x = r.getBoundingClientRect();
124
+ x.width === 0 || x.height === 0 || (s = !0, t = new j({
123
125
  theme: w(),
124
126
  cursorBlink: !0,
125
127
  convertEol: A,
126
128
  fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace',
127
129
  fontSize: 14,
128
130
  ...F
129
- }), e = new X(), t.loadAddon(e), t.open(r), e.fit(), o.current = t, m.current = e, y ? t.onData(S) : s && t.onData(s), z?.(t), y && t.write(c));
131
+ }), e = new X(), t.loadAddon(e), t.open(r), e.fit(), o.current = t, h.current = e, y ? t.onData(S) : u && t.onData(u), z?.(t), y && t.write(c));
130
132
  };
131
- return i = new ResizeObserver((d) => {
132
- !d[0] || b || (h ? e && e.fit() : v());
133
+ return i = new ResizeObserver((x) => {
134
+ !x[0] || b || (s ? e && e.fit() : v());
133
135
  }), i.observe(r), requestAnimationFrame(v), () => {
134
- b = !0, i?.disconnect(), t?.dispose(), o.current = null, m.current = null;
136
+ b = !0, i?.disconnect(), t?.dispose(), o.current = null, h.current = null;
135
137
  };
136
138
  }, []), B(() => {
137
139
  o.current && (o.current.options.theme = w());
138
- }, [l, n]), /* @__PURE__ */ M(
140
+ }, [f, n]), /* @__PURE__ */ M(
139
141
  "div",
140
142
  {
141
- ref: a,
143
+ ref: m,
142
144
  className: C,
143
145
  style: { width: "100%", height: "100%", ...R },
144
146
  "data-testid": T
@@ -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 */\n onLine?: (line: string) => 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 onLine?.(line)\n term.write(prompt)\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","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.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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aster-ui/prefixed",
3
- "version": "0.12.84",
3
+ "version": "0.12.86",
4
4
  "description": "React UI component library with DaisyUI (prefixed classes)",
5
5
  "homepage": "https://asterui.com",
6
6
  "repository": {