@industry-theme/xterm-terminal-panel 0.5.36 → 0.7.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 CHANGED
@@ -35,7 +35,7 @@ import {
35
35
  // package.json
36
36
  var package_default = {
37
37
  name: "@industry-theme/xterm-terminal-panel",
38
- version: "0.5.36",
38
+ version: "0.7.0",
39
39
  description: "Industry-themed xterm.js terminal components with panel framework integration",
40
40
  type: "module",
41
41
  main: "dist/index.js",
@@ -1281,11 +1281,568 @@ var ThemedTerminalWithProvider = forwardRef2((props, ref) => {
1281
1281
  });
1282
1282
  });
1283
1283
  ThemedTerminalWithProvider.displayName = "ThemedTerminalWithProvider";
1284
- // src/hooks/useThemedTerminal.ts
1284
+ // src/renderer/XtermRenderer.tsx
1285
+ import { forwardRef as forwardRef3, useImperativeHandle as useImperativeHandle2, useRef as useRef2 } from "react";
1286
+ import { jsx as jsx4 } from "react/jsx-runtime";
1287
+ var XtermRenderer = forwardRef3((props, ref) => {
1288
+ const {
1289
+ transparent,
1290
+ enableSearch = true,
1291
+ enableWebLinks = true,
1292
+ cursorBlink = true,
1293
+ convertEol = true,
1294
+ ...rest
1295
+ } = props;
1296
+ const innerRef = useRef2(null);
1297
+ useImperativeHandle2(ref, () => ({
1298
+ write: (data) => innerRef.current?.write(data),
1299
+ clear: () => innerRef.current?.clear(),
1300
+ focus: () => innerRef.current?.focus(),
1301
+ blur: () => innerRef.current?.blur(),
1302
+ fit: () => innerRef.current?.fit(),
1303
+ resize: (cols, rows) => innerRef.current?.resize(cols, rows),
1304
+ scrollToBottom: () => innerRef.current?.scrollToBottom(),
1305
+ scrollLines: (delta) => {
1306
+ innerRef.current?.getTerminal()?.scrollLines(delta);
1307
+ },
1308
+ getDimensions: () => {
1309
+ const term = innerRef.current?.getTerminal();
1310
+ if (!term || !term.cols || !term.rows)
1311
+ return null;
1312
+ return { cols: term.cols, rows: term.rows };
1313
+ },
1314
+ getScrollPosition: () => innerRef.current?.getScrollPosition() ?? {
1315
+ isAtTop: false,
1316
+ isAtBottom: true,
1317
+ isScrollLocked: false
1318
+ },
1319
+ isScrollLocked: () => innerRef.current?.isScrollLocked() ?? false
1320
+ }), []);
1321
+ return /* @__PURE__ */ jsx4(ThemedTerminalWithProvider, {
1322
+ ref: innerRef,
1323
+ ...rest,
1324
+ enableSearch,
1325
+ enableWebLinks,
1326
+ cursorBlink,
1327
+ convertEol,
1328
+ hideHeader: true,
1329
+ activityDetection: false,
1330
+ allowTransparency: transparent
1331
+ });
1332
+ });
1333
+ XtermRenderer.displayName = "XtermRenderer";
1334
+ // src/components/TerminalSession.tsx
1285
1335
  import { useTheme as useTheme2 } from "@principal-ade/industry-theme";
