@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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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: (
|
|
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
|
-
|
|
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;
|
|
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;
|
|
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
|
+
"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",
|