@industry-theme/xterm-terminal-panel 0.1.3 → 0.1.6

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
@@ -2,6 +2,7 @@
2
2
  import { FitAddon } from "@xterm/addon-fit";
3
3
  import { SearchAddon } from "@xterm/addon-search";
4
4
  import { WebLinksAddon } from "@xterm/addon-web-links";
5
+ import { WebglAddon } from "@xterm/addon-webgl";
5
6
  import { Terminal } from "@xterm/xterm";
6
7
  import {
7
8
  ChevronDown,
@@ -60,6 +61,7 @@ function getTerminalCSSVariables(theme) {
60
61
 
61
62
  // src/components/ThemedTerminal.tsx
62
63
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
64
+ var SCROLL_DEBOUNCE_MS = 1000;
63
65
  var ThemedTerminal = forwardRef(({
64
66
  theme,
65
67
  onData,
@@ -91,7 +93,9 @@ var ThemedTerminal = forwardRef(({
91
93
  const [terminal, setTerminal] = useState(null);
92
94
  const fitAddonRef = useRef(null);
93
95
  const searchAddonRef = useRef(null);
96
+ const webglAddonRef = useRef(null);
94
97
  const resizeTimeoutRef = useRef(null);
98
+ const scrollDebounceRef = useRef(null);
95
99
  const isVisibleRef = useRef(isVisible);
96
100
  const isScrollLockedRef = useRef(true);
97
101
  useEffect(() => {
@@ -115,14 +119,23 @@ var ThemedTerminal = forwardRef(({
115
119
  isScrollLocked: isScrollLockedRef.current
116
120
  };
117
121
  };
122
+ const scheduleScrollToBottom = () => {
123
+ if (!terminal || !isScrollLockedRef.current)
124
+ return;
125
+ if (scrollDebounceRef.current) {
126
+ clearTimeout(scrollDebounceRef.current);
127
+ }
128
+ scrollDebounceRef.current = setTimeout(() => {
129
+ if (terminal && isScrollLockedRef.current) {
130
+ terminal.scrollToBottom();
131
+ }
132
+ }, SCROLL_DEBOUNCE_MS);
133
+ };
118
134
  useImperativeHandle(ref, () => ({
119
135
  write: (data) => {
120
136
  if (terminal) {
121
- terminal.write(data, () => {
122
- if (isScrollLockedRef.current) {
123
- terminal.scrollToBottom();
124
- }
125
- });
137
+ terminal.write(data);
138
+ scheduleScrollToBottom();
126
139
  } else {
127
140
  console.warn("[ThemedTerminal] write called but terminal is null!");
128
141
  }
@@ -130,9 +143,7 @@ var ThemedTerminal = forwardRef(({
130
143
  writeln: (data) => {
131
144
  if (terminal) {
132
145
  terminal.writeln(data);
133
- if (isScrollLockedRef.current) {
134
- terminal.scrollToBottom();
135
- }
146
+ scheduleScrollToBottom();
136
147
  }
137
148
  },
138
149
  scrollToBottom: () => {
@@ -257,6 +268,19 @@ var ThemedTerminal = forwardRef(({
257
268
  term.loadAddon(webLinksAddon);
258
269
  }
259
270
  term.open(terminalRef.current);
271
+ try {
272
+ const webglAddon = new WebglAddon;
273
+ webglAddonRef.current = webglAddon;
274
+ webglAddon.onContextLoss(() => {
275
+ console.warn("[ThemedTerminal] WebGL context lost, falling back to canvas renderer");
276
+ webglAddon.dispose();
277
+ webglAddonRef.current = null;
278
+ });
279
+ term.loadAddon(webglAddon);
280
+ console.info("[ThemedTerminal] WebGL renderer enabled");
281
+ } catch (e) {
282
+ console.warn("[ThemedTerminal] WebGL not available, using canvas renderer:", e);
283
+ }
260
284
  const scrollDisposable = term.onScroll(() => {
261
285
  const scrollY = term.buffer.active.viewportY;
262
286
  const scrollback2 = term.buffer.active.baseY;
@@ -303,6 +327,13 @@ var ThemedTerminal = forwardRef(({
303
327
  if (resizeTimeoutRef.current) {
304
328
  clearTimeout(resizeTimeoutRef.current);
305
329
  }
330
+ if (scrollDebounceRef.current) {
331
+ clearTimeout(scrollDebounceRef.current);
332
+ }
333
+ if (webglAddonRef.current) {
334
+ webglAddonRef.current.dispose();
335
+ webglAddonRef.current = null;
336
+ }
306
337
  term.dispose();
307
338
  };
308
339
  }, [onLinkClick]);
@@ -1049,7 +1080,8 @@ import React, {
1049
1080
  useRef as useRef3
1050
1081
  } from "react";
1051
1082
  import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1052
- var TerminalTabContentInner = ({ tab, sessionId, isActive, isVisible, actions, terminalContext, onSessionCreated, onScrollPositionChange, isForeign = false }, ref) => {
1083
+ function TerminalTabContentInner(props, ref) {
1084
+ const { tab, sessionId, isActive, isVisible, actions, terminalContext, onSessionCreated, onScrollPositionChange, isForeign = false } = props;
1053
1085
  const terminalRef = useRef3(null);
1054
1086
  const [localSessionId, setLocalSessionId] = useState3(sessionId);
1055
1087
  const [isInitialized, setIsInitialized] = useState3(false);
@@ -1249,8 +1281,26 @@ var TerminalTabContentInner = ({ tab, sessionId, isActive, isVisible, actions, t
1249
1281
  overlayState
1250
1282
  }, shouldRenderTerminal ? "active" : "overlay")
1251
1283
  });
1284
+ }
1285
+ var areTerminalTabContentPropsEqual = (prevProps, nextProps) => {
1286
+ const tabEqual = prevProps.tab.id === nextProps.tab.id && prevProps.tab.directory === nextProps.tab.directory && prevProps.tab.isActive === nextProps.tab.isActive && prevProps.tab.label === nextProps.tab.label;
1287
+ const changes = [];
1288
+ if (!tabEqual)
1289
+ changes.push("tab");
1290
+ if (prevProps.sessionId !== nextProps.sessionId)
1291
+ changes.push("sessionId");
1292
+ if (prevProps.isActive !== nextProps.isActive)
1293
+ changes.push("isActive");
1294
+ if (prevProps.isVisible !== nextProps.isVisible)
1295
+ changes.push("isVisible");
1296
+ if (prevProps.terminalContext !== nextProps.terminalContext)
1297
+ changes.push("terminalContext");
1298
+ if (prevProps.isForeign !== nextProps.isForeign)
1299
+ changes.push("isForeign");
1300
+ return changes.length === 0;
1252
1301
  };
1253
- var TerminalTabContent = React.memo(React.forwardRef(TerminalTabContentInner));
1302
+ var TerminalTabContentForwarded = React.forwardRef(TerminalTabContentInner);
1303
+ var TerminalTabContent = React.memo(TerminalTabContentForwarded, areTerminalTabContentPropsEqual);
1254
1304
  TerminalTabContent.displayName = "TerminalTabContent";
1255
1305
  var TabbedTerminalPanelInner = ({
1256
1306
  context: _context,
@@ -1288,6 +1338,21 @@ var TabbedTerminalPanelInner = ({
1288
1338
  return [...labeledOwnedTabs, ...sortedForeignTabs];
1289
1339
  }, [ownedTabs, foreignTabs, getOwnedTabLabel]);
1290
1340
  const tabRefsMap = useRef3(new Map);
1341
+ const refCallbacksMap = useRef3(new Map);
1342
+ const getRefCallback = useCallback((tabId) => {
1343
+ let callback = refCallbacksMap.current.get(tabId);
1344
+ if (!callback) {
1345
+ callback = (ref) => {
1346
+ if (ref) {
1347
+ tabRefsMap.current.set(tabId, ref);
1348
+ } else {
1349
+ tabRefsMap.current.delete(tabId);
1350
+ }
1351
+ };
1352
+ refCallbacksMap.current.set(tabId, callback);
1353
+ }
1354
+ return callback;
1355
+ }, []);
1291
1356
  const hasInitializedRef = useRef3(false);
1292
1357
  const isCreatingTabRef = useRef3(false);
1293
1358
  const headerRef = useRef3(null);
@@ -1748,13 +1813,7 @@ var TabbedTerminalPanelInner = ({
1748
1813
  },
1749
1814
  children: [
1750
1815
  tabs.map((tab) => /* @__PURE__ */ jsx4(TerminalTabContent, {
1751
- ref: (ref) => {
1752
- if (ref) {
1753
- tabRefsMap.current.set(tab.id, ref);
1754
- } else {
1755
- tabRefsMap.current.delete(tab.id);
1756
- }
1757
- },
1816
+ ref: getRefCallback(tab.id),
1758
1817
  tab,
1759
1818
  sessionId: sessionIds.get(tab.id) || null,
1760
1819
  isActive: tab.id === activeTabId,
@@ -1806,7 +1865,28 @@ var TabbedTerminalPanelInner = ({
1806
1865
  };
1807
1866
  TabbedTerminalPanelInner.displayName = "TabbedTerminalPanelInner";
1808
1867
  var TabbedTerminalPanel = React.memo(TabbedTerminalPanelInner, (prevProps, nextProps) => {
1809
- return prevProps.terminalContext === nextProps.terminalContext && prevProps.directory === nextProps.directory && prevProps.hideHeader === nextProps.hideHeader && prevProps.isVisible === nextProps.isVisible && prevProps.showAllTerminals === nextProps.showAllTerminals && prevProps.tabLabelPrefix === nextProps.tabLabelPrefix && prevProps.actions === nextProps.actions && prevProps.onTabsChange === nextProps.onTabsChange && prevProps.onShowAllTerminalsChange === nextProps.onShowAllTerminalsChange && prevProps.initialTabs === nextProps.initialTabs;
1868
+ const changes = [];
1869
+ if (prevProps.terminalContext !== nextProps.terminalContext)
1870
+ changes.push("terminalContext");
1871
+ if (prevProps.directory !== nextProps.directory)
1872
+ changes.push("directory");
1873
+ if (prevProps.hideHeader !== nextProps.hideHeader)
1874
+ changes.push("hideHeader");
1875
+ if (prevProps.isVisible !== nextProps.isVisible)
1876
+ changes.push("isVisible");
1877
+ if (prevProps.showAllTerminals !== nextProps.showAllTerminals)
1878
+ changes.push("showAllTerminals");
1879
+ if (prevProps.tabLabelPrefix !== nextProps.tabLabelPrefix)
1880
+ changes.push("tabLabelPrefix");
1881
+ if (prevProps.actions !== nextProps.actions)
1882
+ changes.push("actions");
1883
+ if (prevProps.onTabsChange !== nextProps.onTabsChange)
1884
+ changes.push("onTabsChange");
1885
+ if (prevProps.onShowAllTerminalsChange !== nextProps.onShowAllTerminalsChange)
1886
+ changes.push("onShowAllTerminalsChange");
1887
+ if (prevProps.initialTabs !== nextProps.initialTabs)
1888
+ changes.push("initialTabs");
1889
+ return changes.length === 0;
1810
1890
  });
1811
1891
  TabbedTerminalPanel.displayName = "TabbedTerminalPanel";
1812
1892
  // src/tools/index.ts
@@ -1 +1 @@
1
- {"version":3,"file":"ThemedTerminal.d.ts","sourceRoot":"","sources":["../../../src/components/ThemedTerminal.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAC;AAqB3D,OAAO,4BAA4B,CAAC;AAEpC,OAAO,KAAK,EAEV,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,8BAA8B,CAAC;AAEtC,MAAM,WAAW,4BAA6B,SAAQ,mBAAmB;IACvE,KAAK,EAAE,KAAK,CAAC;CACd;AAsBD,eAAO,MAAM,cAAc,4HAksB1B,CAAC"}
1
+ {"version":3,"file":"ThemedTerminal.d.ts","sourceRoot":"","sources":["../../../src/components/ThemedTerminal.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAC;AAyB3D,OAAO,4BAA4B,CAAC;AAEpC,OAAO,KAAK,EAEV,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,8BAA8B,CAAC;AAEtC,MAAM,WAAW,4BAA6B,SAAQ,mBAAmB;IACvE,KAAK,EAAE,KAAK,CAAC;CACd;AAsBD,eAAO,MAAM,cAAc,4HAyuB1B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"TabbedTerminalPanel.d.ts","sourceRoot":"","sources":["../../../src/panels/TabbedTerminalPanel.tsx"],"names":[],"mappings":"AAEA,OAAO,KAKN,MAAM,OAAO,CAAC;AAGf,OAAO,KAAK,EACV,wBAAwB,EAGzB,MAAM,gBAAgB,CAAC;AAuBxB,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,gBAAgB,EAAE,MAAM,IAAI,CAAC;CAC9B;AA6/BD,eAAO,MAAM,mBAAmB,sDAgB9B,CAAC"}
1
+ {"version":3,"file":"TabbedTerminalPanel.d.ts","sourceRoot":"","sources":["../../../src/panels/TabbedTerminalPanel.tsx"],"names":[],"mappings":"AAEA,OAAO,KAKN,MAAM,OAAO,CAAC;AAGf,OAAO,KAAK,EACV,wBAAwB,EAGzB,MAAM,gBAAgB,CAAC;AAuBxB,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,gBAAgB,EAAE,MAAM,IAAI,CAAC;CAC9B;AAsiCD,eAAO,MAAM,mBAAmB,sDAmB9B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@industry-theme/xterm-terminal-panel",
3
- "version": "0.1.3",
3
+ "version": "0.1.6",
4
4
  "description": "Industry-themed xterm.js terminal components with panel framework integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -24,7 +24,7 @@
24
24
  },
25
25
  "scripts": {
26
26
  "build": "bun run clean && bun run build:esm && bun run build:tools && bun run build:types && bun run build:styles",
27
- "build:esm": "NODE_ENV=production bun build ./index.ts --outdir ./dist --entry-naming [dir]/[name].[ext] --format esm --target browser --external react --external react-dom --external @xterm/xterm --external @xterm/addon-fit --external @xterm/addon-search --external @xterm/addon-web-links --external @principal-ade/industry-theme --external @principal-ade/panel-framework-core --external lucide-react --external clsx",
27
+ "build:esm": "NODE_ENV=production bun build ./index.ts --outdir ./dist --entry-naming [dir]/[name].[ext] --format esm --target browser --external react --external react-dom --external @xterm/xterm --external @xterm/addon-fit --external @xterm/addon-search --external @xterm/addon-web-links --external @xterm/addon-webgl --external @principal-ade/industry-theme --external @principal-ade/panel-framework-core --external lucide-react --external clsx",
28
28
  "build:tools": "bun build ./src/tools/index.ts --outfile ./dist/tools.bundle.js --format esm --target browser",
29
29
  "build:types": "tsc --project tsconfig.build.json --emitDeclarationOnly --declaration --declarationMap",
30
30
  "build:styles": "cp src/styles/terminal-theme.css dist/styles.css",
@@ -63,6 +63,7 @@
63
63
  "@xterm/addon-fit": ">=0.10.0",
64
64
  "@xterm/addon-search": ">=0.15.0",
65
65
  "@xterm/addon-web-links": ">=0.11.0",
66
+ "@xterm/addon-webgl": ">=0.18.0",
66
67
  "@xterm/xterm": ">=5.5.0",
67
68
  "lucide-react": ">=0.263.0",
68
69
  "react": ">=19.0.0",
@@ -71,6 +72,9 @@
71
72
  "peerDependenciesMeta": {
72
73
  "lucide-react": {
73
74
  "optional": true
75
+ },
76
+ "@xterm/addon-webgl": {
77
+ "optional": true
74
78
  }
75
79
  },
76
80
  "devDependencies": {
@@ -90,6 +94,7 @@
90
94
  "@xterm/addon-fit": "^0.10.0",
91
95
  "@xterm/addon-search": "^0.15.0",
92
96
  "@xterm/addon-web-links": "^0.11.0",
97
+ "@xterm/addon-webgl": "^0.18.0",
93
98
  "@xterm/xterm": "^5.5.0",
94
99
  "esbuild": "^0.25.8",
95
100
  "eslint": "^9.32.0",