1336
+ import {
1337
+ forwardRef as forwardRef4,
1338
+ useCallback as useCallback2,
1339
+ useEffect as useEffect2,
1340
+ useImperativeHandle as useImperativeHandle3,
1341
+ useRef as useRef3,
1342
+ useState as useState2
1343
+ } from "react";
1344
+
1345
+ // src/components/TerminalOverlay.tsx
1346
+ import { Monitor as Monitor2 } from "lucide-react";
1347
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
1348
+ var TerminalOverlay = ({ state, theme }) => /* @__PURE__ */ jsxs3("div", {
1349
+ style: {
1350
+ position: "absolute",
1351
+ top: 0,
1352
+ left: 0,
1353
+ right: 0,
1354
+ bottom: 0,
1355
+ display: "flex",
1356
+ flexDirection: "column",
1357
+ alignItems: "center",
1358
+ justifyContent: "center",
1359
+ backgroundColor: theme.colors.background,
1360
+ opacity: state.opacity ?? 1,
1361
+ gap: "16px",
1362
+ padding: "32px",
1363
+ zIndex: 10
1364
+ },
1365
+ children: [
1366
+ /* @__PURE__ */ jsx5(Monitor2, {
1367
+ size: 48,
1368
+ color: theme.colors.textSecondary
1369
+ }),
1370
+ /* @__PURE__ */ jsx5("div", {
1371
+ style: {
1372
+ fontSize: "16px",
1373
+ fontWeight: "500",
1374
+ color: theme.colors.text,
1375
+ textAlign: "center"
1376
+ },
1377
+ children: state.message
1378
+ }),
1379
+ state.subtitle && /* @__PURE__ */ jsx5("div", {
1380
+ style: {
1381
+ fontSize: "14px",
1382
+ color: theme.colors.textSecondary,
1383
+ textAlign: "center",
1384
+ maxWidth: "400px"
1385
+ },
1386
+ children: state.subtitle
1387
+ }),
1388
+ state.actions && state.actions.length > 0 && /* @__PURE__ */ jsx5("div", {
1389
+ style: {
1390
+ display: "flex",
1391
+ gap: "12px",
1392
+ marginTop: "8px"
1393
+ },
1394
+ children: state.actions.map((action) => /* @__PURE__ */ jsxs3("button", {
1395
+ type: "button",
1396
+ onClick: action.onClick,
1397
+ style: {
1398
+ padding: "8px 16px",
1399
+ backgroundColor: action.primary ? theme.colors.primary : "transparent",
1400
+ color: action.primary ? "#ffffff" : theme.colors.text,
1401
+ border: action.primary ? "none" : `1px solid ${theme.colors.border}`,
1402
+ borderRadius: "6px",
1403
+ cursor: "pointer",
1404
+ fontSize: "14px",
1405
+ fontWeight: "500",
1406
+ display: "flex",
1407
+ alignItems: "center",
1408
+ gap: "8px",
1409
+ transition: "all 0.2s"
1410
+ },
1411
+ onMouseEnter: (e) => {
1412
+ if (action.primary) {
1413
+ e.currentTarget.style.opacity = "0.8";
1414
+ } else {
1415
+ e.currentTarget.style.backgroundColor = theme.colors.backgroundSecondary;
1416
+ }
1417
+ },
1418
+ onMouseLeave: (e) => {
1419
+ if (action.primary) {
1420
+ e.currentTarget.style.opacity = "1";
1421
+ } else {
1422
+ e.currentTarget.style.backgroundColor = "transparent";
1423
+ }
1424
+ },
1425
+ children: [
1426
+ action.icon,
1427
+ action.label
1428
+ ]
1429
+ }, action.label))
1430
+ })
1431
+ ]
1432
+ });
1433
+
1434
+ // src/components/TerminalSession.tsx
1435
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1436
+ var TerminalSession = forwardRef4((props, ref) => {
1437
+ const {
1438
+ actions,
1439
+ renderer: Renderer = XtermRenderer,
1440
+ transparent,
1441
+ backgroundColor,
1442
+ defaultScrollLocked,
1443
+ autoFocus = true,
1444
+ isVisible = true,
1445
+ scrollback = 1e4,
1446
+ sessionId: sessionIdProp = null,
1447
+ onSessionCreated,
1448
+ cwd,
1449
+ command,
1450
+ sessionContext,
1451
+ persistOnUnmount = false,
1452
+ enableOwnership = false,
1453
+ isForeign = false,
1454
+ events,
1455
+ activityDetection = false,
1456
+ activityTimeout = 500,
1457
+ onActivityChange,
1458
+ onScrollPositionChange,
1459
+ onLinkClick,
1460
+ onShortcut,
1461
+ overlayState,
1462
+ isWorking = false,
1463
+ workingMessage,
1464
+ workingSubtitle
1465
+ } = props;
1466
+ const { theme } = useTheme2();
1467
+ const rendererRef = useRef3(null);
1468
+ const [localSessionId, setLocalSessionId] = useState2(sessionIdProp);
1469
+ const [isInitialized, setIsInitialized] = useState2(false);
1470
+ const hasInitializedRef = useRef3(false);
1471
+ const pendingSessionIdRef = useRef3(null);
1472
+ const [shouldRenderTerminal, setShouldRenderTerminal] = useState2(true);
1473
+ const [ownerWindowId, setOwnerWindowId] = useState2(null);
1474
+ const needsRefreshOnResizeRef = useRef3(false);
1475
+ const [scrollPosition, setScrollPosition] = useState2({
1476
+ isAtTop: false,
1477
+ isAtBottom: true,
1478
+ isScrollLocked: defaultScrollLocked ?? false
1479
+ });
1480
+ const isActiveRef = useRef3(false);
1481
+ const activityStartedAtRef = useRef3(0);
1482
+ const activityTimerRef = useRef3(null);
1483
+ const claimAndConnect = useCallback2(async (targetSessionId, force = false) => {
1484
+ if (!enableOwnership)
1485
+ return;
1486
+ try {
1487
+ if (actions.claimTerminalOwnership) {
1488
+ await actions.claimTerminalOwnership(targetSessionId, force);
1489
+ }
1490
+ if (force && actions.requestTerminalDataPort) {
1491
+ await actions.requestTerminalDataPort(targetSessionId);
1492
+ }
1493
+ setShouldRenderTerminal(true);
1494
+ setOwnerWindowId(null);
1495
+ if (force) {
1496
+ needsRefreshOnResizeRef.current = true;
1497
+ }
1498
+ } catch (error) {
1499
+ console.error("[TerminalSession] Failed to claim ownership:", error);
1500
+ throw error;
1501
+ }
1502
+ }, [actions, enableOwnership]);
1503
+ useEffect2(() => {
1504
+ if (hasInitializedRef.current)
1505
+ return;
1506
+ hasInitializedRef.current = true;
1507
+ let mounted = true;
1508
+ const tracer = getTracer();
1509
+ const init = async () => {
1510
+ try {
1511
+ if (sessionIdProp) {
1512
+ const span2 = tracer.startSpan("terminal.session.reconnect", {
1513
+ attributes: {
1514
+ "session.id": sessionIdProp,
1515
+ "session.context": sessionContext ?? "",
1516
+ "session.isForeign": isForeign
1517
+ }
1518
+ });
1519
+ if (enableOwnership) {
1520
+ if (isForeign && actions.checkTerminalOwnership) {
1521
+ const status = await actions.checkTerminalOwnership(sessionIdProp);
1522
+ if (status.ownedByWindowId && !status.ownedByThisWindow) {
1523
+ setShouldRenderTerminal(false);
1524
+ setOwnerWindowId(status.ownedByWindowId);
1525
+ span2.addEvent("terminal.session.reconnect_blocked", {
1526
+ "owner.windowId": status.ownedByWindowId
1527
+ });
1528
+ }
1529
+ } else {
1530
+ await claimAndConnect(sessionIdProp);
1531
+ }
1532
+ }
1533
+ if (mounted) {
1534
+ setLocalSessionId(sessionIdProp);
1535
+ setIsInitialized(true);
1536
+ }
1537
+ span2.setStatus({ code: SpanStatusCode.OK });
1538
+ span2.end();
1539
+ return;
1540
+ }
1541
+ if (!actions.createTerminalSession) {
1542
+ if (mounted)
1543
+ setIsInitialized(true);
1544
+ return;
1545
+ }
1546
+ const span = tracer.startSpan("terminal.session.create", {
1547
+ attributes: {
1548
+ "session.cwd": cwd ?? "",
1549
+ "session.context": sessionContext ?? ""
1550
+ }
1551
+ });
1552
+ const newId = await actions.createTerminalSession({
1553
+ cwd,
1554
+ command,
1555
+ context: sessionContext
1556
+ });
1557
+ pendingSessionIdRef.current = newId;
1558
+ span.setAttribute("session.id", newId);
1559
+ if (!mounted) {
1560
+ span.setStatus({ code: SpanStatusCode.OK });
1561
+ span.end();
1562
+ return;
1563
+ }
1564
+ if (enableOwnership) {
1565
+ await claimAndConnect(newId);
1566
+ }
1567
+ setLocalSessionId(newId);
1568
+ setIsInitialized(true);
1569
+ onSessionCreated?.(newId);
1570
+ span.setStatus({ code: SpanStatusCode.OK });
1571
+ span.end();
1572
+ } catch (err) {
1573
+ console.error("[TerminalSession] init failed:", err);
1574
+ const errSpan = tracer.startSpan("terminal.session.error", {
1575
+ attributes: {
1576
+ "session.context": sessionContext ?? "",
1577
+ "session.action": sessionIdProp ? "reconnect" : "create"
1578
+ }
1579
+ });
1580
+ errSpan.recordException(err);
1581
+ errSpan.setStatus({
1582
+ code: SpanStatusCode.ERROR,
1583
+ message: err instanceof Error ? err.message : "Unknown error"
1584
+ });
1585
+ errSpan.end();
1586
+ if (mounted)
1587
+ setIsInitialized(true);
1588
+ }
1589
+ };
1590
+ init();
1591
+ return () => {
1592
+ mounted = false;
1593
+ if (!persistOnUnmount && pendingSessionIdRef.current && actions.destroyTerminalSession) {
1594
+ const tracer2 = getTracer();
1595
+ const span = tracer2.startSpan("terminal.session.destroy", {
1596
+ attributes: { "session.id": pendingSessionIdRef.current }
1597
+ });
1598
+ Promise.resolve(actions.destroyTerminalSession(pendingSessionIdRef.current)).then(() => {
1599
+ span.setStatus({ code: SpanStatusCode.OK });
1600
+ span.end();
1601
+ }).catch((err) => {
1602
+ span.recordException(err);
1603
+ span.setStatus({ code: SpanStatusCode.ERROR });
1604
+ span.end();
1605
+ });
1606
+ }
1607
+ };
1608
+ }, []);
1609
+ const observeActivity = useCallback2(() => {
1610
+ if (!activityDetection)
1611
+ return;
1612
+ const now = Date.now();
1613
+ const emitBus = (isWorking2) => {
1614
+ if (!events || !localSessionId)
1615
+ return;
1616
+ events.emit({
1617
+ type: "terminal:activity-changed",
1618
+ source: "TerminalSession",
1619
+ timestamp: Date.now(),
1620
+ payload: {
1621
+ sessionId: localSessionId,
1622
+ activityType: isWorking2 ? "started" : "stopped",
1623
+ isWorking: isWorking2
1624
+ }
1625
+ });
1626
+ };
1627
+ if (!isActiveRef.current) {
1628
+ isActiveRef.current = true;
1629
+ activityStartedAtRef.current = now;
1630
+ onActivityChange?.({ isActive: true, startedAt: now });
1631
+ emitBus(true);
1632
+ }
1633
+ if (activityTimerRef.current)
1634
+ clearTimeout(activityTimerRef.current);
1635
+ activityTimerRef.current = setTimeout(() => {
1636
+ if (isActiveRef.current) {
1637
+ isActiveRef.current = false;
1638
+ onActivityChange?.({
1639
+ isActive: false,
1640
+ duration: Date.now() - activityStartedAtRef.current
1641
+ });
1642
+ emitBus(false);
1643
+ }
1644
+ }, activityTimeout);
1645
+ }, [activityDetection, activityTimeout, onActivityChange, events, localSessionId]);
1646
+ useEffect2(() => {
1647
+ if (!localSessionId || !isInitialized || !shouldRenderTerminal)
1648
+ return;
1649
+ if (!actions.onTerminalData)
1650
+ return;
1651
+ const unsubscribe = actions.onTerminalData(localSessionId, (data) => {
1652
+ rendererRef.current?.write(data);
1653
+ observeActivity();
1654
+ });
1655
+ return () => {
1656
+ unsubscribe();
1657
+ };
1658
+ }, [localSessionId, isInitialized, shouldRenderTerminal, actions, observeActivity]);
1659
+ useEffect2(() => {
1660
+ return () => {
1661
+ if (activityTimerRef.current)
1662
+ clearTimeout(activityTimerRef.current);
1663
+ };
1664
+ }, []);
1665
+ const handleData = useCallback2((data) => {
1666
+ if (localSessionId && actions.writeToTerminal) {
1667
+ actions.writeToTerminal(localSessionId, data);
1668
+ }
1669
+ }, [localSessionId, actions]);
1670
+ const handleResize = useCallback2((cols, rows) => {
1671
+ if (localSessionId && actions.resizeTerminal) {
1672
+ actions.resizeTerminal(localSessionId, cols, rows);
1673
+ }
1674
+ }, [localSessionId, actions]);
1675
+ const handleReady = useCallback2((cols, rows) => {
1676
+ if (!localSessionId || !actions.resizeTerminal)
1677
+ return;
1678
+ const shouldForce = needsRefreshOnResizeRef.current;
1679
+ if (!shouldForce) {
1680
+ actions.resizeTerminal(localSessionId, cols, rows, false);
1681
+ return;
1682
+ }
1683
+ needsRefreshOnResizeRef.current = false;
1684
+ const restoreAndResize = async () => {
1685
+ let bufferRestored = false;
1686
+ if (actions.getTerminalBuffer && rendererRef.current) {
1687
+ try {
1688
+ const buffer = await actions.getTerminalBuffer(localSessionId);
1689
+ if (buffer) {
1690
+ rendererRef.current.write(buffer);
1691
+ bufferRestored = true;
1692
+ }
1693
+ } catch (err) {
1694
+ console.warn("[TerminalSession] Failed to restore buffer:", err);
1695
+ }
1696
+ }
1697
+ actions.resizeTerminal(localSessionId, cols, rows, !bufferRestored);
1698
+ };
1699
+ restoreAndResize();
1700
+ }, [localSessionId, actions]);
1701
+ const handleLinkClickInternal = useCallback2((url, modifiers) => {
1702
+ onLinkClick?.(url, modifiers);
1703
+ if (localSessionId && events) {
1704
+ events.emit({
1705
+ type: "terminal:link-click",
1706
+ source: "TerminalSession",
1707
+ timestamp: Date.now(),
1708
+ payload: {
1709
+ url,
1710
+ sessionId: localSessionId,
1711
+ shiftKey: modifiers.shiftKey,
1712
+ metaKey: modifiers.metaKey,
1713
+ ctrlKey: modifiers.ctrlKey,
1714
+ altKey: modifiers.altKey
1715
+ }
1716
+ });
1717
+ }
1718
+ }, [localSessionId, events, onLinkClick]);
1719
+ const handleShortcutInternal = useCallback2((evt) => {
1720
+ onShortcut?.(evt);
1721
+ if (localSessionId && events) {
1722
+ events.emit({
1723
+ type: "terminal:shortcut",
1724
+ source: "TerminalSession",
1725
+ timestamp: Date.now(),
1726
+ payload: {
1727
+ shortcut: evt.shortcut,
1728
+ sessionId: localSessionId
1729
+ }
1730
+ });
1731
+ }
1732
+ }, [localSessionId, events, onShortcut]);
1733
+ const handleScrollPositionChangeInternal = useCallback2((p) => {
1734
+ setScrollPosition(p);
1735
+ onScrollPositionChange?.(p);
1736
+ }, [onScrollPositionChange]);
1737
+ useEffect2(() => {
1738
+ if (!enableOwnership)
1739
+ return;
1740
+ if (!localSessionId || !actions.onOwnershipLost)
1741
+ return;
1742
+ const unsubscribe = actions.onOwnershipLost((data) => {
1743
+ if (data.sessionId === localSessionId) {
1744
+ setShouldRenderTerminal(false);
1745
+ setOwnerWindowId(data.newOwnerWindowId);
1746
+ }
1747
+ });
1748
+ return () => {
1749
+ unsubscribe();
1750
+ };
1751
+ }, [enableOwnership, localSessionId, actions]);
1752
+ const handleTakeControl = useCallback2(async () => {
1753
+ if (!localSessionId)
1754
+ return;
1755
+ await claimAndConnect(localSessionId, true);
1756
+ }, [localSessionId, claimAndConnect]);
1757
+ useImperativeHandle3(ref, () => ({
1758
+ scrollToBottom: () => rendererRef.current?.scrollToBottom(),
1759
+ toggleScrollLock: () => {
1760
+ if (scrollPosition.isScrollLocked) {
1761
+ rendererRef.current?.scrollLines(-1);
1762
+ } else {
1763
+ rendererRef.current?.scrollToBottom();
1764
+ }
1765
+ },
1766
+ focus: () => rendererRef.current?.focus(),
1767
+ clear: () => rendererRef.current?.clear(),
1768
+ getSessionId: () => localSessionId
1769
+ }), [scrollPosition.isScrollLocked, localSessionId]);
1770
+ if (!isInitialized) {
1771
+ return /* @__PURE__ */ jsxs4("div", {
1772
+ style: {
1773
+ display: "flex",
1774
+ height: "100%",
1775
+ width: "100%",
1776
+ backgroundColor: transparent ? "transparent" : theme.colors.background,
1777
+ padding: "10px 0 0 10px"
1778
+ },
1779
+ children: [
1780
+ /* @__PURE__ */ jsx6("div", {
1781
+ style: {
1782
+ width: "8px",
1783
+ height: "17px",
1784
+ backgroundColor: theme.colors.text,
1785
+ animation: "terminal-session-cursor-blink 1s step-end infinite"
1786
+ }
1787
+ }),
1788
+ /* @__PURE__ */ jsx6("style", {
1789
+ children: `@keyframes terminal-session-cursor-blink {
1790
+ 0%, 100% { opacity: 1; }
1791
+ 50% { opacity: 0; }
1792
+ }`
1793
+ })
1794
+ ]
1795
+ });
1796
+ }
1797
+ const ownershipLostOverlay = enableOwnership && !shouldRenderTerminal ? {
1798
+ message: "This terminal is active in another window",
1799
+ subtitle: ownerWindowId ? `Window ID: ${ownerWindowId}` : "Another window owns this terminal session",
1800
+ actions: [
1801
+ {
1802
+ label: "Take Control",
1803
+ onClick: handleTakeControl,
1804
+ primary: true
1805
+ }
1806
+ ],
1807
+ opacity: 1
1808
+ } : undefined;
1809
+ const effectiveOverlay = ownershipLostOverlay ?? overlayState;
1810
+ return /* @__PURE__ */ jsxs4("div", {
1811
+ style: { position: "relative", height: "100%", width: "100%" },
1812
+ children: [
1813
+ /* @__PURE__ */ jsx6(Renderer, {
1814
+ ref: rendererRef,
1815
+ onData: shouldRenderTerminal ? handleData : undefined,
1816
+ onResize: shouldRenderTerminal ? handleResize : undefined,
1817
+ onReady: shouldRenderTerminal ? handleReady : undefined,
1818
+ onLinkClick: shouldRenderTerminal ? handleLinkClickInternal : undefined,
1819
+ onScrollPositionChange: shouldRenderTerminal ? handleScrollPositionChangeInternal : undefined,
1820
+ onShortcut: handleShortcutInternal,
1821
+ autoFocus: autoFocus && shouldRenderTerminal,
1822
+ isVisible,
1823
+ defaultScrollLocked,
1824
+ scrollback,
1825
+ transparent,
1826
+ backgroundColor
1827
+ }, shouldRenderTerminal ? "active" : "overlay"),
1828
+ effectiveOverlay && /* @__PURE__ */ jsx6(TerminalOverlay, {
1829
+ state: effectiveOverlay,
1830
+ theme
1831
+ }),
1832
+ isWorking && !effectiveOverlay && /* @__PURE__ */ jsx6(WorkingOverlay, {
1833
+ theme,
1834
+ message: workingMessage,
1835
+ subtitle: workingSubtitle
1836
+ })
1837
+ ]
1838
+ });
1839
+ });
1840
+ TerminalSession.displayName = "TerminalSession";
1841
+ // src/hooks/useThemedTerminal.ts
1842
+ import { useTheme as useTheme3 } from "@principal-ade/industry-theme";
1286
1843
  import { useMemo } from "react";
