@mks2508/mks-ui 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/react-ui/blocks/Terminal/ChromeShell/ChromeShell.styles.d.ts +55 -0
  2. package/dist/react-ui/blocks/Terminal/ChromeShell/ChromeShell.styles.d.ts.map +1 -0
  3. package/dist/react-ui/blocks/Terminal/ChromeShell/ChromeShell.styles.js +62 -0
  4. package/dist/react-ui/blocks/Terminal/ChromeShell/ChromeShell.types.d.ts +92 -0
  5. package/dist/react-ui/blocks/Terminal/ChromeShell/ChromeShell.types.d.ts.map +1 -0
  6. package/dist/react-ui/blocks/Terminal/ChromeShell/index.d.ts +71 -0
  7. package/dist/react-ui/blocks/Terminal/ChromeShell/index.d.ts.map +1 -0
  8. package/dist/react-ui/blocks/Terminal/ChromeShell/index.js +114 -0
  9. package/dist/react-ui/blocks/Terminal/chrome.d.ts +1 -0
  10. package/dist/react-ui/blocks/Terminal/chrome.d.ts.map +1 -1
  11. package/dist/react-ui/blocks/Terminal/chrome.js +3 -1
  12. package/dist/react-ui/blocks/Terminal/index.d.ts +1 -0
  13. package/dist/react-ui/blocks/Terminal/index.d.ts.map +1 -1
  14. package/dist/react-ui/blocks/Terminal/index.js +3 -1
  15. package/dist/react-ui/blocks/Terminal/panel/TerminalInteractivePanelWterm.d.ts.map +1 -1
  16. package/dist/react-ui/blocks/Terminal/panel/TerminalInteractivePanelWterm.js +20 -9
  17. package/dist/react-ui/blocks/Terminal/panel/TerminalInteractivePanelXterm.d.ts.map +1 -1
  18. package/dist/react-ui/blocks/Terminal/panel/TerminalInteractivePanelXterm.js +19 -6
  19. package/dist/react-ui/blocks/Terminal/panel/TerminalLogsPanel/index.d.ts.map +1 -1
  20. package/dist/react-ui/blocks/Terminal/panel/TerminalLogsPanel/index.js +31 -9
  21. package/dist/react-ui/blocks/Terminal/panel/TerminalPanelChrome.d.ts +31 -2
  22. package/dist/react-ui/blocks/Terminal/panel/TerminalPanelChrome.d.ts.map +1 -1
  23. package/dist/react-ui/blocks/Terminal/panel/TerminalPanelChrome.js +41 -5
  24. package/dist/react-ui/blocks/Terminal/panel/TerminalPanelChrome.types.d.ts +22 -0
  25. package/dist/react-ui/blocks/Terminal/panel/TerminalPanelChrome.types.d.ts.map +1 -1
  26. package/dist/react-ui/blocks/Terminal/panel/TerminalSettingsPopover/index.d.ts +8 -0
  27. package/dist/react-ui/blocks/Terminal/panel/TerminalSettingsPopover/index.d.ts.map +1 -1
  28. package/dist/react-ui/blocks/Terminal/panel/TerminalSettingsPopover/index.js +54 -12
  29. package/dist/react-ui/blocks/Terminal/renderers/ResttyRenderer.d.ts +52 -0
  30. package/dist/react-ui/blocks/Terminal/renderers/ResttyRenderer.d.ts.map +1 -0
  31. package/dist/react-ui/blocks/Terminal/renderers/ResttyRenderer.js +160 -0
  32. package/dist/react-ui/blocks/Terminal/renderers/WtermRenderer.d.ts +46 -0
  33. package/dist/react-ui/blocks/Terminal/renderers/WtermRenderer.d.ts.map +1 -0
  34. package/dist/react-ui/blocks/Terminal/renderers/WtermRenderer.js +195 -0
  35. package/dist/react-ui/blocks/Terminal/renderers/XTermRenderer.d.ts +39 -0
  36. package/dist/react-ui/blocks/Terminal/renderers/XTermRenderer.d.ts.map +1 -0
  37. package/dist/react-ui/blocks/Terminal/renderers/XTermRenderer.js +251 -0
  38. package/dist/react-ui/blocks/Terminal/renderers/index.d.ts +20 -0
  39. package/dist/react-ui/blocks/Terminal/renderers/index.d.ts.map +1 -0
  40. package/dist/react-ui/blocks/Terminal/renderers/types.d.ts +47 -0
  41. package/dist/react-ui/blocks/Terminal/renderers/types.d.ts.map +1 -0
  42. package/dist/react-ui/blocks/Terminal/restty.d.ts +2 -0
  43. package/dist/react-ui/blocks/Terminal/restty.d.ts.map +1 -1
  44. package/dist/react-ui/blocks/Terminal/restty.js +2 -1
  45. package/dist/react-ui/blocks/Terminal/wterm.d.ts +2 -0
  46. package/dist/react-ui/blocks/Terminal/wterm.d.ts.map +1 -1
  47. package/dist/react-ui/blocks/Terminal/wterm.js +2 -1
  48. package/dist/react-ui/blocks/Terminal/xterm.d.ts +2 -0
  49. package/dist/react-ui/blocks/Terminal/xterm.d.ts.map +1 -1
  50. package/dist/react-ui/blocks/Terminal/xterm.js +2 -1
  51. package/dist/react-ui/index.d.ts +2 -2
  52. package/dist/react-ui/index.d.ts.map +1 -1
  53. package/dist/react-ui/index.js +5 -3
  54. package/dist/react-ui/ui/MiddleTruncate/MiddleTruncate.styles.d.ts +30 -0
  55. package/dist/react-ui/ui/MiddleTruncate/MiddleTruncate.styles.d.ts.map +1 -0
  56. package/dist/react-ui/ui/MiddleTruncate/MiddleTruncate.styles.js +31 -0
  57. package/dist/react-ui/ui/MiddleTruncate/MiddleTruncate.types.d.ts +66 -0
  58. package/dist/react-ui/ui/MiddleTruncate/MiddleTruncate.types.d.ts.map +1 -0
  59. package/dist/react-ui/ui/MiddleTruncate/index.d.ts +79 -0
  60. package/dist/react-ui/ui/MiddleTruncate/index.d.ts.map +1 -0
  61. package/dist/react-ui/ui/MiddleTruncate/index.js +164 -0
  62. package/dist/react-ui/ui/MiddleTruncatePath/MiddleTruncatePath.styles.js +1 -1
  63. package/dist/react-ui/ui/MiddleTruncatePath/index.d.ts +7 -1
  64. package/dist/react-ui/ui/MiddleTruncatePath/index.d.ts.map +1 -1
  65. package/dist/react-ui/ui/MiddleTruncatePath/index.js +21 -17
  66. package/dist/react-ui/ui/index.d.ts +1 -0
  67. package/dist/react-ui/ui/index.d.ts.map +1 -1
  68. package/dist/react-ui/ui/index.js +3 -1
  69. package/package.json +1 -1
