@industry-theme/xterm-terminal-panel 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1933 @@
1
+ var __create = Object.create;
2
+ var __getProtoOf = Object.getPrototypeOf;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __toESM = (mod, isNodeMode, target) => {
7
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
8
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
9
+ for (let key of __getOwnPropNames(mod))
10
+ if (!__hasOwnProp.call(to, key))
11
+ __defProp(to, key, {
12
+ get: () => mod[key],
13
+ enumerable: true
14
+ });
15
+ return to;
16
+ };
17
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
18
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
19
+ }) : x)(function(x) {
20
+ if (typeof require !== "undefined")
21
+ return require.apply(this, arguments);
22
+ throw Error('Dynamic require of "' + x + '" is not supported');
23
+ });
24
+
25
+ // src/components/ThemedTerminal.tsx
26
+ import { FitAddon } from "@xterm/addon-fit";
27
+ import { SearchAddon } from "@xterm/addon-search";
28
+ import { WebLinksAddon } from "@xterm/addon-web-links";
29
+ import { Terminal } from "@xterm/xterm";
30
+ import {
31
+ ChevronDown,
32
+ ExternalLink,
33
+ Monitor,
34
+ Terminal as TerminalIcon,
35
+ X
36
+ } from "lucide-react";
37
+ import {
38
+ forwardRef,
39
+ useEffect,
40
+ useImperativeHandle,
41
+ useRef,
42
+ useState
43
+ } from "react";
44
+ import"@xterm/xterm/css/xterm.css";
45
+
46
+ // src/utils/terminalTheme.ts
47
+ function createTerminalTheme(theme) {
48
+ return {
49
+ background: theme.colors.background,
50
+ foreground: theme.colors.text,
51
+ cursor: theme.colors.primary,
52
+ cursorAccent: theme.colors.background,
53
+ selectionBackground: theme.colors.primary + "40",
54
+ selectionForeground: theme.colors.text,
55
+ selectionInactiveBackground: theme.colors.backgroundSecondary,
56
+ black: "#000000",
57
+ red: "#ff5555",
58
+ green: "#50fa7b",
59
+ yellow: "#f1fa8c",
60
+ blue: "#6272a4",
61
+ magenta: "#bd93f9",
62
+ cyan: "#8be9fd",
63
+ white: "#bfbfbf",
64
+ brightBlack: "#4d4d4d",
65
+ brightRed: "#ff6e67",
66
+ brightGreen: "#5af78e",
67
+ brightYellow: "#f4f99d",
68
+ brightBlue: "#6cadff",
69
+ brightMagenta: "#ff92d0",
70
+ brightCyan: "#9aedfe",
71
+ brightWhite: "#e6e6e6"
72
+ };
73
+ }
74
+ function getTerminalCSSVariables(theme) {
75
+ return {
76
+ "--terminal-bg": theme.colors.background,
77
+ "--terminal-fg": theme.colors.text,
78
+ "--terminal-border": theme.colors.border,
79
+ "--terminal-header-bg": theme.colors.backgroundSecondary || theme.colors.background,
80
+ "--terminal-font-family": 'Menlo, Monaco, "Courier New", monospace',
81
+ "--terminal-font-size": "14px"
82
+ };
83
+ }
84
+
85
+ // src/components/ThemedTerminal.tsx
86
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
87
+ var ThemedTerminal = forwardRef(({
88
+ theme,
89
+ onData,
90
+ onResize,
91
+ onReady,
92
+ onLinkClick,
93
+ onScrollPositionChange,
94
+ className = "",
95
+ hideHeader = false,
96
+ headerTitle = "Terminal",
97
+ headerSubtitle,
98
+ headerBadge,
99
+ autoFocus = true,
100
+ isVisible = true,
101
+ scrollbarStyle = "overlay",
102
+ onClose,
103
+ onDestroy,
104
+ onPopOut,
105
+ overlayState,
106
+ cursorBlink = true,
107
+ cursorStyle = "block",
108
+ scrollback = 1e4,
109
+ fontFamily,
110
+ fontSize = 14,
111
+ enableWebGL = false,
112
+ enableUnicode11 = false,
113
+ enableSearch = true,
114
+ enableWebLinks = true
115
+ }, ref) => {
116
+ const terminalRef = useRef(null);
117
+ const [terminal, setTerminal] = useState(null);
118
+ const fitAddonRef = useRef(null);
119
+ const searchAddonRef = useRef(null);
120
+ const resizeTimeoutRef = useRef(null);
121
+ const isVisibleRef = useRef(isVisible);
122
+ const isScrollLockedRef = useRef(true);
123
+ useEffect(() => {
124
+ isVisibleRef.current = isVisible;
125
+ }, [isVisible]);
126
+ const getScrollPosition = () => {
127
+ if (!terminal) {
128
+ return {
129
+ isAtTop: false,
130
+ isAtBottom: true,
131
+ isScrollLocked: isScrollLockedRef.current
132
+ };
133
+ }
134
+ const scrollY = terminal.buffer.active.viewportY;
135
+ const scrollback2 = terminal.buffer.active.baseY;
136
+ const isAtTop = scrollY === 0;
137
+ const isAtBottom = scrollY + terminal.rows >= scrollback2 + terminal.rows;
138
+ return {
139
+ isAtTop,
140
+ isAtBottom,
141
+ isScrollLocked: isScrollLockedRef.current
142
+ };
143
+ };
144
+ useImperativeHandle(ref, () => ({
145
+ write: (data) => {
146
+ if (terminal) {
147
+ terminal.write(data, () => {
148
+ if (isScrollLockedRef.current) {
149
+ terminal.scrollToBottom();
150
+ }
151
+ });
152
+ } else {
153
+ console.warn("[ThemedTerminal] write called but terminal is null!");
154
+ }
155
+ },
156
+ writeln: (data) => {
157
+ if (terminal) {
158
+ terminal.writeln(data);
159
+ if (isScrollLockedRef.current) {
160
+ terminal.scrollToBottom();
161
+ }
162
+ }
163
+ },
164
+ scrollToBottom: () => {
165
+ if (terminal) {
166
+ terminal.scrollToBottom();
167
+ isScrollLockedRef.current = true;
168
+ }
169
+ },
170
+ focus: () => {
171
+ if (terminal) {
172
+ terminal.focus();
173
+ }
174
+ },
175
+ blur: () => {
176
+ if (terminal) {
177
+ terminal.blur();
178
+ }
179
+ },
180
+ clear: () => {
181
+ if (terminal) {
182
+ terminal.clear();
183
+ }
184
+ },
185
+ reset: () => {
186
+ if (terminal) {
187
+ terminal.reset();
188
+ }
189
+ },
190
+ getTerminal: () => terminal,
191
+ resize: (cols, rows) => {
192
+ if (terminal) {
193
+ terminal.resize(cols, rows);
194
+ }
195
+ },
196
+ selectAll: () => {
197
+ if (terminal) {
198
+ terminal.selectAll();
199
+ }
200
+ },
201
+ clearSelection: () => {
202
+ if (terminal) {
203
+ terminal.clearSelection();
204
+ }
205
+ },
206
+ findNext: (searchTerm, searchOptions) => {
207
+ if (searchAddonRef.current) {
208
+ return searchAddonRef.current.findNext(searchTerm, searchOptions);
209
+ }
210
+ return false;
211
+ },
212
+ findPrevious: (searchTerm, searchOptions) => {
213
+ if (searchAddonRef.current) {
214
+ return searchAddonRef.current.findPrevious(searchTerm, searchOptions);
215
+ }
216
+ return false;
217
+ },
218
+ clearSearch: () => {
219
+ if (searchAddonRef.current) {
220
+ searchAddonRef.current.clearDecorations();
221
+ }
222
+ },
223
+ fit: () => {
224
+ if (fitAddonRef.current && terminal) {
225
+ fitAddonRef.current.fit();
226
+ if (isScrollLockedRef.current) {
227
+ requestAnimationFrame(() => {
228
+ terminal.scrollToBottom();
229
+ });
230
+ }
231
+ }
232
+ },
233
+ isScrollLocked: () => isScrollLockedRef.current,
234
+ getScrollPosition
235
+ }), [terminal]);
236
+ useEffect(() => {
237
+ if (!terminalRef.current || terminal) {
238
+ return;
239
+ }
240
+ const term = new Terminal({
241
+ cursorBlink,
242
+ cursorStyle,
243
+ fontSize,
244
+ fontFamily: fontFamily || 'Menlo, Monaco, "Courier New", monospace',
245
+ theme: createTerminalTheme(theme),
246
+ scrollback,
247
+ allowProposedApi: true,
248
+ lineHeight: 1,
249
+ letterSpacing: 0,
250
+ rightClickSelectsWord: true,
251
+ smoothScrollDuration: 0,
252
+ drawBoldTextInBrightColors: true,
253
+ windowsMode: false,
254
+ fontWeight: "normal",
255
+ fontWeightBold: "bold"
256
+ });
257
+ const fitAddon = new FitAddon;
258
+ fitAddonRef.current = fitAddon;
259
+ term.loadAddon(fitAddon);
260
+ if (enableSearch) {
261
+ const searchAddon = new SearchAddon;
262
+ searchAddonRef.current = searchAddon;
263
+ term.loadAddon(searchAddon);
264
+ }
265
+ if (enableUnicode11) {
266
+ import("@xterm/addon-unicode11").then(({ Unicode11Addon }) => {
267
+ const unicode11Addon = new Unicode11Addon;
268
+ term.loadAddon(unicode11Addon);
269
+ term.unicode.activeVersion = "11";
270
+ }).catch(() => {
271
+ console.warn("[Terminal] Unicode11Addon not available");
272
+ });
273
+ }
274
+ if (enableWebLinks) {
275
+ const webLinksAddon = new WebLinksAddon((event, uri) => {
276
+ event.preventDefault();
277
+ if (onLinkClick) {
278
+ const isLocalhost = /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?/i.test(uri);
279
+ onLinkClick(uri, isLocalhost);
280
+ }
281
+ });
282
+ term.loadAddon(webLinksAddon);
283
+ }
284
+ term.open(terminalRef.current);
285
+ const scrollDisposable = term.onScroll(() => {
286
+ const scrollY = term.buffer.active.viewportY;
287
+ const scrollback2 = term.buffer.active.baseY;
288
+ const isAtTop = scrollY === 0;
289
+ const isAtBottom = scrollY + term.rows >= scrollback2 + term.rows;
290
+ if (onScrollPositionChange) {
291
+ onScrollPositionChange({
292
+ isAtTop,
293
+ isAtBottom,
294
+ isScrollLocked: isScrollLockedRef.current
295
+ });
296
+ }
297
+ });
298
+ if (enableWebGL) {
299
+ import("@xterm/addon-webgl").then(({ WebglAddon }) => {
300
+ try {
301
+ const webglAddon = new WebglAddon;
302
+ webglAddon.onContextLoss(() => {
303
+ webglAddon.dispose();
304
+ console.warn("[Terminal] WebGL context lost, falling back to canvas renderer");
305
+ });
306
+ term.loadAddon(webglAddon);
307
+ } catch (e) {
308
+ console.warn("[Terminal] WebGL renderer not supported, using canvas renderer", e);
309
+ }
310
+ }).catch(() => {
311
+ console.warn("[Terminal] WebglAddon not available");
312
+ });
313
+ }
314
+ setTerminal(term);
315
+ const performFit = () => {
316
+ if (!fitAddonRef.current || !terminalRef.current || !term || !isVisibleRef.current) {
317
+ return;
318
+ }
319
+ fitAddonRef.current.fit();
320
+ if (isScrollLockedRef.current) {
321
+ requestAnimationFrame(() => {
322
+ term.scrollToBottom();
323
+ });
324
+ }
325
+ };
326
+ const handleResize = () => {
327
+ if (resizeTimeoutRef.current) {
328
+ clearTimeout(resizeTimeoutRef.current);
329
+ }
330
+ resizeTimeoutRef.current = setTimeout(() => {
331
+ performFit();
332
+ }, 100);
333
+ };
334
+ const resizeObserver = new ResizeObserver(() => {
335
+ handleResize();
336
+ });
337
+ if (terminalRef.current) {
338
+ resizeObserver.observe(terminalRef.current);
339
+ }
340
+ performFit();
341
+ return () => {
342
+ resizeObserver.disconnect();
343
+ scrollDisposable.dispose();
344
+ if (resizeTimeoutRef.current) {
345
+ clearTimeout(resizeTimeoutRef.current);
346
+ }
347
+ term.dispose();
348
+ };
349
+ }, [theme, onLinkClick, cursorBlink, cursorStyle, scrollback, fontSize, fontFamily, enableSearch, enableWebLinks, enableUnicode11, enableWebGL]);
350
+ useEffect(() => {
351
+ if (!terminal || !onReady) {
352
+ return;
353
+ }
354
+ const timeoutId = setTimeout(() => {
355
+ onReady(terminal.cols, terminal.rows);
356
+ }, 100);
357
+ return () => {
358
+ clearTimeout(timeoutId);
359
+ };
360
+ }, [terminal, onReady]);
361
+ useEffect(() => {
362
+ if (!terminal || !onData) {
363
+ return;
364
+ }
365
+ const disposable = terminal.onData((data) => {
366
+ onData(data);
367
+ });
368
+ return () => {
369
+ disposable.dispose();
370
+ };
371
+ }, [terminal, onData]);
372
+ useEffect(() => {
373
+ if (!terminal || !onResize)
374
+ return;
375
+ const disposable = terminal.onResize((size) => {
376
+ onResize(size.cols, size.rows);
377
+ });
378
+ return () => {
379
+ disposable.dispose();
380
+ };
381
+ }, [terminal, onResize]);
382
+ useEffect(() => {
383
+ const terminalElement = terminalRef.current;
384
+ if (!terminalElement)
385
+ return;
386
+ const handleKeyDown = (e) => {
387
+ if (e.key === " " || e.key === "Space") {
388
+ e.stopPropagation();
389
+ }
390
+ };
391
+ terminalElement.addEventListener("keydown", handleKeyDown, true);
392
+ return () => {
393
+ terminalElement.removeEventListener("keydown", handleKeyDown, true);
394
+ };
395
+ }, []);
396
+ useEffect(() => {
397
+ if (terminal && autoFocus && isVisible) {
398
+ setTimeout(() => {
399
+ terminal.focus();
400
+ }, 50);
401
+ }
402
+ }, [terminal, autoFocus, isVisible]);
403
+ useEffect(() => {
404
+ if (terminal && isVisible && fitAddonRef.current) {
405
+ setTimeout(() => {
406
+ fitAddonRef.current?.fit();
407
+ if (isScrollLockedRef.current) {
408
+ terminal.scrollToBottom();
409
+ }
410
+ }, 50);
411
+ }
412
+ }, [isVisible, terminal]);
413
+ const handleDestroy = () => {
414
+ if (onDestroy) {
415
+ const confirmed = window.confirm("Are you sure you want to close this terminal session? This will terminate any running processes.");
416
+ if (confirmed) {
417
+ onDestroy();
418
+ }
419
+ }
420
+ };
421
+ return /* @__PURE__ */ jsxs("div", {
422
+ className,
423
+ style: {
424
+ display: "flex",
425
+ flexDirection: "column",
426
+ height: "100%",
427
+ width: "100%",
428
+ backgroundColor: theme.colors.background
429
+ },
430
+ children: [
431
+ !hideHeader && /* @__PURE__ */ jsxs("div", {
432
+ style: {
433
+ display: "flex",
434
+ alignItems: "center",
435
+ justifyContent: "space-between",
436
+ padding: "8px 16px",
437
+ borderBottom: `1px solid ${theme.colors.border}`,
438
+ backgroundColor: theme.colors.backgroundSecondary || theme.colors.background
439
+ },
440
+ children: [
441
+ /* @__PURE__ */ jsxs("div", {
442
+ style: { display: "flex", alignItems: "center", gap: "8px" },
443
+ children: [
444
+ /* @__PURE__ */ jsx(TerminalIcon, {
445
+ size: 16,
446
+ color: theme.colors.text
447
+ }),
448
+ /* @__PURE__ */ jsx("span", {
449
+ style: {
450
+ fontSize: "14px",
451
+ color: theme.colors.text,
452
+ fontWeight: "500"
453
+ },
454
+ children: headerTitle
455
+ }),
456
+ headerSubtitle && /* @__PURE__ */ jsx("span", {
457
+ style: {
458
+ fontSize: "12px",
459
+ color: theme.colors.textSecondary
460
+ },
461
+ children: headerSubtitle
462
+ }),
463
+ headerBadge && /* @__PURE__ */ jsxs(Fragment, {
464
+ children: [
465
+ /* @__PURE__ */ jsx("span", {
466
+ style: {
467
+ fontSize: "12px",
468
+ color: theme.colors.textSecondary
469
+ },
470
+ children: "•"
471
+ }),
472
+ /* @__PURE__ */ jsx("span", {
473
+ style: {
474
+ fontSize: "12px",
475
+ color: headerBadge.color || theme.colors.primary
476
+ },
477
+ children: headerBadge.label
478
+ })
479
+ ]
480
+ })
481
+ ]
482
+ }),
483
+ /* @__PURE__ */ jsxs("div", {
484
+ style: { display: "flex", gap: "8px" },
485
+ children: [
486
+ onPopOut && /* @__PURE__ */ jsx("button", {
487
+ type: "button",
488
+ "aria-label": "Pop out terminal to new window",
489
+ onClick: onPopOut,
490
+ style: {
491
+ padding: "4px",
492
+ backgroundColor: "transparent",
493
+ border: "none",
494
+ cursor: "pointer",
495
+ color: theme.colors.textSecondary,
496
+ display: "flex",
497
+ alignItems: "center",
498
+ justifyContent: "center",
499
+ borderRadius: "4px",
500
+ transition: "all 0.2s"
501
+ },
502
+ onMouseEnter: (e) => {
503
+ e.currentTarget.style.backgroundColor = theme.colors.backgroundTertiary;
504
+ e.currentTarget.style.color = theme.colors.primary;
505
+ },
506
+ onMouseLeave: (e) => {
507
+ e.currentTarget.style.backgroundColor = "transparent";
508
+ e.currentTarget.style.color = theme.colors.textSecondary;
509
+ },
510
+ title: "Open terminal in new window",
511
+ children: /* @__PURE__ */ jsx(ExternalLink, {
512
+ size: 16
513
+ })
514
+ }),
515
+ onClose && /* @__PURE__ */ jsx("button", {
516
+ type: "button",
517
+ "aria-label": "Hide terminal",
518
+ onClick: onClose,
519
+ style: {
520
+ padding: "4px",
521
+ backgroundColor: "transparent",
522
+ border: "none",
523
+ cursor: "pointer",
524
+ color: theme.colors.textSecondary,
525
+ display: "flex",
526
+ alignItems: "center",
527
+ justifyContent: "center",
528
+ borderRadius: "4px",
529
+ transition: "all 0.2s"
530
+ },
531
+ onMouseEnter: (e) => {
532
+ e.currentTarget.style.backgroundColor = theme.colors.backgroundTertiary;
533
+ e.currentTarget.style.color = theme.colors.text;
534
+ },
535
+ onMouseLeave: (e) => {
536
+ e.currentTarget.style.backgroundColor = "transparent";
537
+ e.currentTarget.style.color = theme.colors.textSecondary;
538
+ },
539
+ title: "Hide terminal (keeps session running)",
540
+ children: /* @__PURE__ */ jsx(ChevronDown, {
541
+ size: 16
542
+ })
543
+ }),
544
+ onDestroy && /* @__PURE__ */ jsx("button", {
545
+ type: "button",
546
+ "aria-label": "Close terminal session",
547
+ onClick: handleDestroy,
548
+ style: {
549
+ padding: "4px",
550
+ backgroundColor: "transparent",
551
+ border: "none",
552
+ cursor: "pointer",
553
+ color: theme.colors.textSecondary,
554
+ display: "flex",
555
+ alignItems: "center",
556
+ justifyContent: "center",
557
+ borderRadius: "4px",
558
+ transition: "all 0.2s"
559
+ },
560
+ onMouseEnter: (e) => {
561
+ e.currentTarget.style.backgroundColor = "#ff4444";
562
+ e.currentTarget.style.color = "#ffffff";
563
+ },
564
+ onMouseLeave: (e) => {
565
+ e.currentTarget.style.backgroundColor = "transparent";
566
+ e.currentTarget.style.color = theme.colors.textSecondary;
567
+ },
568
+ title: "Close terminal session (terminate process)",
569
+ children: /* @__PURE__ */ jsx(X, {
570
+ size: 16
571
+ })
572
+ })
573
+ ]
574
+ })
575
+ ]
576
+ }),
577
+ /* @__PURE__ */ jsx("div", {
578
+ ref: terminalRef,
579
+ className: `terminal-container-fix ${scrollbarStyle === "hidden" ? "hide-scrollbar" : scrollbarStyle === "thin" ? "thin-scrollbar" : scrollbarStyle === "auto-hide" ? "auto-hide-scrollbar" : ""}`,
580
+ style: {
581
+ flex: 1,
582
+ overflow: "hidden",
583
+ position: "relative",
584
+ width: "100%",
585
+ height: "100%",
586
+ minHeight: 0
587
+ },
588
+ children: overlayState && /* @__PURE__ */ jsxs("div", {
589
+ style: {
590
+ position: "absolute",
591
+ top: 0,
592
+ left: 0,
593
+ right: 0,
594
+ bottom: 0,
595
+ display: "flex",
596
+ flexDirection: "column",
597
+ alignItems: "center",
598
+ justifyContent: "center",
599
+ backgroundColor: theme.colors.background,
600
+ opacity: overlayState.opacity ?? 1,
601
+ gap: "16px",
602
+ padding: "32px",
603
+ zIndex: 10
604
+ },
605
+ children: [
606
+ /* @__PURE__ */ jsx(Monitor, {
607
+ size: 48,
608
+ color: theme.colors.textSecondary
609
+ }),
610
+ /* @__PURE__ */ jsx("div", {
611
+ style: {
612
+ fontSize: "16px",
613
+ fontWeight: "500",
614
+ color: theme.colors.text,
615
+ textAlign: "center"
616
+ },
617
+ children: overlayState.message
618
+ }),
619
+ overlayState.subtitle && /* @__PURE__ */ jsx("div", {
620
+ style: {
621
+ fontSize: "14px",
622
+ color: theme.colors.textSecondary,
623
+ textAlign: "center",
624
+ maxWidth: "400px"
625
+ },
626
+ children: overlayState.subtitle
627
+ }),
628
+ overlayState.actions && overlayState.actions.length > 0 && /* @__PURE__ */ jsx("div", {
629
+ style: {
630
+ display: "flex",
631
+ gap: "12px",
632
+ marginTop: "8px"
633
+ },
634
+ children: overlayState.actions.map((action) => /* @__PURE__ */ jsxs("button", {
635
+ type: "button",
636
+ onClick: action.onClick,
637
+ style: {
638
+ padding: "8px 16px",
639
+ backgroundColor: action.primary ? theme.colors.primary : "transparent",
640
+ color: action.primary ? "#ffffff" : theme.colors.text,
641
+ border: action.primary ? "none" : `1px solid ${theme.colors.border}`,
642
+ borderRadius: "6px",
643
+ cursor: "pointer",
644
+ fontSize: "14px",
645
+ fontWeight: "500",
646
+ display: "flex",
647
+ alignItems: "center",
648
+ gap: "8px",
649
+ transition: "all 0.2s"
650
+ },
651
+ onMouseEnter: (e) => {
652
+ if (action.primary) {
653
+ e.currentTarget.style.opacity = "0.8";
654
+ } else {
655
+ e.currentTarget.style.backgroundColor = theme.colors.backgroundSecondary;
656
+ }
657
+ },
658
+ onMouseLeave: (e) => {
659
+ if (action.primary) {
660
+ e.currentTarget.style.opacity = "1";
661
+ } else {
662
+ e.currentTarget.style.backgroundColor = "transparent";
663
+ }
664
+ },
665
+ children: [
666
+ action.icon,
667
+ action.label
668
+ ]
669
+ }, action.label))
670
+ })
671
+ ]
672
+ })
673
+ })
674
+ ]
675
+ });
676
+ });
677
+ ThemedTerminal.displayName = "ThemedTerminal";
678
+ // src/components/ThemedTerminalWithProvider.tsx
679
+ import { useTheme } from "@principal-ade/industry-theme";
680
+ import { forwardRef as forwardRef2 } from "react";
681
+ import { jsx as jsx2 } from "react/jsx-runtime";
682
+ var ThemedTerminalWithProvider = forwardRef2((props, ref) => {
683
+ const { theme } = useTheme();
684
+ return /* @__PURE__ */ jsx2(ThemedTerminal, {
685
+ ref,
686
+ theme,
687
+ ...props
688
+ });
689
+ });
690
+ ThemedTerminalWithProvider.displayName = "ThemedTerminalWithProvider";
691
+ // src/hooks/useThemedTerminal.ts
692
+ import { useTheme as useTheme2 } from "@principal-ade/industry-theme";
693
+ import { useMemo } from "react";
694
+ function useThemedTerminal() {
695
+ const { theme } = useTheme2();
696
+ const terminalOptions = useMemo(() => ({
697
+ cursorBlink: true,
698
+ fontSize: 14,
699
+ fontFamily: 'Menlo, Monaco, "Courier New", monospace',
700
+ theme: createTerminalTheme(theme),
701
+ scrollback: 1e4,
702
+ allowProposedApi: true,
703
+ lineHeight: 1,
704
+ letterSpacing: 0,
705
+ rightClickSelectsWord: true,
706
+ smoothScrollDuration: 0,
707
+ drawBoldTextInBrightColors: true,
708
+ windowsMode: false,
709
+ fontWeight: "normal",
710
+ fontWeightBold: "bold"
711
+ }), [theme]);
712
+ const getTerminalOptions = (overrides) => {
713
+ return {
714
+ ...terminalOptions,
715
+ ...overrides,
716
+ theme: overrides?.theme ? { ...terminalOptions.theme, ...overrides.theme } : terminalOptions.theme
717
+ };
718
+ };
719
+ const getCSSVariables = () => getTerminalCSSVariables(theme);
720
+ return {
721
+ theme,
722
+ getTerminalOptions,
723
+ getCSSVariables
724
+ };
725
+ }
726
+ // src/panels/TerminalPanel.tsx
727
+ import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
728
+ import { Lock, Unlock, ArrowDown } from "lucide-react";
729
+ import { useTheme as useTheme3 } from "@principal-ade/industry-theme";
730
+
731
+ // src/panel-types/index.ts
732
+ function getTerminalSessions(context) {
733
+ const slice = context.getSlice("terminal");
734
+ return slice?.data ?? [];
735
+ }
736
+ function getTerminalSession(context, sessionId) {
737
+ const sessions = getTerminalSessions(context);
738
+ return sessions.find((s) => s.id === sessionId);
739
+ }
740
+ function isTerminalLoading(context) {
741
+ return context.isSliceLoading("terminal");
742
+ }
743
+ function getRepositoryPath(context) {
744
+ return context.currentScope.repository?.path ?? null;
745
+ }
746
+ function getWorkspacePath(context) {
747
+ return context.currentScope.workspace?.path ?? null;
748
+ }
749
+ function getTerminalDirectory(context, terminalScope = "repository") {
750
+ switch (terminalScope) {
751
+ case "workspace":
752
+ return getWorkspacePath(context);
753
+ case "repository":
754
+ return getRepositoryPath(context) ?? getWorkspacePath(context);
755
+ default:
756
+ return getRepositoryPath(context) ?? getWorkspacePath(context);
757
+ }
758
+ }
759
+ function getTerminalSlice(context) {
760
+ return context.getSlice("terminal");
761
+ }
762
+
763
+ // src/panels/TerminalPanel.tsx
764
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
765
+ var TerminalPanel = ({
766
+ context,
767
+ actions,
768
+ events: _events,
769
+ terminalScope = "repository"
770
+ }) => {
771
+ const { theme } = useTheme3();
772
+ const [sessionId, setSessionId] = useState2(null);
773
+ const [error, setError] = useState2(null);
774
+ const [isInitializing, setIsInitializing] = useState2(true);
775
+ const [usingMessagePort, setUsingMessagePort] = useState2(false);
776
+ const [scrollPosition, setScrollPosition] = useState2({
777
+ isAtTop: false,
778
+ isAtBottom: true,
779
+ isScrollLocked: false
780
+ });
781
+ const terminalRef = useRef2(null);
782
+ const dataPortRef = useRef2(null);
783
+ const terminalDirectory = getTerminalDirectory(context, terminalScope);
784
+ const pendingSessionIdRef = useRef2(null);
785
+ useEffect2(() => {
786
+ let mounted = true;
787
+ let unsubscribePortReady = null;
788
+ const initTerminal = async () => {
789
+ try {
790
+ if (!actions.createTerminalSession) {
791
+ throw new Error("Terminal actions not available. Host must provide createTerminalSession action.");
792
+ }
793
+ if (!actions.onTerminalPortReady) {
794
+ throw new Error("MessagePort support not available. Host must provide onTerminalPortReady action.");
795
+ }
796
+ unsubscribePortReady = actions.onTerminalPortReady((data, port) => {
797
+ if (data.sessionId === pendingSessionIdRef.current || data.sessionId === sessionId) {
798
+ console.info(`[TerminalPanel] Received MessagePort for session ${data.sessionId}`);
799
+ dataPortRef.current = port;
800
+ if (typeof port.start === "function") {
801
+ port.start();
802
+ }
803
+ if (mounted) {
804
+ setUsingMessagePort(true);
805
+ }
806
+ }
807
+ });
808
+ const id = await actions.createTerminalSession({
809
+ cwd: terminalDirectory || undefined
810
+ });
811
+ pendingSessionIdRef.current = id;
812
+ if (actions.claimTerminalOwnership) {
813
+ await actions.claimTerminalOwnership(id);
814
+ }
815
+ if (mounted) {
816
+ setSessionId(id);
817
+ setIsInitializing(false);
818
+ }
819
+ } catch (err) {
820
+ if (mounted) {
821
+ setError(err instanceof Error ? err.message : String(err));
822
+ setIsInitializing(false);
823
+ }
824
+ }
825
+ };
826
+ initTerminal();
827
+ return () => {
828
+ mounted = false;
829
+ if (unsubscribePortReady) {
830
+ unsubscribePortReady();
831
+ }
832
+ if (pendingSessionIdRef.current && actions.destroyTerminalSession) {
833
+ actions.destroyTerminalSession(pendingSessionIdRef.current);
834
+ }
835
+ if (dataPortRef.current) {
836
+ if (typeof dataPortRef.current.close === "function") {
837
+ dataPortRef.current.close();
838
+ }
839
+ dataPortRef.current = null;
840
+ }
841
+ };
842
+ }, []);
843
+ useEffect2(() => {
844
+ if (usingMessagePort)
845
+ return;
846
+ if (!sessionId)
847
+ return;
848
+ const timeout = setTimeout(() => {
849
+ if (!dataPortRef.current) {
850
+ console.error("[TerminalPanel] MessagePort not received after timeout - terminal data will not be displayed");
851
+ setError("MessagePort not available. Terminal data streaming requires MessagePort support from the host.");
852
+ }
853
+ }, 5000);
854
+ return () => clearTimeout(timeout);
855
+ }, [sessionId, usingMessagePort]);
856
+ useEffect2(() => {
857
+ if (!usingMessagePort || !dataPortRef.current)
858
+ return;
859
+ const port = dataPortRef.current;
860
+ let lastWriteTime = 0;
861
+ let cursorMovedToHome = false;
862
+ let autoScrollTimeout = null;
863
+ const handleMessage = (event) => {
864
+ if (event.data?.type === "DATA" && terminalRef.current) {
865
+ const data = event.data.data;
866
+ if (data.includes("\x1B[H")) {
867
+ cursorMovedToHome = true;
868
+ lastWriteTime = Date.now();
869
+ }
870
+ terminalRef.current.write(data);
871
+ if (cursorMovedToHome && scrollPosition.isScrollLocked) {
872
+ if (autoScrollTimeout) {
873
+ clearTimeout(autoScrollTimeout);
874
+ }
875
+ autoScrollTimeout = setTimeout(() => {
876
+ const now = Date.now();
877
+ const timeSinceLastWrite = now - lastWriteTime;
878
+ if (timeSinceLastWrite >= 100 && cursorMovedToHome && terminalRef.current) {
879
+ terminalRef.current.write("\x1B[9999;1H");
880
+ cursorMovedToHome = false;
881
+ }
882
+ }, 100);
883
+ }
884
+ lastWriteTime = Date.now();
885
+ }
886
+ };
887
+ port.onmessage = handleMessage;
888
+ console.info("[TerminalPanel] Listening on MessagePort for terminal data");
889
+ return () => {
890
+ port.onmessage = null;
891
+ if (autoScrollTimeout) {
892
+ clearTimeout(autoScrollTimeout);
893
+ }
894
+ };
895
+ }, [usingMessagePort, scrollPosition.isScrollLocked]);
896
+ const handleTerminalData = (data) => {
897
+ if (sessionId && actions.writeToTerminal) {
898
+ actions.writeToTerminal(sessionId, data);
899
+ }
900
+ };
901
+ const hasNotifiedPtyRef = useRef2(false);
902
+ const handleTerminalResize = (cols, rows) => {
903
+ if (sessionId && actions.resizeTerminal) {
904
+ const isInitialNotification = !hasNotifiedPtyRef.current;
905
+ actions.resizeTerminal(sessionId, cols, rows);
906
+ if (isInitialNotification && actions.writeToTerminal) {
907
+ setTimeout(() => {
908
+ actions.writeToTerminal(sessionId, "\f");
909
+ }, 50);
910
+ }
911
+ hasNotifiedPtyRef.current = true;
912
+ }
913
+ };
914
+ useEffect2(() => {
915
+ hasNotifiedPtyRef.current = false;
916
+ }, [sessionId]);
917
+ const handleScrollPositionChange = (position) => {
918
+ setScrollPosition(position);
919
+ };
920
+ const sessionInfo = sessionId ? getTerminalSession(context, sessionId) : undefined;
921
+ if (error) {
922
+ return /* @__PURE__ */ jsxs2("div", {
923
+ style: {
924
+ padding: "20px",
925
+ color: "#ef4444",
926
+ backgroundColor: "#1a1a1a",
927
+ height: "100%",
928
+ display: "flex",
929
+ alignItems: "center",
930
+ justifyContent: "center",
931
+ flexDirection: "column",
932
+ gap: "10px"
933
+ },
934
+ children: [
935
+ /* @__PURE__ */ jsx3("div", {
936
+ style: { fontSize: "16px", fontWeight: "bold" },
937
+ children: "Terminal Error"
938
+ }),
939
+ /* @__PURE__ */ jsx3("div", {
940
+ style: { fontSize: "14px", opacity: 0.8 },
941
+ children: error
942
+ })
943
+ ]
944
+ });
945
+ }
946
+ if (isInitializing || !sessionId) {
947
+ return /* @__PURE__ */ jsx3("div", {
948
+ style: {
949
+ padding: "20px",
950
+ color: "#a0a0a0",
951
+ backgroundColor: "#1a1a1a",
952
+ height: "100%",
953
+ display: "flex",
954
+ alignItems: "center",
955
+ justifyContent: "center"
956
+ },
957
+ children: "Initializing terminal..."
958
+ });
959
+ }
960
+ const handleScrollToBottom = () => {
961
+ terminalRef.current?.scrollToBottom();
962
+ };
963
+ const handleToggleScrollLock = () => {
964
+ if (scrollPosition.isScrollLocked) {
965
+ const terminal = terminalRef.current?.getTerminal();
966
+ if (terminal) {
967
+ terminal.scrollLines(-1);
968
+ }
969
+ } else {
970
+ terminalRef.current?.scrollToBottom();
971
+ }
972
+ };
973
+ return /* @__PURE__ */ jsxs2("div", {
974
+ style: { height: "100%", width: "100%", display: "flex", flexDirection: "column" },
975
+ children: [
976
+ /* @__PURE__ */ jsxs2("div", {
977
+ style: {
978
+ display: "flex",
979
+ gap: "8px",
980
+ padding: "8px 12px",
981
+ backgroundColor: theme.colors.backgroundSecondary,
982
+ borderBottom: `1px solid ${theme.colors.border}`,
983
+ alignItems: "center"
984
+ },
985
+ children: [
986
+ /* @__PURE__ */ jsxs2("span", {
987
+ style: {
988
+ fontSize: "12px",
989
+ color: theme.colors.textSecondary,
990
+ marginRight: "auto",
991
+ fontFamily: theme.fonts.monospace
992
+ },
993
+ children: [
994
+ sessionInfo?.cwd || "Terminal",
995
+ " • ",
996
+ sessionInfo?.shell
997
+ ]
998
+ }),
999
+ /* @__PURE__ */ jsxs2("button", {
1000
+ onClick: handleToggleScrollLock,
1001
+ style: {
1002
+ display: "flex",
1003
+ alignItems: "center",
1004
+ gap: "4px",
1005
+ fontSize: "11px",
1006
+ padding: "4px 8px",
1007
+ borderRadius: "4px",
1008
+ backgroundColor: scrollPosition.isScrollLocked ? `${theme.colors.success}22` : `${theme.colors.warning}22`,
1009
+ color: scrollPosition.isScrollLocked ? theme.colors.success : theme.colors.warning,
1010
+ border: `1px solid ${scrollPosition.isScrollLocked ? `${theme.colors.success}44` : `${theme.colors.warning}44`}`,
1011
+ cursor: "pointer",
1012
+ transition: "opacity 0.2s"
1013
+ },
1014
+ onMouseEnter: (e) => e.currentTarget.style.opacity = "0.8",
1015
+ onMouseLeave: (e) => e.currentTarget.style.opacity = "1",
1016
+ title: scrollPosition.isScrollLocked ? "Click to unlock scroll" : "Click to lock scroll to bottom",
1017
+ children: [
1018
+ scrollPosition.isScrollLocked ? /* @__PURE__ */ jsx3(Lock, {
1019
+ size: 12
1020
+ }) : /* @__PURE__ */ jsx3(Unlock, {
1021
+ size: 12
1022
+ }),
1023
+ /* @__PURE__ */ jsx3("span", {
1024
+ children: scrollPosition.isScrollLocked ? "Locked" : "Unlocked"
1025
+ })
1026
+ ]
1027
+ }),
1028
+ /* @__PURE__ */ jsxs2("button", {
1029
+ onClick: handleScrollToBottom,
1030
+ disabled: scrollPosition.isAtBottom,
1031
+ style: {
1032
+ display: "flex",
1033
+ alignItems: "center",
1034
+ gap: "4px",
1035
+ fontSize: "11px",
1036
+ padding: "4px 10px",
1037
+ borderRadius: "4px",
1038
+ backgroundColor: scrollPosition.isAtBottom ? theme.colors.backgroundHover : theme.colors.accent,
1039
+ color: scrollPosition.isAtBottom ? theme.colors.textTertiary : theme.colors.text,
1040
+ border: `1px solid ${theme.colors.border}`,
1041
+ cursor: scrollPosition.isAtBottom ? "not-allowed" : "pointer",
1042
+ transition: "opacity 0.2s",
1043
+ opacity: scrollPosition.isAtBottom ? 0.5 : 1
1044
+ },
1045
+ onMouseEnter: (e) => !scrollPosition.isAtBottom && (e.currentTarget.style.opacity = "0.8"),
1046
+ onMouseLeave: (e) => !scrollPosition.isAtBottom && (e.currentTarget.style.opacity = "1"),
1047
+ title: "Scroll to bottom and lock",
1048
+ children: [
1049
+ /* @__PURE__ */ jsx3(ArrowDown, {
1050
+ size: 12
1051
+ }),
1052
+ /* @__PURE__ */ jsx3("span", {
1053
+ children: "Bottom"
1054
+ })
1055
+ ]
1056
+ })
1057
+ ]
1058
+ }),
1059
+ /* @__PURE__ */ jsx3("div", {
1060
+ style: { flex: 1 },
1061
+ children: /* @__PURE__ */ jsx3(ThemedTerminalWithProvider, {
1062
+ ref: terminalRef,
1063
+ onData: handleTerminalData,
1064
+ onResize: handleTerminalResize,
1065
+ onScrollPositionChange: handleScrollPositionChange,
1066
+ hideHeader: true,
1067
+ autoFocus: true,
1068
+ convertEol: true,
1069
+ cursorBlink: true,
1070
+ scrollback: 1e4,
1071
+ enableWebGL: true,
1072
+ enableUnicode11: true,
1073
+ enableSearch: true,
1074
+ enableWebLinks: true
1075
+ })
1076
+ })
1077
+ ]
1078
+ });
1079
+ };
1080
+ // src/panels/TabbedTerminalPanel.tsx
1081
+ import React, {
1082
+ useState as useState3,
1083
+ useCallback,
1084
+ useEffect as useEffect3,
1085
+ useRef as useRef3
1086
+ } from "react";
1087
+ import { Terminal as TerminalIcon2, X as X2, Plus, Lock as Lock2, Unlock as Unlock2, ArrowDown as ArrowDown2, Box, Boxes } from "lucide-react";
1088
+ import { useTheme as useTheme4 } from "@principal-ade/industry-theme";
1089
+ import { jsx as jsx4, jsxs as jsxs3, Fragment as Fragment2 } from "react/jsx-runtime";
1090
+ var TerminalTabContentInner = ({ tab, sessionId, isActive, isVisible, actions, terminalContext, onSessionCreated, onScrollPositionChange, isForeign = false }, ref) => {
1091
+ const terminalRef = useRef3(null);
1092
+ const [localSessionId, setLocalSessionId] = useState3(sessionId);
1093
+ const [isInitialized, setIsInitialized] = useState3(false);
1094
+ const hasInitializedRef = useRef3(false);
1095
+ const [scrollPosition, setScrollPosition] = useState3({
1096
+ isAtTop: false,
1097
+ isAtBottom: true,
1098
+ isScrollLocked: false
1099
+ });
1100
+ const [shouldRenderTerminal, setShouldRenderTerminal] = useState3(true);
1101
+ const [ownerWindowId, setOwnerWindowId] = useState3(null);
1102
+ const needsRefreshOnResizeRef = useRef3(false);
1103
+ const claimAndConnect = useCallback(async (targetSessionId, force = false) => {
1104
+ try {
1105
+ if (actions.claimTerminalOwnership) {
1106
+ await actions.claimTerminalOwnership(targetSessionId, force);
1107
+ }
1108
+ if (force && actions.requestTerminalDataPort) {
1109
+ await actions.requestTerminalDataPort(targetSessionId);
1110
+ }
1111
+ setShouldRenderTerminal(true);
1112
+ setOwnerWindowId(null);
1113
+ if (force) {
1114
+ needsRefreshOnResizeRef.current = true;
1115
+ }
1116
+ } catch (error) {
1117
+ console.error("[TerminalTabContent] Failed to claim ownership:", error);
1118
+ throw error;
1119
+ }
1120
+ }, [actions]);
1121
+ useEffect3(() => {
1122
+ if (hasInitializedRef.current) {
1123
+ return;
1124
+ }
1125
+ hasInitializedRef.current = true;
1126
+ let mounted = true;
1127
+ const initSession = async () => {
1128
+ try {
1129
+ if (sessionId) {
1130
+ if (isForeign && actions.checkTerminalOwnership) {
1131
+ const status = await actions.checkTerminalOwnership(sessionId);
1132
+ if (status.ownedByWindowId && !status.ownedByThisWindow) {
1133
+ setShouldRenderTerminal(false);
1134
+ setOwnerWindowId(status.ownedByWindowId);
1135
+ }
1136
+ } else {
1137
+ await claimAndConnect(sessionId);
1138
+ }
1139
+ setLocalSessionId(sessionId);
1140
+ setIsInitialized(true);
1141
+ return;
1142
+ }
1143
+ if (!actions.createTerminalSession) {
1144
+ return;
1145
+ }
1146
+ const newSessionId = await actions.createTerminalSession({
1147
+ cwd: tab.directory,
1148
+ command: tab.command,
1149
+ context: `${terminalContext}:${tab.id}`
1150
+ });
1151
+ if (!mounted)
1152
+ return;
1153
+ setLocalSessionId(newSessionId);
1154
+ setIsInitialized(true);
1155
+ onSessionCreated(tab.id, newSessionId);
1156
+ await claimAndConnect(newSessionId);
1157
+ } catch (error) {
1158
+ console.error("[TerminalTabContent] Failed to create session:", error);
1159
+ }
1160
+ };
1161
+ initSession();
1162
+ return () => {
1163
+ mounted = false;
1164
+ };
1165
+ }, []);
1166
+ useEffect3(() => {
1167
+ if (!localSessionId || !isInitialized || !shouldRenderTerminal) {
1168
+ return;
1169
+ }
1170
+ if (!actions.onTerminalData) {
1171
+ return;
1172
+ }
1173
+ const unsubscribe = actions.onTerminalData(localSessionId, (data) => {
1174
+ if (terminalRef.current) {
1175
+ terminalRef.current.write(data);
1176
+ }
1177
+ });
1178
+ return () => {
1179
+ unsubscribe();
1180
+ };
1181
+ }, [localSessionId, isInitialized, actions, shouldRenderTerminal]);
1182
+ const handleData = useCallback((data) => {
1183
+ if (localSessionId && actions.writeToTerminal) {
1184
+ actions.writeToTerminal(localSessionId, data);
1185
+ }
1186
+ }, [localSessionId, actions]);
1187
+ const handleResize = useCallback((cols, rows) => {
1188
+ if (localSessionId && actions.resizeTerminal) {
1189
+ actions.resizeTerminal(localSessionId, cols, rows);
1190
+ }
1191
+ }, [localSessionId, actions]);
1192
+ const handleReady = useCallback((cols, rows) => {
1193
+ if (localSessionId && actions.resizeTerminal) {
1194
+ const shouldForce = needsRefreshOnResizeRef.current;
1195
+ if (shouldForce) {
1196
+ needsRefreshOnResizeRef.current = false;
1197
+ }
1198
+ actions.resizeTerminal(localSessionId, cols, rows, shouldForce);
1199
+ }
1200
+ }, [localSessionId, actions]);
1201
+ useEffect3(() => {
1202
+ if (!localSessionId || !actions.onOwnershipLost) {
1203
+ return;
1204
+ }
1205
+ const unsubscribe = actions.onOwnershipLost((data) => {
1206
+ if (data.sessionId === localSessionId) {
1207
+ setShouldRenderTerminal(false);
1208
+ setOwnerWindowId(data.newOwnerWindowId);
1209
+ }
1210
+ });
1211
+ return () => {
1212
+ unsubscribe();
1213
+ };
1214
+ }, [localSessionId, actions]);
1215
+ const handleTakeControl = useCallback(async () => {
1216
+ if (!localSessionId) {
1217
+ return;
1218
+ }
1219
+ await claimAndConnect(localSessionId, true);
1220
+ }, [localSessionId, claimAndConnect]);
1221
+ const handleScrollPositionChange = useCallback((position) => {
1222
+ setScrollPosition(position);
1223
+ onScrollPositionChange?.(tab.id, position);
1224
+ }, [tab.id, onScrollPositionChange]);
1225
+ const handleScrollToBottom = useCallback(() => {
1226
+ terminalRef.current?.scrollToBottom();
1227
+ }, []);
1228
+ const handleToggleScrollLock = useCallback(() => {
1229
+ if (scrollPosition.isScrollLocked) {
1230
+ const terminal = terminalRef.current?.getTerminal();
1231
+ if (terminal) {
1232
+ terminal.scrollLines(-1);
1233
+ }
1234
+ } else {
1235
+ terminalRef.current?.scrollToBottom();
1236
+ }
1237
+ }, [scrollPosition.isScrollLocked]);
1238
+ React.useImperativeHandle(ref, () => ({
1239
+ scrollToBottom: handleScrollToBottom,
1240
+ toggleScrollLock: handleToggleScrollLock
1241
+ }), [handleScrollToBottom, handleToggleScrollLock]);
1242
+ if (!isInitialized) {
1243
+ return /* @__PURE__ */ jsx4("div", {
1244
+ style: {
1245
+ display: isActive ? "flex" : "none",
1246
+ height: "100%",
1247
+ alignItems: "center",
1248
+ justifyContent: "center",
1249
+ color: "#888"
1250
+ },
1251
+ children: "Initializing terminal..."
1252
+ });
1253
+ }
1254
+ const overlayState = !shouldRenderTerminal ? {
1255
+ message: "This terminal is active in another window",
1256
+ subtitle: ownerWindowId ? `Window ID: ${ownerWindowId}` : "Another window owns this terminal session",
1257
+ actions: [
1258
+ {
1259
+ label: "Take Control",
1260
+ onClick: handleTakeControl,
1261
+ primary: true
1262
+ }
1263
+ ],
1264
+ opacity: 1
1265
+ } : undefined;
1266
+ return /* @__PURE__ */ jsx4("div", {
1267
+ style: {
1268
+ display: isActive ? "flex" : "none",
1269
+ flexDirection: "column",
1270
+ height: "100%",
1271
+ width: "100%"
1272
+ },
1273
+ children: /* @__PURE__ */ jsx4(ThemedTerminalWithProvider, {
1274
+ ref: terminalRef,
1275
+ onData: shouldRenderTerminal ? handleData : undefined,
1276
+ onResize: shouldRenderTerminal ? handleResize : undefined,
1277
+ onReady: shouldRenderTerminal ? handleReady : undefined,
1278
+ onScrollPositionChange: shouldRenderTerminal ? handleScrollPositionChange : undefined,
1279
+ hideHeader: true,
1280
+ autoFocus: isActive && shouldRenderTerminal,
1281
+ isVisible: isVisible && isActive,
1282
+ convertEol: true,
1283
+ cursorBlink: shouldRenderTerminal,
1284
+ scrollback: 1e4,
1285
+ enableWebGL: true,
1286
+ enableUnicode11: true,
1287
+ enableSearch: true,
1288
+ enableWebLinks: true,
1289
+ overlayState
1290
+ }, shouldRenderTerminal ? "active" : "overlay")
1291
+ });
1292
+ };
1293
+ var TerminalTabContent = React.memo(React.forwardRef(TerminalTabContentInner));
1294
+ TerminalTabContent.displayName = "TerminalTabContent";
1295
+ var TabbedTerminalPanel = ({
1296
+ context: _context,
1297
+ actions,
1298
+ events: _events,
1299
+ terminalContext,
1300
+ directory,
1301
+ hideHeader = false,
1302
+ isVisible = true,
1303
+ onTabsChange,
1304
+ initialTabs = [],
1305
+ showAllTerminals = false,
1306
+ onShowAllTerminalsChange
1307
+ }) => {
1308
+ const { theme } = useTheme4();
1309
+ const [ownedTabs, setOwnedTabs] = useState3(initialTabs);
1310
+ const [foreignTabs, setForeignTabs] = useState3([]);
1311
+ const [activeTabId, setActiveTabId] = useState3(null);
1312
+ const [sessionIds, setSessionIds] = useState3(new Map);
1313
+ const [hoveredTabId, setHoveredTabId] = useState3(null);
1314
+ const [scrollPositions, setScrollPositions] = useState3(new Map);
1315
+ const tabs = React.useMemo(() => {
1316
+ const sortedForeignTabs = [...foreignTabs].sort((a, b) => a.label.localeCompare(b.label));
1317
+ return [...ownedTabs, ...sortedForeignTabs];
1318
+ }, [ownedTabs, foreignTabs]);
1319
+ const tabRefsMap = useRef3(new Map);
1320
+ const hasInitializedRef = useRef3(false);
1321
+ const isCreatingTabRef = useRef3(false);
1322
+ const headerRef = useRef3(null);
1323
+ const [isCompact, setIsCompact] = useState3(false);
1324
+ useEffect3(() => {
1325
+ if (!headerRef.current)
1326
+ return;
1327
+ const observer = new ResizeObserver((entries) => {
1328
+ const width = entries[0]?.contentRect.width ?? 0;
1329
+ setIsCompact(width < 400);
1330
+ });
1331
+ observer.observe(headerRef.current);
1332
+ return () => observer.disconnect();
1333
+ }, []);
1334
+ const handleTabScrollPositionChange = useCallback((tabId, position) => {
1335
+ setScrollPositions((prev) => new Map(prev).set(tabId, position));
1336
+ }, []);
1337
+ const defaultScrollPosition = {
1338
+ isAtTop: false,
1339
+ isAtBottom: true,
1340
+ isScrollLocked: false
1341
+ };
1342
+ const activeScrollPosition = activeTabId ? scrollPositions.get(activeTabId) ?? defaultScrollPosition : undefined;
1343
+ const handleScrollToBottom = useCallback(() => {
1344
+ if (activeTabId) {
1345
+ tabRefsMap.current.get(activeTabId)?.scrollToBottom();
1346
+ }
1347
+ }, [activeTabId]);
1348
+ const handleToggleScrollLock = useCallback(() => {
1349
+ if (activeTabId) {
1350
+ tabRefsMap.current.get(activeTabId)?.toggleScrollLock();
1351
+ }
1352
+ }, [activeTabId]);
1353
+ const restoreOwnedSessions = useCallback(async () => {
1354
+ try {
1355
+ let sessions = [];
1356
+ if (actions.listTerminalSessions) {
1357
+ sessions = await actions.listTerminalSessions();
1358
+ }
1359
+ const ownedSessions = sessions.filter((session) => session.context?.startsWith(terminalContext));
1360
+ if (ownedSessions.length > 0 && initialTabs.length === 0) {
1361
+ const restoredTabs = [];
1362
+ const restoredSessionIds = new Map;
1363
+ ownedSessions.forEach((session, index) => {
1364
+ const tabId = `tab-restored-${session.id}`;
1365
+ const sessionCwd = session.cwd || directory;
1366
+ const tab = {
1367
+ id: tabId,
1368
+ label: sessionCwd.split("/").pop() || sessionCwd,
1369
+ directory: sessionCwd,
1370
+ isActive: index === 0
1371
+ };
1372
+ restoredTabs.push(tab);
1373
+ restoredSessionIds.set(tabId, session.id);
1374
+ });
1375
+ setOwnedTabs(restoredTabs);
1376
+ setSessionIds(restoredSessionIds);
1377
+ setActiveTabId(restoredTabs[0]?.id || null);
1378
+ onTabsChange?.(restoredTabs);
1379
+ } else if (initialTabs.length > 0) {
1380
+ setOwnedTabs(initialTabs);
1381
+ setActiveTabId(initialTabs.find((t) => t.isActive)?.id || initialTabs[0]?.id || null);
1382
+ }
1383
+ } catch (err) {
1384
+ console.error("[TabbedTerminalPanel] Failed to restore owned sessions:", err);
1385
+ }
1386
+ }, [terminalContext, initialTabs, onTabsChange, actions, directory]);
1387
+ const fetchForeignSessions = useCallback(async () => {
1388
+ try {
1389
+ if (!actions.listTerminalSessions) {
1390
+ return;
1391
+ }
1392
+ const sessions = await actions.listTerminalSessions();
1393
+ const foreignSessions = sessions.filter((session) => !session.context?.startsWith(terminalContext));
1394
+ const foreignTabsList = [];
1395
+ const newSessionIds = new Map;
1396
+ foreignSessions.forEach((session) => {
1397
+ const tabId = `tab-foreign-${session.id}`;
1398
+ const sessionCwd = session.cwd || "/";
1399
+ const tab = {
1400
+ id: tabId,
1401
+ label: sessionCwd.split("/").pop() || sessionCwd,
1402
+ directory: sessionCwd,
1403
+ isActive: false
1404
+ };
1405
+ foreignTabsList.push(tab);
1406
+ newSessionIds.set(tabId, session.id);
1407
+ });
1408
+ setForeignTabs(foreignTabsList);
1409
+ setSessionIds((prev) => {
1410
+ const updated = new Map(prev);
1411
+ newSessionIds.forEach((v, k) => updated.set(k, v));
1412
+ return updated;
1413
+ });
1414
+ } catch (err) {
1415
+ console.error("[TabbedTerminalPanel] Failed to fetch foreign sessions:", err);
1416
+ }
1417
+ }, [terminalContext, actions]);
1418
+ const clearForeignTabs = useCallback(() => {
1419
+ setForeignTabs((prevForeign) => {
1420
+ const foreignTabIds = new Set(prevForeign.map((t) => t.id));
1421
+ setSessionIds((prev) => {
1422
+ const updated = new Map(prev);
1423
+ foreignTabIds.forEach((id) => updated.delete(id));
1424
+ return updated;
1425
+ });
1426
+ return [];
1427
+ });
1428
+ }, []);
1429
+ useEffect3(() => {
1430
+ if (hasInitializedRef.current)
1431
+ return;
1432
+ hasInitializedRef.current = true;
1433
+ restoreOwnedSessions();
1434
+ }, []);
1435
+ useEffect3(() => {
1436
+ if (showAllTerminals) {
1437
+ fetchForeignSessions();
1438
+ } else {
1439
+ clearForeignTabs();
1440
+ }
1441
+ }, [showAllTerminals, fetchForeignSessions, clearForeignTabs]);
1442
+ const switchTab = useCallback((tabId) => {
1443
+ setOwnedTabs((prevTabs) => prevTabs.map((t) => ({
1444
+ ...t,
1445
+ isActive: t.id === tabId
1446
+ })));
1447
+ setForeignTabs((prevTabs) => prevTabs.map((t) => ({
1448
+ ...t,
1449
+ isActive: t.id === tabId
1450
+ })));
1451
+ setActiveTabId(tabId);
1452
+ }, []);
1453
+ const addNewTab = useCallback((label, command, targetDirectory) => {
1454
+ const targetDir = targetDirectory || directory;
1455
+ const directoryName = targetDir.split("/").pop() || targetDir;
1456
+ const newTab = {
1457
+ id: `tab-${Date.now()}`,
1458
+ label: label || directoryName,
1459
+ directory: targetDir,
1460
+ command,
1461
+ isActive: true
1462
+ };
1463
+ setOwnedTabs((prevTabs) => {
1464
+ const updatedTabs = prevTabs.map((t) => ({ ...t, isActive: false }));
1465
+ const newTabs = [...updatedTabs, newTab];
1466
+ onTabsChange?.(newTabs);
1467
+ return newTabs;
1468
+ });
1469
+ setForeignTabs((prevTabs) => prevTabs.map((t) => ({ ...t, isActive: false })));
1470
+ setActiveTabId(newTab.id);
1471
+ }, [directory, onTabsChange]);
1472
+ const isForeignTab = useCallback((tabId) => {
1473
+ return tabId.startsWith("tab-foreign-");
1474
+ }, []);
1475
+ const closeTab = useCallback(async (tabId) => {
1476
+ const sessionId = sessionIds.get(tabId);
1477
+ const isForeign = isForeignTab(tabId);
1478
+ if (!isForeign && sessionId && actions.destroyTerminalSession) {
1479
+ try {
1480
+ await actions.destroyTerminalSession(sessionId);
1481
+ } catch (err) {
1482
+ console.error("[TabbedTerminalPanel] Failed to destroy session:", err);
1483
+ }
1484
+ }
1485
+ setSessionIds((prev) => {
1486
+ const newMap = new Map(prev);
1487
+ newMap.delete(tabId);
1488
+ return newMap;
1489
+ });
1490
+ const findNextActiveTab = (allTabs) => {
1491
+ const remaining = allTabs.filter((t) => t.id !== tabId);
1492
+ if (activeTabId === tabId && remaining.length > 0) {
1493
+ const newActive = remaining[remaining.length - 1];
1494
+ return newActive.id;
1495
+ }
1496
+ return remaining.length === 0 ? null : activeTabId;
1497
+ };
1498
+ if (isForeign) {
1499
+ setForeignTabs((prevTabs) => {
1500
+ const newTabs = prevTabs.filter((t) => t.id !== tabId);
1501
+ const nextActiveId = findNextActiveTab([...ownedTabs, ...newTabs]);
1502
+ if (nextActiveId !== activeTabId) {
1503
+ setActiveTabId(nextActiveId);
1504
+ if (nextActiveId) {
1505
+ setOwnedTabs((owned) => owned.map((t) => ({ ...t, isActive: t.id === nextActiveId })));
1506
+ }
1507
+ }
1508
+ return newTabs.map((t) => ({ ...t, isActive: t.id === nextActiveId }));
1509
+ });
1510
+ } else {
1511
+ setOwnedTabs((prevTabs) => {
1512
+ const newTabs = prevTabs.filter((t) => t.id !== tabId);
1513
+ const nextActiveId = findNextActiveTab([...newTabs, ...foreignTabs]);
1514
+ if (nextActiveId !== activeTabId) {
1515
+ setActiveTabId(nextActiveId);
1516
+ if (nextActiveId) {
1517
+ setForeignTabs((foreign) => foreign.map((t) => ({ ...t, isActive: t.id === nextActiveId })));
1518
+ }
1519
+ }
1520
+ onTabsChange?.(newTabs);
1521
+ return newTabs.map((t) => ({ ...t, isActive: t.id === nextActiveId }));
1522
+ });
1523
+ }
1524
+ }, [activeTabId, sessionIds, actions, onTabsChange, isForeignTab, ownedTabs, foreignTabs]);
1525
+ const handleSessionCreated = useCallback((tabId, sessionId) => {
1526
+ setSessionIds((prev) => new Map(prev).set(tabId, sessionId));
1527
+ }, []);
1528
+ const tabsRef = useRef3(tabs);
1529
+ const activeTabIdRef = useRef3(activeTabId);
1530
+ const addNewTabRef = useRef3(addNewTab);
1531
+ const closeTabRef = useRef3(closeTab);
1532
+ const switchTabRef = useRef3(switchTab);
1533
+ useEffect3(() => {
1534
+ tabsRef.current = tabs;
1535
+ activeTabIdRef.current = activeTabId;
1536
+ addNewTabRef.current = addNewTab;
1537
+ closeTabRef.current = closeTab;
1538
+ switchTabRef.current = switchTab;
1539
+ }, [tabs, activeTabId, addNewTab, closeTab, switchTab]);
1540
+ useEffect3(() => {
1541
+ const handleKeyDown = (e) => {
1542
+ if ((e.metaKey || e.ctrlKey) && e.key === "t") {
1543
+ e.preventDefault();
1544
+ e.stopPropagation();
1545
+ if (isCreatingTabRef.current)
1546
+ return;
1547
+ isCreatingTabRef.current = true;
1548
+ addNewTabRef.current?.();
1549
+ setTimeout(() => {
1550
+ isCreatingTabRef.current = false;
1551
+ }, 500);
1552
+ return;
1553
+ }
1554
+ if ((e.metaKey || e.ctrlKey) && e.key === "w") {
1555
+ const currentActiveTabId = activeTabIdRef.current;
1556
+ const currentTabs = tabsRef.current;
1557
+ if (currentActiveTabId && currentTabs.length > 0) {
1558
+ e.preventDefault();
1559
+ e.stopPropagation();
1560
+ closeTabRef.current?.(currentActiveTabId);
1561
+ }
1562
+ return;
1563
+ }
1564
+ if ((e.metaKey || e.ctrlKey) && e.key >= "1" && e.key <= "9") {
1565
+ e.preventDefault();
1566
+ const currentTabs = tabsRef.current;
1567
+ const keyNum = parseInt(e.key, 10);
1568
+ const tabIndex = keyNum === 9 ? currentTabs.length - 1 : keyNum - 1;
1569
+ if (tabIndex >= 0 && tabIndex < currentTabs.length) {
1570
+ const targetTab = currentTabs[tabIndex];
1571
+ if (targetTab) {
1572
+ switchTabRef.current?.(targetTab.id);
1573
+ }
1574
+ }
1575
+ }
1576
+ };
1577
+ window.addEventListener("keydown", handleKeyDown);
1578
+ return () => window.removeEventListener("keydown", handleKeyDown);
1579
+ }, []);
1580
+ return /* @__PURE__ */ jsxs3("div", {
1581
+ style: {
1582
+ display: "flex",
1583
+ flexDirection: "column",
1584
+ height: "100%",
1585
+ backgroundColor: theme.colors.background
1586
+ },
1587
+ children: [
1588
+ !hideHeader && /* @__PURE__ */ jsxs3("div", {
1589
+ ref: headerRef,
1590
+ style: {
1591
+ display: "flex",
1592
+ alignItems: "stretch",
1593
+ height: "41px",
1594
+ flexShrink: 0,
1595
+ boxSizing: "border-box"
1596
+ },
1597
+ children: [
1598
+ /* @__PURE__ */ jsx4("div", {
1599
+ style: {
1600
+ display: "flex",
1601
+ alignItems: "center",
1602
+ gap: "4px",
1603
+ padding: "0 8px",
1604
+ borderRight: `1px solid ${theme.colors.border}`,
1605
+ borderBottom: `1px solid ${theme.colors.border}`,
1606
+ boxSizing: "border-box"
1607
+ },
1608
+ children: /* @__PURE__ */ jsx4("button", {
1609
+ onClick: () => onShowAllTerminalsChange?.(!showAllTerminals),
1610
+ style: {
1611
+ display: "flex",
1612
+ alignItems: "center",
1613
+ justifyContent: "center",
1614
+ width: "28px",
1615
+ height: "28px",
1616
+ border: "none",
1617
+ borderRadius: "4px",
1618
+ backgroundColor: showAllTerminals ? `${theme.colors.primary}22` : "transparent",
1619
+ cursor: "pointer",
1620
+ color: showAllTerminals ? theme.colors.primary : theme.colors.textSecondary
1621
+ },
1622
+ onMouseEnter: (e) => {
1623
+ if (!showAllTerminals) {
1624
+ e.currentTarget.style.backgroundColor = theme.colors.backgroundTertiary;
1625
+ }
1626
+ },
1627
+ onMouseLeave: (e) => {
1628
+ if (!showAllTerminals) {
1629
+ e.currentTarget.style.backgroundColor = "transparent";
1630
+ }
1631
+ },
1632
+ title: showAllTerminals ? "Showing all terminals (click to filter by context)" : "Show all terminals",
1633
+ children: showAllTerminals ? /* @__PURE__ */ jsx4(Boxes, {
1634
+ size: 14
1635
+ }) : /* @__PURE__ */ jsx4(Box, {
1636
+ size: 14
1637
+ })
1638
+ })
1639
+ }),
1640
+ /* @__PURE__ */ jsx4("div", {
1641
+ style: {
1642
+ display: "flex",
1643
+ alignItems: "center",
1644
+ flex: 1,
1645
+ overflow: "hidden",
1646
+ borderBottom: `1px solid ${theme.colors.border}`,
1647
+ boxSizing: "border-box"
1648
+ },
1649
+ children: tabs.map((tab) => /* @__PURE__ */ jsxs3("div", {
1650
+ onClick: (e) => {
1651
+ e.stopPropagation();
1652
+ switchTab(tab.id);
1653
+ },
1654
+ onMouseEnter: () => setHoveredTabId(tab.id),
1655
+ onMouseLeave: () => setHoveredTabId(null),
1656
+ style: {
1657
+ display: "flex",
1658
+ alignItems: "center",
1659
+ justifyContent: "center",
1660
+ gap: "6px",
1661
+ padding: "6px 8px",
1662
+ backgroundColor: tab.isActive ? theme.colors.background : theme.colors.backgroundSecondary,
1663
+ borderBottom: `1px solid ${theme.colors.border}`,
1664
+ cursor: "pointer",
1665
+ fontSize: theme.fontSizes[1],
1666
+ fontWeight: tab.isActive ? theme.fontWeights.semibold : theme.fontWeights.body,
1667
+ fontFamily: theme.fonts.body,
1668
+ color: tab.isActive ? theme.colors.text : theme.colors.textSecondary,
1669
+ whiteSpace: "nowrap",
1670
+ transition: "all 0.2s",
1671
+ flex: 1,
1672
+ minWidth: 0,
1673
+ height: "100%",
1674
+ position: "relative",
1675
+ boxSizing: "border-box"
1676
+ },
1677
+ children: [
1678
+ hoveredTabId === tab.id && /* @__PURE__ */ jsx4("button", {
1679
+ onClick: (e) => {
1680
+ e.stopPropagation();
1681
+ closeTab(tab.id);
1682
+ },
1683
+ style: {
1684
+ display: "flex",
1685
+ alignItems: "center",
1686
+ justifyContent: "center",
1687
+ width: "16px",
1688
+ height: "16px",
1689
+ borderRadius: "3px",
1690
+ border: "none",
1691
+ backgroundColor: "transparent",
1692
+ cursor: "pointer",
1693
+ color: theme.colors.textSecondary,
1694
+ padding: 0,
1695
+ position: "absolute",
1696
+ left: "8px"
1697
+ },
1698
+ onMouseEnter: (e) => {
1699
+ e.currentTarget.style.backgroundColor = theme.colors.backgroundTertiary;
1700
+ },
1701
+ onMouseLeave: (e) => {
1702
+ e.currentTarget.style.backgroundColor = "transparent";
1703
+ },
1704
+ children: /* @__PURE__ */ jsx4(X2, {
1705
+ size: 12
1706
+ })
1707
+ }),
1708
+ /* @__PURE__ */ jsx4("span", {
1709
+ title: tab.directory,
1710
+ children: tab.label
1711
+ })
1712
+ ]
1713
+ }, tab.id))
1714
+ }),
1715
+ /* @__PURE__ */ jsxs3("div", {
1716
+ style: {
1717
+ display: "flex",
1718
+ alignItems: "center",
1719
+ gap: "4px",
1720
+ padding: "0 8px",
1721
+ borderLeft: `1px solid ${theme.colors.border}`,
1722
+ borderBottom: `1px solid ${theme.colors.border}`,
1723
+ boxSizing: "border-box"
1724
+ },
1725
+ children: [
1726
+ activeTabId && activeScrollPosition && /* @__PURE__ */ jsxs3(Fragment2, {
1727
+ children: [
1728
+ /* @__PURE__ */ jsxs3("button", {
1729
+ onClick: handleToggleScrollLock,
1730
+ style: {
1731
+ display: "flex",
1732
+ alignItems: "center",
1733
+ gap: "4px",
1734
+ fontSize: "11px",
1735
+ padding: isCompact ? "4px 6px" : "4px 8px",
1736
+ borderRadius: "4px",
1737
+ backgroundColor: activeScrollPosition.isScrollLocked ? `${theme.colors.success}22` : `${theme.colors.warning}22`,
1738
+ color: activeScrollPosition.isScrollLocked ? theme.colors.success : theme.colors.warning,
1739
+ border: `1px solid ${activeScrollPosition.isScrollLocked ? `${theme.colors.success}44` : `${theme.colors.warning}44`}`,
1740
+ cursor: "pointer",
1741
+ transition: "opacity 0.2s"
1742
+ },
1743
+ onMouseEnter: (e) => e.currentTarget.style.opacity = "0.8",
1744
+ onMouseLeave: (e) => e.currentTarget.style.opacity = "1",
1745
+ title: activeScrollPosition.isScrollLocked ? "Click to unlock scroll" : "Click to lock scroll to bottom",
1746
+ children: [
1747
+ activeScrollPosition.isScrollLocked ? /* @__PURE__ */ jsx4(Lock2, {
1748
+ size: 12
1749
+ }) : /* @__PURE__ */ jsx4(Unlock2, {
1750
+ size: 12
1751
+ }),
1752
+ !isCompact && /* @__PURE__ */ jsx4("span", {
1753
+ children: activeScrollPosition.isScrollLocked ? "Locked" : "Unlocked"
1754
+ })
1755
+ ]
1756
+ }),
1757
+ /* @__PURE__ */ jsxs3("button", {
1758
+ onClick: handleScrollToBottom,
1759
+ disabled: activeScrollPosition.isAtBottom,
1760
+ style: {
1761
+ display: "flex",
1762
+ alignItems: "center",
1763
+ gap: "4px",
1764
+ fontSize: "11px",
1765
+ padding: isCompact ? "4px 6px" : "4px 8px",
1766
+ borderRadius: "4px",
1767
+ backgroundColor: activeScrollPosition.isAtBottom ? theme.colors.backgroundHover : theme.colors.accent,
1768
+ color: activeScrollPosition.isAtBottom ? theme.colors.textTertiary : theme.colors.text,
1769
+ border: `1px solid ${theme.colors.border}`,
1770
+ cursor: activeScrollPosition.isAtBottom ? "not-allowed" : "pointer",
1771
+ transition: "opacity 0.2s",
1772
+ opacity: activeScrollPosition.isAtBottom ? 0.5 : 1
1773
+ },
1774
+ onMouseEnter: (e) => !activeScrollPosition.isAtBottom && (e.currentTarget.style.opacity = "0.8"),
1775
+ onMouseLeave: (e) => !activeScrollPosition.isAtBottom && (e.currentTarget.style.opacity = "1"),
1776
+ title: "Scroll to bottom and lock",
1777
+ children: [
1778
+ /* @__PURE__ */ jsx4(ArrowDown2, {
1779
+ size: 12
1780
+ }),
1781
+ !isCompact && /* @__PURE__ */ jsx4("span", {
1782
+ children: "Bottom"
1783
+ })
1784
+ ]
1785
+ })
1786
+ ]
1787
+ }),
1788
+ /* @__PURE__ */ jsx4("button", {
1789
+ onClick: () => addNewTab(),
1790
+ style: {
1791
+ display: "flex",
1792
+ alignItems: "center",
1793
+ justifyContent: "center",
1794
+ width: "28px",
1795
+ height: "28px",
1796
+ border: "none",
1797
+ borderRadius: "4px",
1798
+ backgroundColor: "transparent",
1799
+ cursor: "pointer",
1800
+ color: theme.colors.textSecondary
1801
+ },
1802
+ onMouseEnter: (e) => {
1803
+ e.currentTarget.style.backgroundColor = theme.colors.backgroundTertiary;
1804
+ },
1805
+ onMouseLeave: (e) => {
1806
+ e.currentTarget.style.backgroundColor = "transparent";
1807
+ },
1808
+ title: "New terminal (Cmd+T)",
1809
+ children: /* @__PURE__ */ jsx4(Plus, {
1810
+ size: 14
1811
+ })
1812
+ })
1813
+ ]
1814
+ })
1815
+ ]
1816
+ }),
1817
+ /* @__PURE__ */ jsxs3("div", {
1818
+ style: {
1819
+ flex: 1,
1820
+ display: "flex",
1821
+ flexDirection: "column",
1822
+ overflow: "hidden",
1823
+ width: "100%",
1824
+ minHeight: 0
1825
+ },
1826
+ children: [
1827
+ tabs.map((tab) => /* @__PURE__ */ jsx4(TerminalTabContent, {
1828
+ ref: (ref) => {
1829
+ if (ref) {
1830
+ tabRefsMap.current.set(tab.id, ref);
1831
+ } else {
1832
+ tabRefsMap.current.delete(tab.id);
1833
+ }
1834
+ },
1835
+ tab,
1836
+ sessionId: sessionIds.get(tab.id) || null,
1837
+ isActive: tab.id === activeTabId,
1838
+ isVisible,
1839
+ actions,
1840
+ terminalContext,
1841
+ onSessionCreated: handleSessionCreated,
1842
+ onScrollPositionChange: handleTabScrollPositionChange,
1843
+ isForeign: tab.id.startsWith("tab-foreign-")
1844
+ }, tab.id)),
1845
+ tabs.length === 0 && /* @__PURE__ */ jsxs3("div", {
1846
+ style: {
1847
+ display: "flex",
1848
+ flexDirection: "column",
1849
+ alignItems: "center",
1850
+ justifyContent: "center",
1851
+ height: "100%",
1852
+ color: theme.colors.textSecondary
1853
+ },
1854
+ children: [
1855
+ /* @__PURE__ */ jsx4(TerminalIcon2, {
1856
+ size: 32,
1857
+ style: { opacity: 0.5, marginBottom: "16px" }
1858
+ }),
1859
+ /* @__PURE__ */ jsx4("p", {
1860
+ children: "No terminal sessions"
1861
+ }),
1862
+ /* @__PURE__ */ jsx4("button", {
1863
+ onClick: () => addNewTab(),
1864
+ style: {
1865
+ marginTop: "16px",
1866
+ padding: "8px 16px",
1867
+ backgroundColor: theme.colors.primary,
1868
+ color: theme.colors.background,
1869
+ border: "none",
1870
+ borderRadius: "4px",
1871
+ cursor: "pointer",
1872
+ fontSize: theme.fontSizes[1],
1873
+ fontFamily: theme.fonts.body
1874
+ },
1875
+ children: "New Terminal"
1876
+ })
1877
+ ]
1878
+ })
1879
+ ]
1880
+ })
1881
+ ]
1882
+ });
1883
+ };
1884
+ TabbedTerminalPanel.displayName = "TabbedTerminalPanel";
1885
+ // src/panel-exports.ts
1886
+ var panels = [
1887
+ {
1888
+ metadata: {
1889
+ id: "com.principal.terminal",
1890
+ name: "Terminal",
1891
+ icon: "⚡",
1892
+ version: "0.1.0",
1893
+ author: "Principal",
1894
+ description: "Integrated terminal emulator with industry theming",
1895
+ slices: ["terminal"]
1896
+ },
1897
+ component: TerminalPanel,
1898
+ onMount: async (context) => {
1899
+ console.log("Terminal Panel mounted for repository:", context.currentScope.repository?.path);
1900
+ if (!context.hasSlice("terminal")) {
1901
+ console.warn("Terminal data slice not available in context");
1902
+ }
1903
+ },
1904
+ onUnmount: async (_context) => {
1905
+ console.log("Terminal Panel unmounting");
1906
+ }
1907
+ }
1908
+ ];
1909
+ var onPackageLoad = async () => {
1910
+ console.log("Panel package loaded - Terminal Panel Extension");
1911
+ };
1912
+ var onPackageUnload = async () => {
1913
+ console.log("Panel package unloading - Terminal Panel Extension");
1914
+ };
1915
+ export {
1916
+ useThemedTerminal,
1917
+ panels,
1918
+ onPackageUnload,
1919
+ onPackageLoad,
1920
+ isTerminalLoading,
1921
+ getWorkspacePath,
1922
+ getTerminalSlice,
1923
+ getTerminalSessions,
1924
+ getTerminalSession,
1925
+ getTerminalDirectory,
1926
+ getTerminalCSSVariables,
1927
+ getRepositoryPath,
1928
+ createTerminalTheme,
1929
+ ThemedTerminalWithProvider,
1930
+ ThemedTerminal,
1931
+ TerminalPanel,
1932
+ TabbedTerminalPanel
1933
+ };