1287
1844
  function useThemedTerminal() {
1288
- const { theme } = useTheme2();
1845
+ const { theme } = useTheme3();
1289
1846
  const terminalOptions = useMemo(() => ({
1290
1847
  cursorBlink: true,
1291
1848
  fontSize: 14,
@@ -1317,14 +1874,14 @@ function useThemedTerminal() {
1317
1874
  };
1318
1875
  }
1319
1876
  // src/components/TabBar/TabBar.tsx
1320
- import { useTheme as useTheme4 } from "@principal-ade/industry-theme";
1877
+ import { useTheme as useTheme5 } from "@principal-ade/industry-theme";
1321
1878
  import { Plus } from "lucide-react";
1322
- import { useState as useState2, useCallback as useCallback3 } from "react";
1879
+ import { useState as useState3, useCallback as useCallback4 } from "react";
1323
1880
 
1324
1881
  // src/components/TabBar/TabButton.tsx
1325
- import { useTheme as useTheme3 } from "@principal-ade/industry-theme";
1326
- import { useCallback as useCallback2 } from "react";
1327
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1882
+ import { useTheme as useTheme4 } from "@principal-ade/industry-theme";
1883
+ import { useCallback as useCallback3 } from "react";
1884
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1328
1885
  var TabButton = ({
1329
1886
  tab,
1330
1887
  isActive,
@@ -1347,10 +1904,10 @@ var TabButton = ({
1347
1904
  onDragLeave,
1348
1905
  isBeingDragged: _isBeingDragged = false
1349
1906
  }) => {
1350
- const { theme } = useTheme3();
1907
+ const { theme } = useTheme4();
1351
1908
  const showCloseButton = isHovered || isActive;
1352
1909
  const closable = tab.closable !== false;
1353
- const handleDragStart = useCallback2((e) => {
1910
+ const handleDragStart = useCallback3((e) => {
1354
1911
  const target = e.currentTarget;
1355
1912
  e.dataTransfer.setData("application/x-tab-association", tab.id);
1356
1913
  e.dataTransfer.effectAllowed = "move";
@@ -1375,27 +1932,27 @@ var TabButton = ({
1375
1932
  }
1376
1933
  onDragStart?.(tab.id);
1377
1934
  }, [tab.id, onDragStart]);
1378
- const handleDragEnd = useCallback2(() => {
1935
+ const handleDragEnd = useCallback3(() => {
1379
1936
  onDragEnd?.();
1380
1937
  }, [onDragEnd]);
1381
- const handleDragOver = useCallback2((e) => {
1938
+ const handleDragOver = useCallback3((e) => {
1382
1939
  if (canAcceptDrop) {
1383
1940
  e.preventDefault();
1384
1941
  e.dataTransfer.dropEffect = "move";
1385
1942
  }
1386
1943
  }, [canAcceptDrop]);
1387
- const handleDrop = useCallback2((e) => {
1944
+ const handleDrop = useCallback3((e) => {
1388
1945
  e.preventDefault();
1389
1946
  const draggedTabId = e.dataTransfer.getData("application/x-tab-association");
1390
1947
  if (draggedTabId && draggedTabId !== tab.id) {
1391
1948
  onDrop?.(draggedTabId);
1392
1949
  }
1393
1950
  }, [tab.id, onDrop]);
1394
- const handleDragEnter = useCallback2((e) => {
1951
+ const handleDragEnter = useCallback3((e) => {
1395
1952
  e.preventDefault();
1396
1953
  onDragEnter?.();
1397
1954
  }, [onDragEnter]);
1398
- const handleDragLeave = useCallback2((e) => {
1955
+ const handleDragLeave = useCallback3((e) => {
1399
1956
  e.preventDefault();
1400
1957
  onDragLeave?.();
1401
1958
  }, [onDragLeave]);
@@ -1409,7 +1966,7 @@ var TabButton = ({
1409
1966
  }
1410
1967
  return {};
1411
1968
  };
1412
- return /* @__PURE__ */ jsxs3("div", {
1969
+ return /* @__PURE__ */ jsxs5("div", {
1413
1970
  "data-tab-id": tab.id,
1414
1971
  draggable,
1415
1972
  onDragStart: draggable ? handleDragStart : undefined,
@@ -1445,7 +2002,7 @@ var TabButton = ({
1445
2002
  ...getDragOverStyles()
1446
2003
  },
1447
2004
  children: [
1448
- closable && showCloseButton && /* @__PURE__ */ jsx4("button", {
2005
+ closable && showCloseButton && /* @__PURE__ */ jsx7("button", {
1449
2006
  onClick: (e) => {
1450
2007
  e.stopPropagation();
1451
2008
  onClose();
@@ -1477,7 +2034,7 @@ var TabButton = ({
1477
2034
  (() => {
1478
2035
  const icon = renderIcon ? renderIcon(tab) : undefined;
1479
2036
  const displayIcon = icon !== undefined ? icon : tab.icon;
1480
- return displayIcon ? /* @__PURE__ */ jsx4("div", {
2037
+ return displayIcon ? /* @__PURE__ */ jsx7("div", {
1481
2038
  style: { flexShrink: 0, display: "flex", alignItems: "center", pointerEvents: "none" },
1482
2039
  children: displayIcon
1483
2040
  }) : null;
@@ -1485,7 +2042,7 @@ var TabButton = ({
1485
2042
  (() => {
1486
2043
  const label = renderLabel ? renderLabel(tab) : undefined;
1487
2044
  const displayLabel = label !== undefined ? label : tab.label;
1488
- return /* @__PURE__ */ jsx4("span", {
2045
+ return /* @__PURE__ */ jsx7("span", {
1489
2046
  style: {
1490
2047
  overflow: "hidden",
1491
2048
  textOverflow: "ellipsis",
@@ -1496,7 +2053,7 @@ var TabButton = ({
1496
2053
  children: displayLabel
1497
2054
  });
1498
2055
  })(),
1499
- renderAccessory ? renderAccessory(tab) : keyboardHint && /* @__PURE__ */ jsx4("span", {
2056
+ renderAccessory ? renderAccessory(tab) : keyboardHint && /* @__PURE__ */ jsx7("span", {
1500
2057
  style: {
1501
2058
  fontSize: theme.fontSizes[0],
1502
2059
  color: theme.colors.textSecondary,
@@ -1511,7 +2068,7 @@ var TabButton = ({
1511
2068
  TabButton.displayName = "TabButton";
1512
2069
 
1513
2070
  // src/components/TabBar/TabBar.tsx
1514
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
2071
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
1515
2072
  var TabBar = ({
1516
2073
  tabs,
1517
2074
  activeTabId,
@@ -1529,31 +2086,31 @@ var TabBar = ({
1529
2086
  canDropOnTab,
1530
2087
  enableDragAndDrop = false
1531
2088
  }) => {
1532
- const { theme } = useTheme4();
1533
- const [hoveredTabId, setHoveredTabId] = useState2(null);
1534
- const [draggedTabId, setDraggedTabId] = useState2(null);
1535
- const [dragOverTabId, setDragOverTabId] = useState2(null);
1536
- const handleDragStart = useCallback3((tabId) => {
2089
+ const { theme } = useTheme5();
2090
+ const [hoveredTabId, setHoveredTabId] = useState3(null);
2091
+ const [draggedTabId, setDraggedTabId] = useState3(null);
2092
+ const [dragOverTabId, setDragOverTabId] = useState3(null);
2093
+ const handleDragStart = useCallback4((tabId) => {
1537
2094
  setDraggedTabId(tabId);
1538
2095
  }, []);
1539
- const handleDragEnd = useCallback3(() => {
2096
+ const handleDragEnd = useCallback4(() => {
1540
2097
  setDraggedTabId(null);
1541
2098
  setDragOverTabId(null);
1542
2099
  }, []);
1543
- const handleDrop = useCallback3((targetTabId, draggedId) => {
2100
+ const handleDrop = useCallback4((targetTabId, draggedId) => {
1544
2101
  if (draggedId !== targetTabId) {
1545
2102
  onTabDrop?.(draggedId, targetTabId);
1546
2103
  }
1547
2104
  setDraggedTabId(null);
1548
2105
  setDragOverTabId(null);
1549
2106
  }, [onTabDrop]);
1550
- const handleDragEnter = useCallback3((tabId) => {
2107
+ const handleDragEnter = useCallback4((tabId) => {
1551
2108
  setDragOverTabId(tabId);
1552
2109
  }, []);
1553
- const handleDragLeave = useCallback3(() => {
2110
+ const handleDragLeave = useCallback4(() => {
1554
2111
  setDragOverTabId(null);
1555
2112
  }, []);
1556
- return /* @__PURE__ */ jsxs4("div", {
2113
+ return /* @__PURE__ */ jsxs6("div", {
1557
2114
  className,
1558
2115
  style: {
1559
2116
  display: "flex",
@@ -1563,7 +2120,7 @@ var TabBar = ({
1563
2120
  boxSizing: "border-box"
1564
2121
  },
1565
2122
  children: [
1566
- leftSection && /* @__PURE__ */ jsx5("div", {
2123
+ leftSection && /* @__PURE__ */ jsx8("div", {
1567
2124
  style: {
1568
2125
  display: "flex",
1569
2126
  alignItems: "center",
@@ -1576,7 +2133,7 @@ var TabBar = ({
1576
2133
  },
1577
2134
  children: leftSection
1578
2135
  }),
1579
- /* @__PURE__ */ jsx5("div", {
2136
+ /* @__PURE__ */ jsx8("div", {
1580
2137
  style: {
1581
2138
  display: "flex",
1582
2139
  alignItems: "center",
@@ -1588,7 +2145,7 @@ var TabBar = ({
1588
2145
  },
1589
2146
  children: tabs.map((tab, index) => {
1590
2147
  const canDrop = enableDragAndDrop && draggedTabId !== null && draggedTabId !== tab.id ? canDropOnTab ? canDropOnTab(draggedTabId, tab.id) : true : false;
1591
- return /* @__PURE__ */ jsx5(TabButton, {
2148
+ return /* @__PURE__ */ jsx8(TabButton, {
1592
2149
  tab,
1593
2150
  isActive: tab.id === activeTabId,
1594
2151
  isHovered: tab.id === hoveredTabId,
@@ -1612,7 +2169,7 @@ var TabBar = ({
1612
2169
  }, tab.id);
1613
2170
  })
1614
2171
  }),
1615
- rightSection !== undefined ? rightSection && /* @__PURE__ */ jsx5("div", {
2172
+ rightSection !== undefined ? rightSection && /* @__PURE__ */ jsx8("div", {
1616
2173
  style: {
1617
2174
  display: "flex",
1618
2175
  alignItems: "center",
@@ -1624,7 +2181,7 @@ var TabBar = ({
1624
2181
  boxSizing: "border-box"
1625
2182
  },
1626
2183
  children: rightSection
1627
- }) : onNewTab && /* @__PURE__ */ jsx5("div", {
2184
+ }) : onNewTab && /* @__PURE__ */ jsx8("div", {
1628
2185
  style: {
1629
2186
  display: "flex",
1630
2187
  alignItems: "center",
@@ -1635,7 +2192,7 @@ var TabBar = ({
1635
2192
  borderBottom: `1px solid ${theme.colors.border}`,
1636
2193
  boxSizing: "border-box"
1637
2194
  },
1638
- children: /* @__PURE__ */ jsx5("button", {
2195
+ children: /* @__PURE__ */ jsx8("button", {
1639
2196
  onClick: onNewTab,
1640
2197
  style: {
1641
2198
  display: "flex",
@@ -1656,7 +2213,7 @@ var TabBar = ({
1656
2213
  e.currentTarget.style.backgroundColor = "transparent";
1657
2214
  },
1658
2215
  title: "New Tab (⌘T)",
1659
- children: /* @__PURE__ */ jsx5(Plus, {
2216
+ children: /* @__PURE__ */ jsx8(Plus, {
1660
2217
  size: 14
1661
2218
  })
1662
2219
  })
@@ -1670,14 +2227,14 @@ function isTabOfType(tab, contentType) {
1670
2227
  return tab.contentType === contentType;
1671
2228
  }
1672
2229
  // src/hooks/useTabKeyboardShortcuts.ts
1673
- import { useEffect as useEffect2 } from "react";
2230
+ import { useEffect as useEffect3 } from "react";
1674
2231
  var useTabKeyboardShortcuts = ({
1675
2232
  onNewTab,
1676
2233
  onCloseTab,
1677
2234
  onSwitchToTab,
1678
2235
  enabled = true
1679
2236
  }) => {
1680
- useEffect2(() => {
2237
+ useEffect3(() => {
1681
2238
  if (!enabled)
1682
2239
  return;
1683
2240
  const handleKeyDown = (e) => {
@@ -1698,358 +2255,29 @@ var useTabKeyboardShortcuts = ({
1698
2255
  e.preventDefault();
1699
2256
  e.stopPropagation();
1700
2257
  const index = parseInt(e.key, 10) - 1;
1701
- onSwitchToTab(index);
1702
- return;
1703
- }
1704
- };
1705
- window.addEventListener("keydown", handleKeyDown);
1706
- return () => window.removeEventListener("keydown", handleKeyDown);
1707
- }, [enabled, onNewTab, onCloseTab, onSwitchToTab]);
1708
- };
1709
- // src/panels/TerminalPanel.tsx
1710
- import { useTheme as useTheme5 } from "@principal-ade/industry-theme";
1711
- import { Lock, Unlock, ArrowDown } from "lucide-react";
1712
- import { useCallback as useCallback4, useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
1713
-
1714
- // src/panel-types/index.ts
1715
- function getTerminalSessions(context2) {
1716
- return context2.terminal?.data ?? [];
1717
- }
1718
- function getTerminalSession(context2, sessionId) {
1719
- const sessions = getTerminalSessions(context2);
1720
- return sessions.find((s) => s.id === sessionId);
1721
- }
1722
- function isTerminalLoading(context2) {
1723
- return context2.terminal?.loading ?? false;
1724
- }
1725
- function getRepositoryPath(context2) {
1726
- return context2.currentScope.repository?.path ?? null;
1727
- }
1728
- function getWorkspacePath(context2) {
1729
- return context2.currentScope.workspace?.path ?? null;
1730
- }
1731
- function getTerminalDirectory(context2, terminalScope = "repository") {
1732
- switch (terminalScope) {
1733
- case "workspace":
1734
- return getWorkspacePath(context2);
1735
- case "repository":
1736
- return getRepositoryPath(context2) ?? getWorkspacePath(context2);
1737
- default:
1738
- return getRepositoryPath(context2) ?? getWorkspacePath(context2);
1739
- }
1740
- }
1741
- function getTerminalSlice(context2) {
1742
- return context2.terminal;
1743
- }
1744
-
1745
- // src/panels/TerminalPanel.tsx
1746
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1747
- var TerminalPanel = ({
1748
- context: context2,
1749
- actions,
1750
- events: _events,
1751
- terminalScope = "repository",
1752
- allowTransparency = false,
1753
- backgroundColor
1754
- }) => {
1755
- const { theme } = useTheme5();
1756
- const [sessionId, setSessionId] = useState3(null);
1757
- const [error, setError] = useState3(null);
1758
- const [isInitializing, setIsInitializing] = useState3(true);
1759
- const [scrollPosition, setScrollPosition] = useState3({
1760
- isAtTop: false,
1761
- isAtBottom: true,
1762
- isScrollLocked: false
1763
- });
1764
- const terminalRef = useRef2(null);
1765
- const isScrollLockedRef = useRef2(scrollPosition.isScrollLocked);
1766
- const terminalDirectory = getTerminalDirectory(context2, terminalScope);
1767
- const pendingSessionIdRef = useRef2(null);
1768
- useEffect3(() => {
1769
- let mounted = true;
1770
- const initTerminal = async () => {
1771
- try {
1772
- if (!actions.createTerminalSession) {
1773
- throw new Error("Terminal actions not available. Host must provide createTerminalSession action.");
1774
- }
1775
- const id = await actions.createTerminalSession({
1776
- cwd: terminalDirectory || undefined
1777
- });
1778
- pendingSessionIdRef.current = id;
1779
- if (actions.claimTerminalOwnership) {
1780
- await actions.claimTerminalOwnership(id);
1781
- }
1782
- if (mounted) {
1783
- setSessionId(id);
1784
- setIsInitializing(false);
1785
- }
1786
- } catch (err) {
1787
- if (mounted) {
1788
- setError(err instanceof Error ? err.message : String(err));
1789
- setIsInitializing(false);
1790
- }
1791
- }
1792
- };
1793
- initTerminal();
1794
- return () => {
1795
- mounted = false;
1796
- if (pendingSessionIdRef.current && actions.destroyTerminalSession) {
1797
- actions.destroyTerminalSession(pendingSessionIdRef.current);
1798
- }
1799
- };
1800
- }, []);
1801
- useEffect3(() => {
1802
- if (!sessionId)
1803
- return;
1804
- if (!actions.onTerminalData) {
1805
- setError("Host does not provide onTerminalData. Terminal data cannot be displayed.");
1806
- return;
1807
- }
1808
- let cursorMovedToHome = false;
1809
- let lastWriteTime = 0;
1810
- let autoScrollTimeout = null;
1811
- const unsubscribe = actions.onTerminalData(sessionId, (data) => {
1812
- if (!terminalRef.current)
1813
- return;
1814
- if (data.includes("\x1B[H")) {
1815
- cursorMovedToHome = true;
1816
- lastWriteTime = Date.now();
1817
- }
1818
- terminalRef.current.write(data);
1819
- if (cursorMovedToHome && isScrollLockedRef.current) {
1820
- if (autoScrollTimeout)
1821
- clearTimeout(autoScrollTimeout);
1822
- autoScrollTimeout = setTimeout(() => {
1823
- const timeSinceLastWrite = Date.now() - lastWriteTime;
1824
- if (timeSinceLastWrite >= 100 && cursorMovedToHome && terminalRef.current) {
1825
- terminalRef.current.write("\x1B[9999;1H");
1826
- cursorMovedToHome = false;
1827
- }
1828
- }, 100);
1829
- }
1830
- lastWriteTime = Date.now();
1831
- });
1832
- return () => {
1833
- if (autoScrollTimeout)
1834
- clearTimeout(autoScrollTimeout);
1835
- unsubscribe();
1836
- };
1837
- }, [sessionId, actions.onTerminalData]);
1838
- const handleTerminalData = useCallback4((data) => {
1839
- if (sessionId && actions.writeToTerminal) {
1840
- actions.writeToTerminal(sessionId, data);
1841
- }
1842
- }, [sessionId, actions.writeToTerminal]);
1843
- const hasNotifiedPtyRef = useRef2(false);
1844
- const handleTerminalResize = useCallback4((cols, rows) => {
1845
- if (sessionId && actions.resizeTerminal) {
1846
- const isInitialNotification = !hasNotifiedPtyRef.current;
1847
- actions.resizeTerminal(sessionId, cols, rows);
1848
- if (isInitialNotification && actions.writeToTerminal) {
1849
- setTimeout(() => {
1850
- actions.writeToTerminal(sessionId, "\f");
1851
- }, 50);
1852
- }
1853
- hasNotifiedPtyRef.current = true;
1854
- }
1855
- }, [sessionId, actions.resizeTerminal, actions.writeToTerminal]);
1856
- useEffect3(() => {
1857
- hasNotifiedPtyRef.current = false;
1858
- }, [sessionId]);
1859
- useEffect3(() => {
1860
- if (!sessionId || !actions.resizeTerminal)
1861
- return;
1862
- if (hasNotifiedPtyRef.current)
1863
- return;
1864
- const term = terminalRef.current?.getTerminal();
1865
- if (!term || !term.cols || !term.rows)
1866
- return;
1867
- actions.resizeTerminal(sessionId, term.cols, term.rows);
1868
- hasNotifiedPtyRef.current = true;
1869
- if (actions.writeToTerminal) {
1870
- const writeFn = actions.writeToTerminal;
1871
- setTimeout(() => writeFn(sessionId, "\f"), 50);
1872
- }
1873
- }, [sessionId, actions.resizeTerminal, actions.writeToTerminal]);
1874
- const handleScrollPositionChange = useCallback4((position) => {
1875
- setScrollPosition(position);
1876
- isScrollLockedRef.current = position.isScrollLocked;
1877
- }, []);
1878
- const sessionInfo = sessionId ? getTerminalSession(context2, sessionId) : undefined;
1879
- if (error) {
1880
- return /* @__PURE__ */ jsxs5("div", {
1881
- style: {
1882
- padding: "20px",
1883
- color: "#ef4444",
1884
- backgroundColor: allowTransparency ? "transparent" : "#1a1a1a",
1885
- height: "100%",
1886
- display: "flex",
1887
- alignItems: "center",
1888
- justifyContent: "center",
1889
- flexDirection: "column",
1890
- gap: "10px"
1891
- },
1892
- children: [
1893
- /* @__PURE__ */ jsx6("div", {
1894
- style: { fontSize: "16px", fontWeight: "bold" },
1895
- children: "Terminal Error"
1896
- }),
1897
- /* @__PURE__ */ jsx6("div", {
1898
- style: { fontSize: "14px", opacity: 0.8 },
1899
- children: error
1900
- })
1901
- ]
1902
- });
1903
- }
1904
- if (isInitializing || !sessionId) {
1905
- return /* @__PURE__ */ jsx6("div", {
1906
- style: {
1907
- padding: "20px",
1908
- color: "#a0a0a0",
1909
- backgroundColor: allowTransparency ? "transparent" : "#1a1a1a",
1910
- height: "100%",
1911
- display: "flex",
1912
- alignItems: "center",
1913
- justifyContent: "center"
1914
- },
1915
- children: "Initializing terminal..."
1916
- });
1917
- }
1918
- const handleScrollToBottom = () => {
1919
- terminalRef.current?.scrollToBottom();
1920
- };
1921
- const handleToggleScrollLock = () => {
1922
- if (scrollPosition.isScrollLocked) {
1923
- const terminal = terminalRef.current?.getTerminal();
1924
- if (terminal) {
1925
- terminal.scrollLines(-1);
1926
- }
1927
- } else {
1928
- terminalRef.current?.scrollToBottom();
1929
- }
1930
- };
1931
- return /* @__PURE__ */ jsxs5("div", {
1932
- style: { height: "100%", width: "100%", display: "flex", flexDirection: "column" },
1933
- children: [
1934
- /* @__PURE__ */ jsxs5("div", {
1935
- style: {
1936
- display: "flex",
1937
- gap: "8px",
1938
- padding: "8px 12px",
1939
- backgroundColor: allowTransparency ? "transparent" : theme.colors.backgroundSecondary,
1940
- borderBottom: `1px solid ${theme.colors.border}`,
1941
- alignItems: "center"
1942
- },
1943
- children: [
1944
- /* @__PURE__ */ jsxs5("span", {
1945
- style: {
1946
- fontSize: "12px",
1947
- color: theme.colors.textSecondary,
1948
- marginRight: "auto",
1949
- fontFamily: theme.fonts.monospace
1950
- },
1951
- children: [
1952
- sessionInfo?.cwd || "Terminal",
1953
- " • ",
1954
- sessionInfo?.shell
1955
- ]
1956
- }),
1957
- /* @__PURE__ */ jsxs5("button", {
1958
- onClick: handleToggleScrollLock,
1959
- style: {
1960
- display: "flex",
1961
- alignItems: "center",
1962
- gap: "4px",
1963
- fontSize: "11px",
1964
- padding: "4px 8px",
1965
- borderRadius: "4px",
1966
- backgroundColor: scrollPosition.isScrollLocked ? `${theme.colors.success}22` : `${theme.colors.warning}22`,
1967
- color: scrollPosition.isScrollLocked ? theme.colors.success : theme.colors.warning,
1968
- border: `1px solid ${scrollPosition.isScrollLocked ? `${theme.colors.success}44` : `${theme.colors.warning}44`}`,
1969
- cursor: "pointer",
1970
- transition: "opacity 0.2s"
1971
- },
1972
- onMouseEnter: (e) => e.currentTarget.style.opacity = "0.8",
1973
- onMouseLeave: (e) => e.currentTarget.style.opacity = "1",
1974
- title: scrollPosition.isScrollLocked ? "Click to unlock scroll" : "Click to lock scroll to bottom",
1975
- children: [
1976
- scrollPosition.isScrollLocked ? /* @__PURE__ */ jsx6(Lock, {
1977
- size: 12
1978
- }) : /* @__PURE__ */ jsx6(Unlock, {
1979
- size: 12
1980
- }),
1981
- /* @__PURE__ */ jsx6("span", {
1982
- children: scrollPosition.isScrollLocked ? "Locked" : "Unlocked"
1983
- })
1984
- ]
1985
- }),
1986
- /* @__PURE__ */ jsxs5("button", {
1987
- onClick: handleScrollToBottom,
1988
- disabled: scrollPosition.isAtBottom,
1989
- style: {
1990
- display: "flex",
1991
- alignItems: "center",
1992
- gap: "4px",
1993
- fontSize: "11px",
1994
- padding: "4px 10px",
1995
- borderRadius: "4px",
1996
- backgroundColor: scrollPosition.isAtBottom ? theme.colors.backgroundHover : theme.colors.accent,
1997
- color: scrollPosition.isAtBottom ? theme.colors.textTertiary : theme.colors.text,
1998
- border: `1px solid ${theme.colors.border}`,
1999
- cursor: scrollPosition.isAtBottom ? "not-allowed" : "pointer",
2000
- transition: "opacity 0.2s",
2001
- opacity: scrollPosition.isAtBottom ? 0.5 : 1
2002
- },
2003
- onMouseEnter: (e) => !scrollPosition.isAtBottom && (e.currentTarget.style.opacity = "0.8"),
2004
- onMouseLeave: (e) => !scrollPosition.isAtBottom && (e.currentTarget.style.opacity = "1"),
2005
- title: "Scroll to bottom and lock",
2006
- children: [
2007
- /* @__PURE__ */ jsx6(ArrowDown, {
2008
- size: 12
2009
- }),
2010
- /* @__PURE__ */ jsx6("span", {
2011
- children: "Bottom"
2012
- })
2013
- ]
2014
- })
2015
- ]
2016
- }),
2017
- /* @__PURE__ */ jsx6("div", {
2018
- style: { flex: 1 },
2019
- children: /* @__PURE__ */ jsx6(ThemedTerminalWithProvider, {
2020
- ref: terminalRef,
2021
- onData: handleTerminalData,
2022
- onResize: handleTerminalResize,
2023
- onScrollPositionChange: handleScrollPositionChange,
2024
- hideHeader: true,
2025
- autoFocus: true,
2026
- convertEol: true,
2027
- cursorBlink: true,
2028
- scrollback: 1e4,
2029
- enableSearch: true,
2030
- enableWebLinks: true,
2031
- allowTransparency,
2032
- backgroundColor
2033
- })
2034
- })
2035
- ]
2036
- });
2258
+ onSwitchToTab(index);
2259
+ return;
2260
+ }
2261
+ };
2262
+ window.addEventListener("keydown", handleKeyDown);
2263
+ return () => window.removeEventListener("keydown", handleKeyDown);
2264
+ }, [enabled, onNewTab, onCloseTab, onSwitchToTab]);
2037
2265
  };
2038
2266
  // src/panels/TabbedTerminalPanel.tsx
2039
2267
  import { useTheme as useTheme6 } from "@principal-ade/industry-theme";
2040
2268
  import { CollapsibleSplitPane } from "@principal-ade/panels";
2041
- import { Terminal as TerminalIcon2, Lock as Lock2, Unlock as Unlock2, Box, Boxes, Paperclip } from "lucide-react";
2269
+ import { Terminal as TerminalIcon2, Lock, Unlock, Box, Boxes, Paperclip } from "lucide-react";
2042
2270
  import React3, {
2043
2271
  useState as useState4,
2044
2272
  useCallback as useCallback5,
2045
2273
  useEffect as useEffect4,
2046
- useRef as useRef3
2274
+ useRef as useRef4
2047
2275
  } from "react";
2048
- import { jsx as jsx7, jsxs as jsxs6, Fragment as Fragment2 } from "react/jsx-runtime";
2049
- var ActivityIndicator = ({ color, isAnimating }) => /* @__PURE__ */ jsxs6("div", {
2276
+ import { jsx as jsx9, jsxs as jsxs7, Fragment as Fragment2 } from "react/jsx-runtime";
2277
+ var ActivityIndicator = ({ color, isAnimating }) => /* @__PURE__ */ jsxs7("div", {
2050
2278
  style: { display: "flex", gap: 1, alignItems: "center", height: 12 },
2051
2279
  children: [
2052
- [0, 1, 2, 3, 4].map((i) => /* @__PURE__ */ jsx7("div", {
2280
+ [0, 1, 2, 3, 4].map((i) => /* @__PURE__ */ jsx9("div", {
2053
2281
  style: {
2054
2282
  width: 2,
2055
2283
  height: 10,
@@ -2060,7 +2288,7 @@ var ActivityIndicator = ({ color, isAnimating }) => /* @__PURE__ */ jsxs6("div",
2060
2288
  animation: isAnimating ? `waveSine 1.2s ease-in-out ${i * 0.1}s infinite` : "none"
2061
2289
  }
2062
2290
  }, i)),
2063
- /* @__PURE__ */ jsx7("style", {
2291
+ /* @__PURE__ */ jsx9("style", {
2064
2292
  children: `
2065
2293
  @keyframes waveSine {
2066
2294
  0%, 100% { transform: scaleY(0.4); }
@@ -2071,376 +2299,69 @@ var ActivityIndicator = ({ color, isAnimating }) => /* @__PURE__ */ jsxs6("div",
2071
2299
  ]
2072
2300
  });
2073
2301
  function TerminalTabContentInner(props, ref) {
2074
- const { tab, sessionId, isActive, isVisible, actions, events, terminalContext, onSessionCreated, onScrollPositionChange, isForeign = false, defaultScrollLocked, activityDetection = true, activityTimeout = 500, autoShowBlinds = false, onActivityStateChange } = props;
2075
- const { theme } = useTheme6();
2076
- const terminalRef = useRef3(null);
2077
- const [localSessionId, setLocalSessionId] = useState4(sessionId);
2078
- const [isInitialized, setIsInitialized] = useState4(false);
2079
- const hasInitializedRef = useRef3(false);
2080
- const [scrollPosition, setScrollPosition] = useState4({
2081
- isAtTop: false,
2082
- isAtBottom: true,
2083
- isScrollLocked: false
2084
- });
2085
- const [shouldRenderTerminal, setShouldRenderTerminal] = useState4(true);
2086
- const [ownerWindowId, setOwnerWindowId] = useState4(null);
2087
- const needsRefreshOnResizeRef = useRef3(false);
2088
- const claimAndConnect = useCallback5(async (targetSessionId, force = false) => {
2089
- try {
2090
- if (actions.claimTerminalOwnership) {
2091
- await actions.claimTerminalOwnership(targetSessionId, force);
2092
- }
2093
- if (force && actions.requestTerminalDataPort) {
2094
- await actions.requestTerminalDataPort(targetSessionId);
2095
- }
2096
- setShouldRenderTerminal(true);
2097
- setOwnerWindowId(null);
2098
- if (force) {
2099
- needsRefreshOnResizeRef.current = true;
2100
- }
2101
- } catch (error) {
2102
- console.error("[TerminalTabContent] Failed to claim ownership:", error);
2103
- throw error;
2104
- }
2105
- }, [actions]);
2106
- useEffect4(() => {
2107
- if (hasInitializedRef.current) {
2108
- return;
2109
- }
2110
- hasInitializedRef.current = true;
2111
- let mounted = true;
2112
- const initSession = async () => {
2113
- try {
2114
- if (sessionId) {
2115
- const tracer2 = getTracer();
2116
- const reconnectSpan = tracer2.startSpan("terminal.session.reconnect", {
2117
- attributes: {
2118
- "session.id": sessionId,
2119
- "session.context": `${terminalContext}:${tab.id}`,
2120
- "session.isForeign": isForeign
2121
- }
2122
- });
2123
- reconnectSpan.addEvent("terminal.session.reconnecting", {
2124
- "session.id": sessionId,
2125
- "reconnect.reason": "component_remount"
2126
- });
2127
- if (isForeign && actions.checkTerminalOwnership) {
2128
- const status = await actions.checkTerminalOwnership(sessionId);
2129
- if (status.ownedByWindowId && !status.ownedByThisWindow) {
2130
- setShouldRenderTerminal(false);
2131
- setOwnerWindowId(status.ownedByWindowId);
2132
- reconnectSpan.addEvent("terminal.session.reconnect_blocked", {
2133
- "session.id": sessionId,
2134
- "owner.windowId": status.ownedByWindowId
2135
- });
2136
- }
2137
- } else {
2138
- await claimAndConnect(sessionId);
2139
- reconnectSpan.addEvent("terminal.session.reconnected", {
2140
- "session.id": sessionId
2141
- });
2142
- }
2143
- reconnectSpan.setStatus({ code: SpanStatusCode.OK });
2144
- reconnectSpan.end();
2145
- setLocalSessionId(sessionId);
2146
- setIsInitialized(true);
2147
- return;
2148
- }
2149
- if (!actions.createTerminalSession) {
2150
- return;
2151
- }
2152
- const newSessionId = await actions.createTerminalSession({
2153
- cwd: tab.directory,
2154
- command: tab.command,
2155
- context: `${terminalContext}:${tab.id}`
2156
- });
2157
- if (!mounted)
2158
- return;
2159
- const tracer = getTracer();
2160
- const span = tracer.startSpan("terminal.session", {
2161
- attributes: {
2162
- "session.id": newSessionId,
2163
- "session.cwd": tab.directory,
2164
- "session.context": `${terminalContext}:${tab.id}`
2165
- }
2166
- });
2167
- span.addEvent("terminal.session.created", {
2168
- "session.id": newSessionId,
2169
- "session.cwd": tab.directory
2170
- });
2171
- span.setStatus({ code: SpanStatusCode.OK });
2172
- span.end();
2173
- setLocalSessionId(newSessionId);
2174
- setIsInitialized(true);
2175
- onSessionCreated(tab.id, newSessionId);
2176
- await claimAndConnect(newSessionId);
2177
- } catch (error) {
2178
- console.error("[TerminalTabContent] Failed to create session:", error);
2179
- const tracer = getTracer();
2180
- const span = tracer.startSpan("terminal.session", {
2181
- attributes: {
2182
- "session.action": "create",
2183
- "error.occurred": true
2184
- }
2185
- });
2186
- span.addEvent("terminal.session.error", {
2187
- "error.message": error instanceof Error ? error.message : "Unknown error"
2188
- });
2189
- span.recordException(error);
2190
- span.setStatus({ code: SpanStatusCode.ERROR, message: "Session creation failed" });
2191
- span.end();
2192
- }
2193
- };
2194
- initSession();
2195
- return () => {
2196
- mounted = false;
2197
- };
2198
- }, []);
2199
- useEffect4(() => {
2200
- if (!localSessionId || !isInitialized || !shouldRenderTerminal) {
2201
- return;
2202
- }
2203
- if (!actions.onTerminalData) {
2204
- return;
2205
- }
2206
- const tracer = getTracer();
2207
- const subscribeSpan = tracer.startSpan("terminal.data.subscribe", {
2208
- attributes: {
2209
- "session.id": localSessionId,
2210
- "tab.id": tab.id
2211
- }
2212
- });
2213
- subscribeSpan.addEvent("terminal.data.subscribed", {
2214
- "session.id": localSessionId
2215
- });
2216
- console.log("[TerminalTabContent] SUBSCRIBING to terminal data", { tabId: tab.id, sessionId: localSessionId });
2217
- let hasReceivedData = false;
2218
- const unsubscribe = actions.onTerminalData(localSessionId, (data) => {
2219
- if (terminalRef.current) {
2220
- if (!hasReceivedData) {
2221
- hasReceivedData = true;
2222
- subscribeSpan.addEvent("terminal.data.first_received", {
2223
- "session.id": localSessionId,
2224
- "data.length": data.length
2225
- });
2226
- }
2227
- terminalRef.current.write(data);
2228
- }
2229
- });
2230
- subscribeSpan.setStatus({ code: SpanStatusCode.OK });
2231
- subscribeSpan.end();
2232
- return () => {
2233
- const unsubscribeSpan = tracer.startSpan("terminal.data.unsubscribe", {
2234
- attributes: {
2235
- "session.id": localSessionId,
2236
- "tab.id": tab.id,
2237
- "data.wasReceived": hasReceivedData
2238
- }
2239
- });
2240
- unsubscribeSpan.addEvent("terminal.data.unsubscribed", {
2241
- "session.id": localSessionId
2242
- });
2243
- unsubscribeSpan.setStatus({ code: SpanStatusCode.OK });
2244
- unsubscribeSpan.end();
2245
- console.log("[TerminalTabContent] UNSUBSCRIBING from terminal data", { tabId: tab.id, sessionId: localSessionId });
2246
- unsubscribe();
2247
- };
2248
- }, [localSessionId, isInitialized, actions, shouldRenderTerminal, tab.id]);
2249
- const handleData = useCallback5((data) => {
2250
- if (localSessionId && actions.writeToTerminal) {
2251
- actions.writeToTerminal(localSessionId, data);
2252
- }
2253
- }, [localSessionId, actions]);
2254
- const handleResize = useCallback5((cols, rows) => {
2255
- if (localSessionId && actions.resizeTerminal) {
2256
- actions.resizeTerminal(localSessionId, cols, rows);
2257
- }
2258
- }, [localSessionId, actions]);
2259
- const handleReady = useCallback5((cols, rows) => {
2260
- if (!localSessionId || !actions.resizeTerminal) {
2261
- return;
2262
- }
2263
- const shouldForce = needsRefreshOnResizeRef.current;
2264
- if (!shouldForce) {
2265
- actions.resizeTerminal(localSessionId, cols, rows, false);
2266
- return;
2267
- }
2268
- needsRefreshOnResizeRef.current = false;
2269
- const restoreBufferAndResize = async () => {
2270
- let bufferRestored = false;
2271
- if (actions.getTerminalBuffer && terminalRef.current) {
2272
- try {
2273
- const buffer = await actions.getTerminalBuffer(localSessionId);
2274
- if (buffer) {
2275
- terminalRef.current.write(buffer);
2276
- bufferRestored = true;
2277
- }
2278
- } catch (error) {
2279
- console.warn("[TabbedTerminalPanel] Failed to restore buffer:", error);
2280
- }
2281
- }
2282
- const forceResize = !bufferRestored;
2283
- actions.resizeTerminal(localSessionId, cols, rows, forceResize);
2284
- };
2285
- restoreBufferAndResize();
2286
- }, [localSessionId, actions]);
2287
- const handleLinkClick = useCallback5((url, modifiers) => {
2288
- if (localSessionId) {
2289
- events.emit({
2290
- type: "terminal:link-click",
2291
- source: "TabbedTerminalPanel",
2292
- timestamp: Date.now(),
2293
- payload: {
2294
- url,
2295
- sessionId: localSessionId,
2296
- shiftKey: modifiers.shiftKey,
2297
- metaKey: modifiers.metaKey,
2298
- ctrlKey: modifiers.ctrlKey,
2299
- altKey: modifiers.altKey
2300
- }
2301
- });
2302
- }
2303
- }, [localSessionId, events]);
2304
- useEffect4(() => {
2305
- if (!localSessionId || !actions.onOwnershipLost) {
2306
- return;
2307
- }
2308
- const unsubscribe = actions.onOwnershipLost((data) => {
2309
- if (data.sessionId === localSessionId) {
2310
- setShouldRenderTerminal(false);
2311
- setOwnerWindowId(data.newOwnerWindowId);
2312
- }
2313
- });
2314
- return () => {
2315
- unsubscribe();
2316
- };
2317
- }, [localSessionId, actions]);
2318
- const handleTakeControl = useCallback5(async () => {
2319
- if (!localSessionId) {
2320
- return;
2321
- }
2322
- await claimAndConnect(localSessionId, true);
2323
- }, [localSessionId, claimAndConnect]);
2302
+ const {
2303
+ tab,
2304
+ sessionId,
2305
+ isActive,
2306
+ isVisible,
2307
+ actions,
2308
+ events,
2309
+ terminalContext,
2310
+ onSessionCreated,
2311
+ onScrollPositionChange,
2312
+ isForeign = false,
2313
+ defaultScrollLocked,
2314
+ activityDetection = true,
2315
+ activityTimeout = 500,
2316
+ autoShowBlinds = false,
2317
+ onActivityStateChange
2318
+ } = props;
2319
+ const [isWorkingFromActivity, setIsWorkingFromActivity] = useState4(false);
2320
+ const sessionRef = useRef4(null);
2321
+ const handleSessionCreated = useCallback5((sid) => onSessionCreated(tab.id, sid), [onSessionCreated, tab.id]);
2324
2322
  const handleScrollPositionChange = useCallback5((position) => {
2325
- setScrollPosition(position);
2326
2323
  onScrollPositionChange?.(tab.id, position);
2327
- }, [tab.id, onScrollPositionChange]);
2328
- const handleShortcut = useCallback5((shortcutEvent) => {
2329
- if (localSessionId) {
2330
- events.emit({
2331
- type: "terminal:shortcut",
2332
- source: "TabbedTerminalPanel",
2333
- timestamp: Date.now(),
2334
- payload: {
2335
- shortcut: shortcutEvent.shortcut,
2336
- sessionId: localSessionId
2337
- }
2338
- });
2339
- }
2340
- }, [localSessionId, events]);
2324
+ }, [onScrollPositionChange, tab.id]);
2341
2325
  const handleActivityChange = useCallback5((state) => {
2342
- if (localSessionId) {
2343
- onActivityStateChange?.(localSessionId, state.isActive);
2344
- events.emit({
2345
- type: "terminal:activity-changed",
2346
- source: "TabbedTerminalPanel",
2347
- timestamp: Date.now(),
2348
- payload: {
2349
- sessionId: localSessionId,
2350
- activityType: state.isActive ? "started" : "stopped",
2351
- isWorking: state.isActive
2352
- }
2353
- });
2326
+ const sid = sessionRef.current?.getSessionId();
2327
+ if (sid) {
2328
+ onActivityStateChange?.(sid, state.isActive);
2354
2329
  }
2355
- }, [localSessionId, events, onActivityStateChange]);
2356
- const handleScrollToBottom = useCallback5(() => {
2357
- terminalRef.current?.scrollToBottom();
2358
- }, []);
2359
- const handleToggleScrollLock = useCallback5(() => {
2360
- if (scrollPosition.isScrollLocked) {
2361
- const terminal = terminalRef.current?.getTerminal();
2362
- if (terminal) {
2363
- terminal.scrollLines(-1);
2364
- }
2365
- } else {
2366
- terminalRef.current?.scrollToBottom();
2330
+ if (autoShowBlinds) {
2331
+ setIsWorkingFromActivity(state.isActive);
2367
2332
  }
2368
- }, [scrollPosition.isScrollLocked]);
2333
+ }, [autoShowBlinds, onActivityStateChange]);
2369
2334
  React3.useImperativeHandle(ref, () => ({
2370
- scrollToBottom: handleScrollToBottom,
2371
- toggleScrollLock: handleToggleScrollLock
2372
- }), [handleScrollToBottom, handleToggleScrollLock]);
2373
- if (!isInitialized) {
2374
- return /* @__PURE__ */ jsxs6("div", {
2375
- style: {
2376
- display: isActive ? "flex" : "none",
2377
- height: "100%",
2378
- width: "100%",
2379
- backgroundColor: theme.colors.background,
2380
- padding: "10px 0 0 10px"
2381
- },
2382
- children: [
2383
- /* @__PURE__ */ jsx7("div", {
2384
- style: {
2385
- width: "8px",
2386
- height: "17px",
2387
- backgroundColor: theme.colors.text,
2388
- animation: "blink 1s step-end infinite"
2389
- }
2390
- }),
2391
- /* @__PURE__ */ jsx7("style", {
2392
- children: `
2393
- @keyframes blink {
2394
- 0%, 100% { opacity: 1; }
2395
- 50% { opacity: 0; }
2396
- }
2397
- `
2398
- })
2399
- ]
2400
- });
2401
- }
2402
- const overlayState = !shouldRenderTerminal ? {
2403
- message: "This terminal is active in another window",
2404
- subtitle: ownerWindowId ? `Window ID: ${ownerWindowId}` : "Another window owns this terminal session",
2405
- actions: [
2406
- {
2407
- label: "Take Control",
2408
- onClick: handleTakeControl,
2409
- primary: true
2410
- }
2411
- ],
2412
- opacity: 1
2413
- } : undefined;
2414
- return /* @__PURE__ */ jsx7("div", {
2335
+ scrollToBottom: () => sessionRef.current?.scrollToBottom(),
2336
+ toggleScrollLock: () => sessionRef.current?.toggleScrollLock()
2337
+ }), []);
2338
+ return /* @__PURE__ */ jsx9("div", {
2415
2339
  style: {
2416
2340
  display: isActive ? "flex" : "none",
2417
2341
  flexDirection: "column",
2418
2342
  height: "100%",
2419
2343
  width: "100%"
2420
2344
  },
2421
- children: /* @__PURE__ */ jsx7(ThemedTerminalWithProvider, {
2422
- ref: terminalRef,
2423
- onData: shouldRenderTerminal ? handleData : undefined,
2424
- onResize: shouldRenderTerminal ? handleResize : undefined,
2425
- onReady: shouldRenderTerminal ? handleReady : undefined,
2426
- onLinkClick: shouldRenderTerminal ? handleLinkClick : undefined,
2427
- onScrollPositionChange: shouldRenderTerminal ? handleScrollPositionChange : undefined,
2428
- onShortcut: handleShortcut,
2429
- hideHeader: true,
2430
- autoFocus: isActive && shouldRenderTerminal,
2431
- isVisible: isVisible && isActive,
2432
- defaultScrollLocked,
2433
- convertEol: true,
2434
- cursorBlink: shouldRenderTerminal,
2435
- scrollback: 1e4,
2436
- enableSearch: true,
2345
+ children: /* @__PURE__ */ jsx9(TerminalSession, {
2346
+ ref: sessionRef,
2347
+ actions,
2348
+ events,
2349
+ sessionId,
2350
+ onSessionCreated: handleSessionCreated,
2351
+ cwd: tab.directory,
2352
+ command: tab.command,
2353
+ sessionContext: `${terminalContext}:${tab.id}`,
2354
+ enableOwnership: true,
2355
+ isForeign,
2437
2356
  activityDetection,
2438
2357
  activityTimeout,
2439
- autoShowBlinds,
2440
- onActivityChange: shouldRenderTerminal ? handleActivityChange : undefined,
2441
- enableWebLinks: true,
2442
- overlayState
2443
- }, shouldRenderTerminal ? "active" : "overlay")
2358
+ onActivityChange: handleActivityChange,
2359
+ onScrollPositionChange: handleScrollPositionChange,
2360
+ defaultScrollLocked,
2361
+ autoFocus: isActive,
2362
+ isVisible: isVisible && isActive,
2363
+ isWorking: autoShowBlinds && isWorkingFromActivity
2364
+ })
2444
2365
  });
2445
2366
  }
2446
2367
  var areTerminalTabContentPropsEqual = (prevProps, nextProps) => {
@@ -2597,8 +2518,8 @@ var TabbedTerminalPanelInner = ({
2597
2518
  }
2598
2519
  }));
2599
2520
  }, [tabs]);
2600
- const tabRefsMap = useRef3(new Map);
2601
- const refCallbacksMap = useRef3(new Map);
2521
+ const tabRefsMap = useRef4(new Map);
2522
+ const refCallbacksMap = useRef4(new Map);
2602
2523
  const getRefCallback = useCallback5((tabId) => {
2603
2524
  let callback = refCallbacksMap.current.get(tabId);
2604
2525
  if (!callback) {
@@ -2613,9 +2534,9 @@ var TabbedTerminalPanelInner = ({
2613
2534
  }
2614
2535
  return callback;
2615
2536
  }, []);
2616
- const hasInitializedRef = useRef3(false);
2617
- const isCreatingTabRef = useRef3(false);
2618
- const headerRef = useRef3(null);
2537
+ const hasInitializedRef = useRef4(false);
2538
+ const isCreatingTabRef = useRef4(false);
2539
+ const headerRef = useRef4(null);
2619
2540
  const handleTabScrollPositionChange = useCallback5((tabId, position) => {
2620
2541
  setScrollPositions((prev) => new Map(prev).set(tabId, position));
2621
2542
  }, []);
@@ -2888,9 +2809,9 @@ var TabbedTerminalPanelInner = ({
2888
2809
  if (!isActive)
2889
2810
  return null;
2890
2811
  const scrollPosition = scrollPositions.get(tab.id) ?? defaultScrollPosition;
2891
- return /* @__PURE__ */ jsxs6(Fragment2, {
2812
+ return /* @__PURE__ */ jsxs7(Fragment2, {
2892
2813
  children: [
2893
- /* @__PURE__ */ jsx7("button", {
2814
+ /* @__PURE__ */ jsx9("button", {
2894
2815
  onClick: (e) => {
2895
2816
  e.stopPropagation();
2896
2817
  handleToggleScrollLock();
@@ -2915,13 +2836,13 @@ var TabbedTerminalPanelInner = ({
2915
2836
  e.currentTarget.style.backgroundColor = "transparent";
2916
2837
  },
2917
2838
  title: scrollPosition.isScrollLocked ? "Scroll locked" : "Scroll unlocked",
2918
- children: scrollPosition.isScrollLocked ? /* @__PURE__ */ jsx7(Lock2, {
2839
+ children: scrollPosition.isScrollLocked ? /* @__PURE__ */ jsx9(Lock, {
2919
2840
  size: 10
2920
- }) : /* @__PURE__ */ jsx7(Unlock2, {
2841
+ }) : /* @__PURE__ */ jsx9(Unlock, {
2921
2842
  size: 10
2922
2843
  })
2923
2844
  }),
2924
- /* @__PURE__ */ jsx7("div", {
2845
+ /* @__PURE__ */ jsx9("div", {
2925
2846
  style: {
2926
2847
  position: "absolute",
2927
2848
  right: "8px",
@@ -2932,7 +2853,7 @@ var TabbedTerminalPanelInner = ({
2932
2853
  height: "16px"
2933
2854
  },
2934
2855
  title: isWorking ? "Terminal active" : "Terminal idle",
2935
- children: /* @__PURE__ */ jsx7(ActivityIndicator, {
2856
+ children: /* @__PURE__ */ jsx9(ActivityIndicator, {
2936
2857
  color: theme.colors.primary,
2937
2858
  isAnimating: isWorking ?? false
2938
2859
  })
@@ -2941,7 +2862,7 @@ var TabbedTerminalPanelInner = ({
2941
2862
  });
2942
2863
  }, [activeTabId, scrollPositions, defaultScrollPosition, handleToggleScrollLock, theme, sessionIds, workingStates, activityStates]);
2943
2864
  const renderTerminalWithAssociation = useCallback5((tab, isActive, sessionId, association) => {
2944
- const terminalContent = /* @__PURE__ */ jsx7(TerminalTabContent, {
2865
+ const terminalContent = /* @__PURE__ */ jsx9(TerminalTabContent, {
2945
2866
  ref: getRefCallback(tab.id),
2946
2867
  tab,
2947
2868
  sessionId,
@@ -2960,7 +2881,7 @@ var TabbedTerminalPanelInner = ({
2960
2881
  onActivityStateChange: handleActivityStateChange
2961
2882
  }, `terminal-${tab.id}`);
2962
2883
  const hasAssociation = !!association;
2963
- const secondaryContent = hasAssociation && renderAssociatedContent ? renderAssociatedContent(association.associatedTabId, isActive) : /* @__PURE__ */ jsx7("div", {
2884
+ const secondaryContent = hasAssociation && renderAssociatedContent ? renderAssociatedContent(association.associatedTabId, isActive) : /* @__PURE__ */ jsx9("div", {
2964
2885
  style: {
2965
2886
  height: "100%",
2966
2887
  display: "flex",
@@ -2972,12 +2893,12 @@ var TabbedTerminalPanelInner = ({
2972
2893
  },
2973
2894
  children: "Drag a tab here to associate it with this terminal"
2974
2895
  });
2975
- const headerConfig = hasAssociation && getAssociatedHeader ? getAssociatedHeader(association.associatedTabId) : { title: "Drop zone", icon: /* @__PURE__ */ jsx7(Paperclip, {
2896
+ const headerConfig = hasAssociation && getAssociatedHeader ? getAssociatedHeader(association.associatedTabId) : { title: "Drop zone", icon: /* @__PURE__ */ jsx9(Paperclip, {
2976
2897
  size: 14
2977
2898
  }) };
2978
2899
  const isCollapsed = hasAssociation ? association.collapsed : true;
2979
2900
  const handleCollapsedChange = hasAssociation ? (collapsed) => onAssociationCollapsedChange?.(tab.id, collapsed) : undefined;
2980
- return /* @__PURE__ */ jsx7(CollapsibleSplitPane, {
2901
+ return /* @__PURE__ */ jsx9(CollapsibleSplitPane, {
2981
2902
  style: { height: "100%" },
2982
2903
  primaryContent: terminalContent,
2983
2904
  secondaryContent,
@@ -3052,7 +2973,7 @@ var TabbedTerminalPanelInner = ({
3052
2973
  setActiveTabId(activeTabId);
3053
2974
  }
3054
2975
  }, [activeTabId, tabs, onTabAssociate, setActiveTabId]);
3055
- return /* @__PURE__ */ jsxs6("div", {
2976
+ return /* @__PURE__ */ jsxs7("div", {
3056
2977
  style: {
3057
2978
  display: "flex",
3058
2979
  flexDirection: "column",
@@ -3060,15 +2981,15 @@ var TabbedTerminalPanelInner = ({
3060
2981
  backgroundColor: theme.colors.background
3061
2982
  },
3062
2983
  children: [
3063
- !hideHeader && /* @__PURE__ */ jsx7("div", {
2984
+ !hideHeader && /* @__PURE__ */ jsx9("div", {
3064
2985
  ref: headerRef,
3065
- children: /* @__PURE__ */ jsx7(TabBar, {
2986
+ children: /* @__PURE__ */ jsx9(TabBar, {
3066
2987
  tabs: genericTabs,
3067
2988
  activeTabId,
3068
2989
  onTabClick: switchTab,
3069
2990
  onTabClose: closeTab,
3070
2991
  onNewTab: addNewTab,
3071
- leftSection: /* @__PURE__ */ jsx7("button", {
2992
+ leftSection: /* @__PURE__ */ jsx9("button", {
3072
2993
  onClick: () => onShowAllTerminalsChange?.(!showAllTerminals),
3073
2994
  style: {
3074
2995
  display: "flex",
@@ -3093,9 +3014,9 @@ var TabbedTerminalPanelInner = ({
3093
3014
  }
3094
3015
  },
3095
3016
  title: showAllTerminals ? "Showing all terminals (click to filter by context)" : "Show all terminals",
3096
- children: showAllTerminals ? /* @__PURE__ */ jsx7(Boxes, {
3017
+ children: showAllTerminals ? /* @__PURE__ */ jsx9(Boxes, {
3097
3018
  size: 14
3098
- }) : /* @__PURE__ */ jsx7(Box, {
3019
+ }) : /* @__PURE__ */ jsx9(Box, {
3099
3020
  size: 14
3100
3021
  })
3101
3022
  }),
@@ -3108,7 +3029,7 @@ var TabbedTerminalPanelInner = ({
3108
3029
  canDropOnTab
3109
3030
  })
3110
3031
  }),
3111
- /* @__PURE__ */ jsxs6("div", {
3032
+ /* @__PURE__ */ jsxs7("div", {
3112
3033
  style: {
3113
3034
  flex: 1,
3114
3035
  display: "flex",
@@ -3162,7 +3083,7 @@ var TabbedTerminalPanelInner = ({
3162
3083
  const customContent = renderTabContent(tab, isActive, sessionId, width);
3163
3084
  if (customContent === null && tab.contentType === "terminal") {
3164
3085
  const association = associations?.[tab.id];
3165
- return /* @__PURE__ */ jsx7("div", {
3086
+ return /* @__PURE__ */ jsx9("div", {
3166
3087
  style: {
3167
3088
  display: isActive ? "flex" : "none",
3168
3089
  flexDirection: "column",
@@ -3173,7 +3094,7 @@ var TabbedTerminalPanelInner = ({
3173
3094
  }, tab.id);
3174
3095
  }
3175
3096
  const hasBeenActivated = activatedTabs.has(tab.id);
3176
- return /* @__PURE__ */ jsx7("div", {
3097
+ return /* @__PURE__ */ jsx9("div", {
3177
3098
  style: {
3178
3099
  display: isActive ? "flex" : "none",
3179
3100
  flexDirection: "column",
@@ -3185,7 +3106,7 @@ var TabbedTerminalPanelInner = ({
3185
3106
  }
3186
3107
  if (tab.contentType === "terminal") {
3187
3108
  const association = associations?.[tab.id];
3188
- return /* @__PURE__ */ jsx7("div", {
3109
+ return /* @__PURE__ */ jsx9("div", {
3189
3110
  style: {
3190
3111
  display: isActive ? "flex" : "none",
3191
3112
  flexDirection: "column",
@@ -3195,7 +3116,7 @@ var TabbedTerminalPanelInner = ({
3195
3116
  children: renderTerminalWithAssociation(tab, isActive, sessionId, association)
3196
3117
  }, tab.id);
3197
3118
  }
3198
- return /* @__PURE__ */ jsxs6("div", {
3119
+ return /* @__PURE__ */ jsxs7("div", {
3199
3120
  style: {
3200
3121
  display: isActive ? "flex" : "none",
3201
3122
  alignItems: "center",
@@ -3204,20 +3125,20 @@ var TabbedTerminalPanelInner = ({
3204
3125
  color: theme.colors.textSecondary
3205
3126
  },
3206
3127
  children: [
3207
- /* @__PURE__ */ jsxs6("p", {
3128
+ /* @__PURE__ */ jsxs7("p", {
3208
3129
  children: [
3209
3130
  "Unknown content type: ",
3210
3131
  tab.contentType
3211
3132
  ]
3212
3133
  }),
3213
- /* @__PURE__ */ jsx7("p", {
3134
+ /* @__PURE__ */ jsx9("p", {
3214
3135
  style: { fontSize: theme.fontSizes[0], marginTop: "8px" },
3215
3136
  children: "Provide a renderTabContent prop to render custom tab types"
3216
3137
  })
3217
3138
  ]
3218
3139
  }, tab.id);
3219
3140
  }),
3220
- tabs.length === 0 && /* @__PURE__ */ jsxs6("div", {
3141
+ tabs.length === 0 && /* @__PURE__ */ jsxs7("div", {
3221
3142
  style: {
3222
3143
  display: "flex",
3223
3144
  flexDirection: "column",
@@ -3227,11 +3148,11 @@ var TabbedTerminalPanelInner = ({
3227
3148
  color: theme.colors.textSecondary
3228
3149
  },
3229
3150
  children: [
3230
- /* @__PURE__ */ jsx7(TerminalIcon2, {
3151
+ /* @__PURE__ */ jsx9(TerminalIcon2, {
3231
3152
  size: 32,
3232
3153
  style: { opacity: 0.5, marginBottom: "16px" }
3233
3154
  }),
3234
- /* @__PURE__ */ jsx7("button", {
3155
+ /* @__PURE__ */ jsx9("button", {
3235
3156
  onClick: () => addNewTab(),
3236
3157
  style: {
3237
3158
  marginTop: "16px",
@@ -3460,33 +3381,43 @@ var terminalPanelToolsMetadata = {
3460
3381
  };
3461
3382
 
3462
3383
  // src/panel-exports.ts
3463
- var panels = [
3464
- {
3465
- metadata: {
3466
- id: "com.principal.terminal",
3467
- name: "Terminal",
3468
- icon: "terminal",
3469
- version: "0.1.0",
3470
- author: "Principal",
3471
- description: "Integrated terminal emulator with industry theming",
3472
- slices: ["terminal"],
3473
- tools: terminalPanelTools
3474
- },
3475
- component: TerminalPanel,
3476
- onMount: async (_context) => {
3477
- console.log("Terminal Panel mounted for repository:", _context.currentScope.repository?.path);
3478
- },
3479
- onUnmount: async (_context) => {
3480
- console.log("Terminal Panel unmounting");
3481
- }
3482
- }
3483
- ];
3384
+ var panels = [];
3484
3385
  var onPackageLoad = async () => {
3485
3386
  console.log("Panel package loaded - Terminal Panel Extension");
3486
3387
  };
3487
3388
  var onPackageUnload = async () => {
3488
3389
  console.log("Panel package unloading - Terminal Panel Extension");
3489
3390
  };
3391
+ // src/panel-types/index.ts
3392
+ function getTerminalSessions(context2) {
3393
+ return context2.terminal?.data ?? [];
3394
+ }
3395
+ function getTerminalSession(context2, sessionId) {
3396
+ const sessions = getTerminalSessions(context2);
3397
+ return sessions.find((s) => s.id === sessionId);
3398
+ }
3399
+ function isTerminalLoading(context2) {
3400
+ return context2.terminal?.loading ?? false;
3401
+ }
3402
+ function getRepositoryPath(context2) {
3403
+ return context2.currentScope.repository?.path ?? null;
3404
+ }
3405
+ function getWorkspacePath(context2) {
3406
+ return context2.currentScope.workspace?.path ?? null;
3407
+ }
3408
+ function getTerminalDirectory(context2, terminalScope = "repository") {
3409
+ switch (terminalScope) {
3410
+ case "workspace":
3411
+ return getWorkspacePath(context2);
3412
+ case "repository":
3413
+ return getRepositoryPath(context2) ?? getWorkspacePath(context2);
3414
+ default:
3415
+ return getRepositoryPath(context2) ?? getWorkspacePath(context2);
3416
+ }
3417
+ }
3418
+ function getTerminalSlice(context2) {
3419
+ return context2.terminal;
3420
+ }
3490
3421
  export {
3491
3422
  writeToTerminalTool,
3492
3423
  withSpanSync,
@@ -3516,10 +3447,12 @@ export {
3516
3447
  context,
3517
3448
  closeTerminalSessionTool,
3518
3449
  clearTerminalTool,
3450
+ XtermRenderer,
3519
3451
  WorkingOverlay,
3520
3452
  ThemedTerminalWithProvider,
3521
3453
  ThemedTerminal,
3522
- TerminalPanel,
3454
+ TerminalSession,
3455
+ TerminalOverlay,
3523
3456
  TabbedTerminalPanel,
3524
3457
  TabButton,
3525
3458
  TabBar,