@@ -0,0 +1,160 @@
1
+ 'use client';
2
+
3
+ import { cn } from "../../../lib/utils.js";
4
+ import { useTerminalSettings } from "../hooks/useTerminalSettings.js";
5
+ import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from "react";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+
8
+ //#region src/react-ui/blocks/Terminal/renderers/ResttyRenderer.tsx
9
+ /**
10
+ * ResttyRenderer Component.
11
+ *
12
+ * Chrome-less restty renderer for maximum composability.
13
+ * Wraps @mks2508/restty ResttyTerminal component without any chrome,
14
+ * toolbar, or status bar. Use this when you want to build your own
15
+ * terminal shell or embed in custom layouts.
16
+ *
17
+ * Lazy-loads @mks2508/restty on first render. Returns null if
18
+ * the peer dependency is not installed.
19
+ *
20
+ * @module components/devenv/terminal/renderers/restty
21
+ */
22
+ /**
23
+ * Terminal container styles for restty renderer.
24
+ */
25
+ const terminalContainerStyles = "relative bg-[hsl(var(--terminal-bg,var(--background)))] min-h-[400px] border-x border-border/10";
26
+ /** Lazy-loaded ResttyTerminal component. */
27
+ let ResttyTerminalComponent = null;
28
+ /** Whether we've attempted to load restty. */
29
+ let resttyLoadAttempted = false;
30
+ /**
31
+ * Lazy-load @mks2508/restty ResttyTerminal component.
32
+ *
33
+ * Returns null if @mks2508/restty is not installed.
34
+ */
35
+ async function loadResttyTerminal() {
36
+ if (ResttyTerminalComponent !== null || resttyLoadAttempted) return ResttyTerminalComponent;
37
+ resttyLoadAttempted = true;
38
+ try {
39
+ ResttyTerminalComponent = (await import("@mks2508/restty")).ResttyTerminal;
40
+ return ResttyTerminalComponent;
41
+ } catch {
42
+ ResttyTerminalComponent = null;
43
+ return null;
44
+ }
45
+ }
46
+ /**
47
+ * ResttyRenderer Component.
48
+ *
49
+ * Chrome-less restty renderer. Provides:
50
+ * - @mks2508/restty ResttyTerminal with WebGPU/WebGL2 rendering
51
+ * - Auto-resize on container resize
52
+ * - Font size from useTerminalSettings context (override via props)
53
+ * - Ref forwarding with focus, write, clear, resize methods
54
+ * - Lazy-loading with graceful fallback if @mks2508/restty is missing
55
+ *
56
+ * restty features:
57
+ * - GPU-accelerated rendering (WebGPU/WebGL2)
58
+ * - libghostty-vt WASM core (full VT100/VT220/xterm parser)
59
+ * - Built-in theme catalog (Dracula, Synthwave, etc.)
60
+ * - Multi-pane splits
61
+ * - Scrollback history
62
+ *
63
+ * @example
64
+ * ```tsx
65
+ * const termRef = useRef<ITerminalRendererRef>(null);
66
+ *
67
+ * <ResttyRenderer
68
+ * ref={termRef}
69
+ * sessionId="my-session"
70
+ * fontSize={16}
71
+ * resttyThemeName="Dracula"
72
+ * onReady={() => console.log('Terminal ready')}
73
+ * className="h-[600px]"
74
+ * />
75
+ * ```
76
+ */
77
+ const ResttyRenderer = forwardRef(({ sessionId, fontSize: fontSizeProp, resttyThemeName: resttyThemeNameProp, gpuRenderer = "auto", onReady, className }, ref) => {
78
+ const containerRef = useRef(null);
79
+ const terminalReadyRef = useRef(false);
80
+ const { settings, setFontSize } = useTerminalSettings();
81
+ /**
82
+ * Font size reads from global terminal settings store, with prop
83
+ * override taking precedence. This matches the v0.11.0 pattern
84
+ * where all consumers sync to the single source of truth.
85
+ */
86
+ const fontSize = fontSizeProp ?? settings.fontSize;
87
+ const resttyThemeName = resttyThemeNameProp ?? settings.resttyThemeName;
88
+ const [resttyAvailable, setResttyAvailable] = useState(null);
89
+ const [ResttyTerminal, setResttyTerminal] = useState(null);
90
+ /**
91
+ * Lazy-load restty on mount.
92
+ */
93
+ useEffect(() => {
94
+ loadResttyTerminal().then((term) => {
95
+ if (term) {
96
+ setResttyTerminal(() => term);
97
+ setResttyAvailable(true);
98
+ } else setResttyAvailable(false);
99
+ });
100
+ }, []);
101
+ /**
102
+ * Handle terminal ready callback.
103
+ */
104
+ const handleResttyReady = useCallback(() => {
105
+ terminalReadyRef.current = true;
106
+ onReady?.();
107
+ }, [onReady]);
108
+ /**
109
+ * Font size update propagates to global settings if not overridden.
110
+ */
111
+ useEffect(() => {
112
+ if (!fontSizeProp) setFontSize(fontSize);
113
+ }, [
114
+ fontSize,
115
+ fontSizeProp,
116
+ setFontSize
117
+ ]);
118
+ /**
119
+ * Expose imperative methods to parent.
120
+ *
121
+ * Note: write() is a no-op for restty since it manages its own PTY.
122
+ * The renderer focuses on display only.
123
+ */
124
+ useImperativeHandle(ref, () => ({
125
+ focus: () => {},
126
+ write: (_data) => {},
127
+ clear: () => {},
128
+ resize: (_cols, _rows) => {}
129
+ }));
130
+ /**
131
+ * Render fallbacks for missing/unavailable restty.
132
+ */
133
+ if (resttyAvailable === null) return /* @__PURE__ */ jsx("div", {
134
+ className: "flex items-center justify-center h-full text-muted-foreground text-sm min-h-[400px]",
135
+ children: "Loading restty..."
136
+ });
137
+ if (resttyAvailable === false) return /* @__PURE__ */ jsxs("div", {
138
+ className: "flex flex-col items-center justify-center h-full text-muted-foreground text-sm gap-2 min-h-[400px]",
139
+ children: [/* @__PURE__ */ jsx("span", { children: "restty is not installed" }), /* @__PURE__ */ jsx("code", {
140
+ className: "text-xs bg-muted px-2 py-1 rounded",
141
+ children: "bun add @mks2508/restty"
142
+ })]
143
+ });
144
+ return /* @__PURE__ */ jsx("div", {
145
+ ref: containerRef,
146
+ className: cn(terminalContainerStyles, className),
147
+ "data-session-id": sessionId,
148
+ children: ResttyTerminal && /* @__PURE__ */ jsx(ResttyTerminal, {
149
+ ref: void 0,
150
+ resttyThemeName,
151
+ gpuRenderer,
152
+ className: "w-full h-full min-h-[400px]",
153
+ onReady: handleResttyReady
154
+ })
155
+ });
156
+ });
157
+ ResttyRenderer.displayName = "ResttyRenderer";
158
+
159
+ //#endregion
160
+ export { ResttyRenderer };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * WtermRenderer Component.
3
+ *
4
+ * Chrome-less wterm renderer for maximum composability.
5
+ * Wraps @wterm/react Terminal component without any chrome,
6
+ * toolbar, or status bar. Use this when you want to build
7
+ * your own terminal shell or embed in custom layouts.
8
+ *
9
+ * Lazy-loads @wterm/react on first render. Returns null if
10
+ * the peer dependency is not installed.
11
+ *
12
+ * @module components/devenv/terminal/renderers/wterm
13
+ */
14
+ import type { ITerminalRendererProps, ITerminalRendererRef } from './types';
15
+ /**
16
+ * WtermRenderer Component.
17
+ *
18
+ * Chrome-less wterm renderer. Provides:
19
+ * - @wterm/react Terminal with Zig + WASM VT100/VT220/xterm parser
20
+ * - Auto-resize on container resize
21
+ * - Font size from useTerminalSettings context (override via props)
22
+ * - Ref forwarding with focus, write, clear, resize methods
23
+ * - Lazy-loading with graceful fallback if @wterm/react is missing
24
+ *
25
+ * wterm features:
26
+ * - ~12KB WASM binary (embedded base64 fallback)
27
+ * - DOM rendering with dirty-row tracking via requestAnimationFrame
28
+ * - Full 24-bit color (SGR) support
29
+ * - Alternate screen buffer (vim, less, htop work)
30
+ * - Scrollback history ring buffer
31
+ *
32
+ * @example
33
+ * ```tsx
34
+ * const termRef = useRef<ITerminalRendererRef>(null);
35
+ *
36
+ * <WtermRenderer
37
+ * ref={termRef}
38
+ * sessionId="my-session"
39
+ * fontSize={16}
40
+ * onReady={() => console.log('Terminal ready')}
41
+ * className="h-[600px]"
42
+ * />
43
+ * ```
44
+ */
45
+ export declare const WtermRenderer: import("react").ForwardRefExoticComponent<ITerminalRendererProps & import("react").RefAttributes<ITerminalRendererRef>>;
46
+ //# sourceMappingURL=WtermRenderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WtermRenderer.d.ts","sourceRoot":"","sources":["../../../../../src/react-ui/blocks/Terminal/renderers/WtermRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAaH,OAAO,KAAK,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAyD5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,aAAa,yHAoJzB,CAAC"}
@@ -0,0 +1,195 @@
1
+ 'use client';
2
+
3
+ import { cn } from "../../../lib/utils.js";
4
+ import { useTerminalSettings } from "../hooks/useTerminalSettings.js";
5
+ import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from "react";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+
8
+ //#region src/react-ui/blocks/Terminal/renderers/WtermRenderer.tsx
9
+ /**
10
+ * WtermRenderer Component.
11
+ *
12
+ * Chrome-less wterm renderer for maximum composability.
13
+ * Wraps @wterm/react Terminal component without any chrome,
14
+ * toolbar, or status bar. Use this when you want to build
15
+ * your own terminal shell or embed in custom layouts.
16
+ *
17
+ * Lazy-loads @wterm/react on first render. Returns null if
18
+ * the peer dependency is not installed.
19
+ *
20
+ * @module components/devenv/terminal/renderers/wterm
21
+ */
22
+ /**
23
+ * Terminal container styles for wterm renderer.
24
+ */
25
+ const terminalContainerStyles = "relative bg-[hsl(var(--terminal-bg,var(--background)))] min-h-[400px] border-x border-border/10 terminal-scroll wterm-wrapper";
26
+ /** Lazy-loaded wterm Terminal component. */
27
+ let WtermTerminalComponent = null;
28
+ /** Whether we've attempted to load wterm. */
29
+ let wtermLoadAttempted = false;
30
+ /**
31
+ * Lazy-load @wterm/react Terminal component.
32
+ *
33
+ * Returns null if @wterm/react is not installed.
34
+ */
35
+ async function loadWtermTerminal() {
36
+ if (WtermTerminalComponent !== null || wtermLoadAttempted) return WtermTerminalComponent;
37
+ wtermLoadAttempted = true;
38
+ try {
39
+ WtermTerminalComponent = (await import("@wterm/react")).Terminal;
40
+ await import("@wterm/react/css");
41
+ return WtermTerminalComponent;
42
+ } catch {
43
+ WtermTerminalComponent = null;
44
+ return null;
45
+ }
46
+ }
47
+ /**
48
+ * WtermRenderer Component.
49
+ *
50
+ * Chrome-less wterm renderer. Provides:
51
+ * - @wterm/react Terminal with Zig + WASM VT100/VT220/xterm parser
52
+ * - Auto-resize on container resize
53
+ * - Font size from useTerminalSettings context (override via props)
54
+ * - Ref forwarding with focus, write, clear, resize methods
55
+ * - Lazy-loading with graceful fallback if @wterm/react is missing
56
+ *
57
+ * wterm features:
58
+ * - ~12KB WASM binary (embedded base64 fallback)
59
+ * - DOM rendering with dirty-row tracking via requestAnimationFrame
60
+ * - Full 24-bit color (SGR) support
61
+ * - Alternate screen buffer (vim, less, htop work)
62
+ * - Scrollback history ring buffer
63
+ *
64
+ * @example
65
+ * ```tsx
66
+ * const termRef = useRef<ITerminalRendererRef>(null);
67
+ *
68
+ * <WtermRenderer
69
+ * ref={termRef}
70
+ * sessionId="my-session"
71
+ * fontSize={16}
72
+ * onReady={() => console.log('Terminal ready')}
73
+ * className="h-[600px]"
74
+ * />
75
+ * ```
76
+ */
77
+ const WtermRenderer = forwardRef(({ sessionId, fontSize: fontSizeProp, onReady, className }, ref) => {
78
+ /** wterm terminal instance handle */
79
+ const terminalRef = useRef(null);
80
+ const containerRef = useRef(null);
81
+ const terminalReadyRef = useRef(false);
82
+ const { settings, setFontSize } = useTerminalSettings();
83
+ /**
84
+ * Font size reads from global terminal settings store, with prop
85
+ * override taking precedence. This matches the v0.11.0 pattern
86
+ * where all consumers sync to the single source of truth.
87
+ */
88
+ const fontSize = fontSizeProp ?? settings.fontSize;
89
+ const [dimensions, setDimensions] = useState({
90
+ cols: 80,
91
+ rows: 24
92
+ });
93
+ const [wtermAvailable, setWtermAvailable] = useState(null);
94
+ const [Terminal, setTerminal] = useState(null);
95
+ /**
96
+ * Lazy-load wterm on mount.
97
+ */
98
+ useEffect(() => {
99
+ loadWtermTerminal().then((term) => {
100
+ if (term) {
101
+ setTerminal(() => term);
102
+ setWtermAvailable(true);
103
+ } else setWtermAvailable(false);
104
+ });
105
+ }, []);
106
+ /**
107
+ * Handle terminal data input (no-op for chrome-less renderer).
108
+ * Consumer should handle I/O via ref methods.
109
+ */
110
+ const handleData = useCallback((_data) => {}, []);
111
+ /**
112
+ * Handle terminal resize.
113
+ */
114
+ const handleResize = useCallback((cols, rows) => {
115
+ setDimensions({
116
+ cols,
117
+ rows
118
+ });
119
+ }, []);
120
+ /**
121
+ * Handle terminal ready callback.
122
+ */
123
+ const handleWtermReady = useCallback((core) => {
124
+ terminalReadyRef.current = true;
125
+ onReady?.();
126
+ }, [onReady]);
127
+ /**
128
+ * Handle font size changes via CSS custom property.
129
+ */
130
+ useEffect(() => {
131
+ if (containerRef.current) containerRef.current.style.setProperty("--wterm-font-size", `${fontSize}px`);
132
+ }, [fontSize]);
133
+ /**
134
+ * Font size update propagates to global settings if not overridden.
135
+ */
136
+ useEffect(() => {
137
+ if (!fontSizeProp) setFontSize(fontSize);
138
+ }, [
139
+ fontSize,
140
+ fontSizeProp,
141
+ setFontSize
142
+ ]);
143
+ /**
144
+ * Expose imperative methods to parent.
145
+ */
146
+ useImperativeHandle(ref, () => ({
147
+ focus: () => {
148
+ terminalRef.current?.focus();
149
+ },
150
+ write: (data) => {
151
+ terminalRef.current?.write(data);
152
+ },
153
+ clear: () => {
154
+ terminalRef.current?.write("\x1B[2J\x1B[H");
155
+ },
156
+ resize: (cols, rows) => {
157
+ terminalRef.current?.resize(cols, rows);
158
+ }
159
+ }));
160
+ /**
161
+ * Render fallbacks for missing/unavailable wterm.
162
+ */
163
+ if (wtermAvailable === null) return /* @__PURE__ */ jsx("div", {
164
+ className: "flex items-center justify-center h-full text-muted-foreground text-sm min-h-[400px]",
165
+ children: "Loading wterm..."
166
+ });
167
+ if (wtermAvailable === false) return /* @__PURE__ */ jsxs("div", {
168
+ className: "flex flex-col items-center justify-center h-full text-muted-foreground text-sm gap-2 min-h-[400px]",
169
+ children: [/* @__PURE__ */ jsx("span", { children: "wterm is not installed" }), /* @__PURE__ */ jsx("code", {
170
+ className: "text-xs bg-muted px-2 py-1 rounded",
171
+ children: "bun add @wterm/react"
172
+ })]
173
+ });
174
+ return /* @__PURE__ */ jsx("div", {
175
+ ref: containerRef,
176
+ className: cn(terminalContainerStyles, className),
177
+ style: { "--wterm-font-size": `${fontSize}px` },
178
+ "data-session-id": sessionId,
179
+ children: Terminal && /* @__PURE__ */ jsx(Terminal, {
180
+ ref: terminalRef,
181
+ cols: dimensions.cols,
182
+ rows: dimensions.rows,
183
+ autoResize: false,
184
+ cursorBlink: true,
185
+ onData: handleData,
186
+ onResize: handleResize,
187
+ onReady: handleWtermReady,
188
+ className: "w-full h-full"
189
+ })
190
+ });
191
+ });
192
+ WtermRenderer.displayName = "WtermRenderer";
193
+
194
+ //#endregion
195
+ export { WtermRenderer };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * XTermRenderer Component.
3
+ *
4
+ * Chrome-less xterm.js renderer for maximum composability.
5
+ * Wraps xterm.js Terminal + FitAddon without any chrome,
6
+ * toolbar, or status bar. Use this when you want to build
7
+ * your own terminal shell or embed in custom layouts.
8
+ *
9
+ * Lazy-loads @xterm/xterm and addon family on first render.
10
+ * Returns null if the peer dependency is not installed.
11
+ *
12
+ * @module components/devenv/terminal/renderers/xterm
13
+ */
14
+ import type { ITerminalRendererProps, ITerminalRendererRef } from './types';
15
+ /**
16
+ * XTermRenderer Component.
17
+ *
18
+ * Chrome-less xterm.js renderer. Provides:
19
+ * - xterm.js Terminal with FitAddon, WebLinksAddon, SearchAddon
20
+ * - Auto-resize on container resize
21
+ * - Font size from useTerminalSettings context (override via props)
22
+ * - Ref forwarding with focus, write, clear, resize methods
23
+ * - Lazy-loading with graceful fallback if @xterm/xterm is missing
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * const termRef = useRef<ITerminalRendererRef>(null);
28
+ *
29
+ * <XTermRenderer
30
+ * ref={termRef}
31
+ * sessionId="my-session"
32
+ * fontSize={16}
33
+ * onReady={() => console.log('Terminal ready')}
34
+ * className="h-[600px]"
35
+ * />
36
+ * ```
37
+ */
38
+ export declare const XTermRenderer: import("react").ForwardRefExoticComponent<ITerminalRendererProps & import("react").RefAttributes<ITerminalRendererRef>>;
39
+ //# sourceMappingURL=XTermRenderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"XTermRenderer.d.ts","sourceRoot":"","sources":["../../../../../src/react-ui/blocks/Terminal/renderers/XTermRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAcH,OAAO,KAAK,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAwF5E;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,aAAa,yHA6LzB,CAAC"}
@@ -0,0 +1,251 @@
1
+ 'use client';
2
+
3
+ import { cn } from "../../../lib/utils.js";
4
+ import { useTerminalSettings } from "../hooks/useTerminalSettings.js";
5
+ import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from "react";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+
8
+ //#region src/react-ui/blocks/Terminal/renderers/XTermRenderer.tsx
9
+ /**
10
+ * XTermRenderer Component.
11
+ *
12
+ * Chrome-less xterm.js renderer for maximum composability.
13
+ * Wraps xterm.js Terminal + FitAddon without any chrome,
14
+ * toolbar, or status bar. Use this when you want to build
15
+ * your own terminal shell or embed in custom layouts.
16
+ *
17
+ * Lazy-loads @xterm/xterm and addon family on first render.
18
+ * Returns null if the peer dependency is not installed.
19
+ *
20
+ * @module components/devenv/terminal/renderers/xterm
21
+ */
22
+ /**
23
+ * Terminal container styles for xterm.js renderer.
24
+ */
25
+ const terminalContainerStyles = "relative bg-[hsl(var(--terminal-bg,var(--background)))] min-h-[400px] border-x border-border/10 terminal-scroll";
26
+ /** Cached lazy-loaded xterm.js modules. */
27
+ let xtermModules = null;
28
+ /** Whether we've attempted to load xterm.js modules. */
29
+ let xtermLoadAttempted = false;
30
+ /**
31
+ * Lazy-load xterm.js and addon family.
32
+ *
33
+ * Returns null if @xterm/xterm is not installed.
34
+ */
35
+ async function loadXtermModules() {
36
+ if (xtermModules !== null || xtermLoadAttempted) return xtermModules;
37
+ xtermLoadAttempted = true;
38
+ try {
39
+ const [xterm, fitAddon, webLinksAddon, searchAddon] = await Promise.all([
40
+ import("@xterm/xterm"),
41
+ import("@xterm/addon-fit"),
42
+ import("@xterm/addon-web-links"),
43
+ import("@xterm/addon-search")
44
+ ]);
45
+ await import("@xterm/xterm/css/xterm.css");
46
+ xtermModules = {
47
+ Terminal: xterm.Terminal,
48
+ FitAddon: fitAddon.FitAddon,
49
+ WebLinksAddon: webLinksAddon.WebLinksAddon,
50
+ SearchAddon: searchAddon.SearchAddon
51
+ };
52
+ return xtermModules;
53
+ } catch {
54
+ xtermModules = null;
55
+ return null;
56
+ }
57
+ }
58
+ /**
59
+ * Get xterm.js theme from current theme context.
60
+ */
61
+ function getXtermTheme() {
62
+ return {
63
+ background: "#241B2F",
64
+ foreground: "#e8e8ec",
65
+ cursor: "#D40C67",
66
+ black: "#000000",
67
+ red: "#cd3131",
68
+ green: "#0dbc79",
69
+ yellow: "#e5e510",
70
+ blue: "#2472c8",
71
+ magenta: "#bc3fbc",
72
+ cyan: "#11a8cd",
73
+ white: "#e5e5e5",
74
+ brightBlack: "#666666",
75
+ brightRed: "#f14c4c",
76
+ brightGreen: "#23d18b",
77
+ brightYellow: "#f5f543",
78
+ brightBlue: "#3b8eea",
79
+ brightMagenta: "#d670d6",
80
+ brightCyan: "#29b8db",
81
+ brightWhite: "#ffffff"
82
+ };
83
+ }
84
+ /**
85
+ * XTermRenderer Component.
86
+ *
87
+ * Chrome-less xterm.js renderer. Provides:
88
+ * - xterm.js Terminal with FitAddon, WebLinksAddon, SearchAddon
89
+ * - Auto-resize on container resize
90
+ * - Font size from useTerminalSettings context (override via props)
91
+ * - Ref forwarding with focus, write, clear, resize methods
92
+ * - Lazy-loading with graceful fallback if @xterm/xterm is missing
93
+ *
94
+ * @example
95
+ * ```tsx
96
+ * const termRef = useRef<ITerminalRendererRef>(null);
97
+ *
98
+ * <XTermRenderer
99
+ * ref={termRef}
100
+ * sessionId="my-session"
101
+ * fontSize={16}
102
+ * onReady={() => console.log('Terminal ready')}
103
+ * className="h-[600px]"
104
+ * />
105
+ * ```
106
+ */
107
+ const XTermRenderer = forwardRef(({ sessionId, fontSize: fontSizeProp, fontFamily: fontFamilyProp, onReady, className }, ref) => {
108
+ const terminalRef = useRef(null);
109
+ const fitAddonRef = useRef(null);
110
+ const containerRef = useRef(null);
111
+ const terminalReadyRef = useRef(false);
112
+ const { settings, setFontSize } = useTerminalSettings();
113
+ /**
114
+ * Font size reads from global terminal settings store, with prop
115
+ * override taking precedence. This matches the v0.11.0 pattern
116
+ * where all consumers sync to the single source of truth.
117
+ */
118
+ const fontSize = fontSizeProp ?? settings.fontSize;
119
+ const fontFamily = fontFamilyProp ?? "'JetBrains Mono', 'Fira Code', 'SF Mono', 'Cascadia Code', monospace";
120
+ const [xtermAvailable, setXtermAvailable] = useState(null);
121
+ const [XTerm, setXTerm] = useState(null);
122
+ /**
123
+ * Lazy-load xterm.js on mount.
124
+ */
125
+ useEffect(() => {
126
+ loadXtermModules().then((mods) => {
127
+ if (mods) {
128
+ setXTerm(() => mods.Terminal);
129
+ setXtermAvailable(true);
130
+ } else setXtermAvailable(false);
131
+ });
132
+ }, []);
133
+ /**
134
+ * Initialize xterm.js instance.
135
+ */
136
+ useEffect(() => {
137
+ if (!XTerm || !containerRef.current || !xtermAvailable) return;
138
+ if (!xtermModules) return;
139
+ const terminal = new XTerm({
140
+ cursorBlink: true,
141
+ cursorStyle: "bar",
142
+ scrollback: 1e4,
143
+ fontSize,
144
+ fontFamily,
145
+ theme: getXtermTheme(),
146
+ allowProposedApi: true,
147
+ disableStdin: false
148
+ });
149
+ const fitAddon = new xtermModules.FitAddon();
150
+ const webLinksAddon = new xtermModules.WebLinksAddon();
151
+ const searchAddon = new xtermModules.SearchAddon();
152
+ terminal.loadAddon(fitAddon);
153
+ terminal.loadAddon(webLinksAddon);
154
+ terminal.loadAddon(searchAddon);
155
+ terminal.open(containerRef.current);
156
+ const rafId = requestAnimationFrame(() => {
157
+ requestAnimationFrame(() => {
158
+ try {
159
+ fitAddon.fit();
160
+ } catch {}
161
+ });
162
+ });
163
+ terminalRef.current = terminal;
164
+ fitAddonRef.current = fitAddon;
165
+ terminalReadyRef.current = true;
166
+ onReady?.();
167
+ return () => {
168
+ cancelAnimationFrame(rafId);
169
+ terminalReadyRef.current = false;
170
+ terminal.dispose();
171
+ terminalRef.current = null;
172
+ fitAddonRef.current = null;
173
+ };
174
+ }, [XTerm, xtermAvailable]);
175
+ /**
176
+ * Update font size on existing terminal.
177
+ */
178
+ useEffect(() => {
179
+ if (!terminalRef.current || !terminalReadyRef.current) return;
180
+ terminalRef.current.options.fontSize = fontSize;
181
+ requestAnimationFrame(() => {
182
+ try {
183
+ fitAddonRef.current?.fit();
184
+ } catch {}
185
+ });
186
+ }, [fontSize]);
187
+ /**
188
+ * Handle container resize.
189
+ */
190
+ useEffect(() => {
191
+ const handleResize = () => {
192
+ requestAnimationFrame(() => {
193
+ if (fitAddonRef.current && terminalRef.current) try {
194
+ fitAddonRef.current.fit();
195
+ } catch {}
196
+ });
197
+ };
198
+ window.addEventListener("resize", handleResize);
199
+ return () => window.removeEventListener("resize", handleResize);
200
+ }, []);
201
+ /**
202
+ * Font size update propagates to global settings if not overridden.
203
+ */
204
+ useEffect(() => {
205
+ if (!fontSizeProp) setFontSize(fontSize);
206
+ }, [
207
+ fontSize,
208
+ fontSizeProp,
209
+ setFontSize
210
+ ]);
211
+ /**
212
+ * Expose imperative methods to parent.
213
+ */
214
+ useImperativeHandle(ref, () => ({
215
+ focus: () => {
216
+ terminalRef.current?.focus();
217
+ },
218
+ write: (data) => {
219
+ terminalRef.current?.write(data);
220
+ },
221
+ clear: () => {
222
+ terminalRef.current?.clear();
223
+ },
224
+ resize: (cols, rows) => {
225
+ terminalRef.current?.resize(cols, rows);
226
+ }
227
+ }));
228
+ /**
229
+ * Render fallbacks for missing/unavailable xterm.js.
230
+ */
231
+ if (xtermAvailable === null) return /* @__PURE__ */ jsx("div", {
232
+ className: "flex items-center justify-center h-full text-muted-foreground text-sm min-h-[400px]",
233
+ children: "Loading xterm.js..."
234
+ });
235
+ if (xtermAvailable === false) return /* @__PURE__ */ jsxs("div", {
236
+ className: "flex flex-col items-center justify-center h-full text-muted-foreground text-sm gap-2 min-h-[400px]",
237
+ children: [/* @__PURE__ */ jsx("span", { children: "xterm.js is not installed" }), /* @__PURE__ */ jsx("code", {
238
+ className: "text-xs bg-muted px-2 py-1 rounded",
239
+ children: "bun add @xterm/xterm"
240
+ })]
241
+ });
242
+ return /* @__PURE__ */ jsx("div", {
243
+ ref: containerRef,
244
+ className: cn(terminalContainerStyles, className),
245
+ "data-session-id": sessionId
246
+ });
247
+ });
248
+ XTermRenderer.displayName = "XTermRenderer";
249
+
250
+ //#endregion
251
+ export { XTermRenderer };