@lijian-ui/dsh-term 0.2.0 → 0.3.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/lib/client.js +468 -87
- package/lib/client.js.map +1 -1
- package/lib/index.js +454 -29
- package/lib/tsconfig.client.tsbuildinfo +1 -1
- package/lib/tsconfig.host.tsbuildinfo +1 -1
- package/lib/types/client/client-i18n.d.ts +29 -0
- package/lib/types/client/client-i18n.d.ts.map +1 -0
- package/lib/types/client/i18n-seat.d.ts +15 -0
- package/lib/types/client/i18n-seat.d.ts.map +1 -0
- package/lib/types/client/index.d.ts +7 -0
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/term/AnimatedDock.d.ts.map +1 -1
- package/lib/types/client/term/TerminalPanel.d.ts +8 -3
- package/lib/types/client/term/TerminalPanel.d.ts.map +1 -1
- package/lib/types/core/types.d.ts +1 -0
- package/lib/types/core/types.d.ts.map +1 -1
- package/lib/types/gateway/i18n.d.ts +17 -0
- package/lib/types/gateway/i18n.d.ts.map +1 -0
- package/lib/types/host/routes.d.ts +3 -1
- package/lib/types/host/routes.d.ts.map +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/client/client-i18n.ts +68 -0
- package/src/client/i18n-seat.ts +27 -0
- package/src/client/index.ts +22 -3
- package/src/client/term/AnimatedDock.tsx +3 -1
- package/src/client/term/TerminalPanel.tsx +327 -44
- package/src/client/term/api.ts +17 -2
- package/src/client/term/chat-helper.ts +28 -0
- package/src/client/term/term.module.css +137 -0
- package/src/core/types.ts +22 -4
- package/src/gateway/i18n.ts +67 -0
- package/src/host/pty-service.ts +160 -17
- package/src/host/routes.ts +44 -4
- package/src/index.ts +31 -1
package/lib/client.js
CHANGED
|
@@ -10,6 +10,56 @@ window.__ModuleLoader__.load({
|
|
|
10
10
|
let react = require("react");
|
|
11
11
|
let react_dom_client = require("react-dom/client");
|
|
12
12
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
13
|
+
//#region src/client/client-i18n.ts
|
|
14
|
+
/**
|
|
15
|
+
* 客户端 i18n 字典:dsh-term 面板的中英文翻译。
|
|
16
|
+
*
|
|
17
|
+
* 通过 ctx.locale.register('dsh-term', dicts) 注册,
|
|
18
|
+
* 组件中用 const t = ctx.locale.bind('dsh-term') 获取翻译函数。
|
|
19
|
+
*
|
|
20
|
+
* @module dsh-term/client-i18n
|
|
21
|
+
*/
|
|
22
|
+
const zh = {
|
|
23
|
+
"ui.panel.title": "终端",
|
|
24
|
+
"ui.panel.addTabTitle": "新建终端",
|
|
25
|
+
"ui.panel.collapseTitle": "收起",
|
|
26
|
+
"ui.panel.emptyHint": "点击 + 新建终端",
|
|
27
|
+
"ui.tab.closeAria": "关闭 {0}",
|
|
28
|
+
"msg.sessionExited": "[dsh-term] 进程已退出(code {0})",
|
|
29
|
+
"msg.spawnFailed": "[dsh-term] 启动失败: {0}",
|
|
30
|
+
"ui.panel.shellTitle": "Shell",
|
|
31
|
+
"ui.shell.bash": "Bash",
|
|
32
|
+
"ui.shell.zsh": "Zsh",
|
|
33
|
+
"ui.shell.powershell": "PowerShell",
|
|
34
|
+
"ui.shell.cmd": "命令提示符",
|
|
35
|
+
"ui.shell.gitbash": "Git Bash",
|
|
36
|
+
"ui.panel.reopenTitle": "恢复终端",
|
|
37
|
+
"ui.panel.backgroundSessions": "后台终端",
|
|
38
|
+
"ui.panel.noBackground": "无后台终端",
|
|
39
|
+
"ui.panel.addToChat": "添加到对话",
|
|
40
|
+
"ui.dock.label": "终端"
|
|
41
|
+
};
|
|
42
|
+
const en = {
|
|
43
|
+
"ui.panel.title": "Terminal",
|
|
44
|
+
"ui.panel.addTabTitle": "New Terminal",
|
|
45
|
+
"ui.panel.collapseTitle": "Collapse",
|
|
46
|
+
"ui.panel.emptyHint": "Click + to create a terminal",
|
|
47
|
+
"ui.tab.closeAria": "Close {0}",
|
|
48
|
+
"msg.sessionExited": "[dsh-term] Process exited (code {0})",
|
|
49
|
+
"msg.spawnFailed": "[dsh-term] Spawn failed: {0}",
|
|
50
|
+
"ui.panel.shellTitle": "Shell",
|
|
51
|
+
"ui.shell.bash": "Bash",
|
|
52
|
+
"ui.shell.zsh": "Zsh",
|
|
53
|
+
"ui.shell.powershell": "PowerShell",
|
|
54
|
+
"ui.shell.cmd": "Command Prompt",
|
|
55
|
+
"ui.shell.gitbash": "Git Bash",
|
|
56
|
+
"ui.panel.reopenTitle": "Reopen terminal",
|
|
57
|
+
"ui.panel.backgroundSessions": "Background terminals",
|
|
58
|
+
"ui.panel.noBackground": "No background terminals",
|
|
59
|
+
"ui.panel.addToChat": "Add to chat",
|
|
60
|
+
"ui.dock.label": "Terminal"
|
|
61
|
+
};
|
|
62
|
+
//#endregion
|
|
13
63
|
//#region src/client/term/api.ts
|
|
14
64
|
async function call(path, body) {
|
|
15
65
|
const envelope = await (await fetch(path, {
|
|
@@ -41,10 +91,22 @@ window.__ModuleLoader__.load({
|
|
|
41
91
|
rows
|
|
42
92
|
});
|
|
43
93
|
}
|
|
44
|
-
/** Close one session. */
|
|
94
|
+
/** Close one session (kill the PTY). */
|
|
45
95
|
close(id) {
|
|
46
96
|
return call("/dsh-term/close", { id });
|
|
47
97
|
}
|
|
98
|
+
/** Detach a session (keep the PTY alive, mark as detached). */
|
|
99
|
+
detach(id) {
|
|
100
|
+
return call("/dsh-term/detach", { id });
|
|
101
|
+
}
|
|
102
|
+
/** Reattach to a detached session; returns the wire info. */
|
|
103
|
+
reattach(id) {
|
|
104
|
+
return call("/dsh-term/reattach", { id });
|
|
105
|
+
}
|
|
106
|
+
/** List available shells on the host. */
|
|
107
|
+
shells() {
|
|
108
|
+
return call("/dsh-term/shells");
|
|
109
|
+
}
|
|
48
110
|
/** Current session listing (used on reconnect). */
|
|
49
111
|
list() {
|
|
50
112
|
return call("/dsh-term/list");
|
|
@@ -7176,10 +7238,29 @@ window.__ModuleLoader__.load({
|
|
|
7176
7238
|
})()));
|
|
7177
7239
|
}));
|
|
7178
7240
|
//#endregion
|
|
7179
|
-
//#region src/client/term/
|
|
7241
|
+
//#region src/client/term/chat-helper.ts
|
|
7180
7242
|
var import_xterm = require_xterm();
|
|
7181
7243
|
var import_addon_fit = require_addon_fit();
|
|
7182
7244
|
/**
|
|
7245
|
+
* Append `text` to the current session's composer draft.
|
|
7246
|
+
* Returns false when there is no active session or the conversation service
|
|
7247
|
+
* is unavailable.
|
|
7248
|
+
*/
|
|
7249
|
+
function appendToConversationDraft(ctx, text) {
|
|
7250
|
+
const sessionId = ctx.sessions.list.getSnapshot().current;
|
|
7251
|
+
if (sessionId === void 0) return false;
|
|
7252
|
+
const actx = ctx.sessions.scope(sessionId);
|
|
7253
|
+
if (actx === void 0) return false;
|
|
7254
|
+
const conversation = ctx.conversation;
|
|
7255
|
+
if (conversation === void 0) return false;
|
|
7256
|
+
const input = conversation.input.for(actx);
|
|
7257
|
+
const draft = input.state.getSnapshot().draft;
|
|
7258
|
+
input.setDraft(draft.trim() === "" ? text : `${draft}\n${text}`);
|
|
7259
|
+
return true;
|
|
7260
|
+
}
|
|
7261
|
+
//#endregion
|
|
7262
|
+
//#region src/client/term/xterm-styles.ts
|
|
7263
|
+
/**
|
|
7183
7264
|
* Inlined xterm.js 5.3.0 core styles (MIT) — injected as a <style> tag at
|
|
7184
7265
|
* runtime because the client build compiles only *.module.css and the
|
|
7185
7266
|
* shell's css-modules pipeline rejects a raw :global block with xterm's
|
|
@@ -7362,7 +7443,7 @@ window.__ModuleLoader__.load({
|
|
|
7362
7443
|
`;
|
|
7363
7444
|
//#endregion
|
|
7364
7445
|
//#region \0dsh-css:src/client/term/term.module.css.mjs
|
|
7365
|
-
const css = ".WK4grq_col{border-left:1px solid var(--aion-border-base,#e5e6eb);background:var(--aion-bg-1,#f9fafb);width:100%;height:100%;font-family:var(--aion-font-sans,-apple-system, \"Segoe UI\", \"PingFang SC\", \"Microsoft YaHei\", sans-serif);flex-direction:column;display:flex;overflow:hidden}.WK4grq_title{color:var(--aion-text-primary,#1f2329);user-select:none;flex:none;padding:0 8px;font-size:12px;font-weight:600}.WK4grq_tabDivider{background:var(--aion-border-base,#e5e6eb);flex:none;width:1px;height:16px;margin:0 4px}.WK4grq_toolbar{border-bottom:1px solid var(--aion-border-base,#e5e6eb);background:var(--aion-bg-base,#fff);flex:none;align-items:center;gap:4px;height:32px;padding:0 6px;display:flex}.WK4grq_tab{height:22px;color:var(--aion-text-secondary,#454d5f);cursor:pointer;background:0 0;border:none;border-radius:5px;align-items:center;gap:6px;max-width:200px;padding:0 8px;font-size:12px;display:inline-flex}.WK4grq_tabActive{background:var(--aion-bg-2,#f2f3f5);color:var(--aion-text-primary,#000)}.WK4grq_tabClose{width:14px;height:14px;color:var(--aion-text-tertiary,#86909c);cursor:pointer;background:0 0;border:none;border-radius:3px;justify-content:center;align-items:center;font-size:11px;line-height:1;display:inline-flex}.WK4grq_tabClose:hover{background:var(--aion-bg-3,#e5e6eb);color:var(--aion-text-primary,#000)}.WK4grq_addTab{width:22px;height:22px;color:var(--aion-text-secondary,#454d5f);cursor:pointer;background:0 0;border:none;border-radius:5px;justify-content:center;align-items:center;font-size:14px;display:inline-flex}.WK4grq_addTab:hover{background:var(--aion-bg-2,#f2f3f5)}.WK4grq_spacer{flex:1}.WK4grq_collapse{width:22px;height:22px;color:var(--aion-text-secondary,#454d5f);cursor:pointer;background:0 0;border:none;border-radius:5px;justify-content:center;align-items:center;font-size:13px;display:inline-flex}.WK4grq_collapse:hover{background:var(--aion-bg-2,#f2f3f5)}.WK4grq_stage{background:var(--aion-bg-base,#fff);flex:1;min-height:0;position:relative}.WK4grq_termWrap{padding:4px;display:none;position:absolute;inset:0}.WK4grq_termWrapActive{display:block}.WK4grq_emptyHint{color:var(--aion-text-tertiary,#86909c);justify-content:center;align-items:center;font-size:13px;display:flex;position:absolute;inset:0}";
|
|
7446
|
+
const css = ".WK4grq_col{border-left:1px solid var(--aion-border-base,#e5e6eb);background:var(--aion-bg-1,#f9fafb);width:100%;height:100%;font-family:var(--aion-font-sans,-apple-system, \"Segoe UI\", \"PingFang SC\", \"Microsoft YaHei\", sans-serif);flex-direction:column;display:flex;overflow:hidden}.WK4grq_title{color:var(--aion-text-primary,#1f2329);user-select:none;flex:none;padding:0 8px;font-size:12px;font-weight:600}.WK4grq_tabDivider{background:var(--aion-border-base,#e5e6eb);flex:none;width:1px;height:16px;margin:0 4px}.WK4grq_toolbar{border-bottom:1px solid var(--aion-border-base,#e5e6eb);background:var(--aion-bg-base,#fff);flex:none;align-items:center;gap:4px;height:32px;padding:0 6px;display:flex}.WK4grq_tab{height:22px;color:var(--aion-text-secondary,#454d5f);cursor:pointer;background:0 0;border:none;border-radius:5px;align-items:center;gap:6px;max-width:200px;padding:0 8px;font-size:12px;display:inline-flex}.WK4grq_tabActive{background:var(--aion-bg-2,#f2f3f5);color:var(--aion-text-primary,#000)}.WK4grq_tabClose{width:14px;height:14px;color:var(--aion-text-tertiary,#86909c);cursor:pointer;background:0 0;border:none;border-radius:3px;justify-content:center;align-items:center;font-size:11px;line-height:1;display:inline-flex}.WK4grq_tabClose:hover{background:var(--aion-bg-3,#e5e6eb);color:var(--aion-text-primary,#000)}.WK4grq_addTab{width:22px;height:22px;color:var(--aion-text-secondary,#454d5f);cursor:pointer;background:0 0;border:none;border-radius:5px;justify-content:center;align-items:center;font-size:14px;display:inline-flex}.WK4grq_addTab:hover{background:var(--aion-bg-2,#f2f3f5)}.WK4grq_spacer{flex:1}.WK4grq_collapse{width:22px;height:22px;color:var(--aion-text-secondary,#454d5f);cursor:pointer;background:0 0;border:none;border-radius:5px;justify-content:center;align-items:center;font-size:13px;display:inline-flex}.WK4grq_collapse:hover{background:var(--aion-bg-2,#f2f3f5)}.WK4grq_stage{background:var(--aion-bg-base,#fff);flex:1;min-height:0;position:relative}.WK4grq_termWrap{padding:4px;display:none;position:absolute;inset:0}.WK4grq_termWrapActive{display:block}.WK4grq_emptyHint{color:var(--aion-text-tertiary,#86909c);justify-content:center;align-items:center;font-size:13px;display:flex;position:absolute;inset:0}.WK4grq_shellSelect{border:1px solid var(--aion-border-base,#e5e6eb);background:var(--aion-bg-base,#fff);height:22px;color:var(--aion-text-primary,#1f2329);cursor:pointer;border-radius:5px;outline:none;flex:none;padding:0 6px;font-size:12px}.WK4grq_shellSelect:hover{border-color:var(--aion-border-strong,#c9cdd4)}.WK4grq_shellSelect:focus{border-color:var(--aion-primary,#1677ff)}.WK4grq_reopenBtn{background:var(--aion-bg-2,#f2f3f5);height:22px;color:var(--aion-text-secondary,#454d5f);cursor:pointer;border:none;border-radius:5px;justify-content:center;align-items:center;gap:2px;padding:0 6px;font-size:12px;display:inline-flex}.WK4grq_reopenBtn:hover{background:var(--aion-bg-3,#e5e6eb);color:var(--aion-text-primary,#000)}.WK4grq_reopenDropdown{z-index:50;border:1px solid var(--aion-border-base,#e5e6eb);background:var(--aion-bg-base,#fff);border-radius:8px;min-width:180px;max-width:280px;padding:4px 0;position:absolute;top:32px;left:6px;box-shadow:0 4px 12px #00000014}.WK4grq_reopenHeader{color:var(--aion-text-tertiary,#86909c);user-select:none;padding:4px 10px;font-size:11px;font-weight:600}.WK4grq_reopenItem{align-items:center;gap:4px;padding:0 6px 0 4px;display:flex}.WK4grq_reopenItemBtn{height:28px;color:var(--aion-text-primary,#1f2329);cursor:pointer;text-align:left;text-overflow:ellipsis;white-space:nowrap;background:0 0;border:none;border-radius:5px;flex:1;align-items:center;padding:0 8px;font-size:12px;display:flex;overflow:hidden}.WK4grq_reopenItemBtn:hover{background:var(--aion-bg-2,#f2f3f5)}.WK4grq_reopenItemKill{width:20px;height:20px;color:var(--aion-text-tertiary,#86909c);cursor:pointer;background:0 0;border:none;border-radius:4px;flex:none;justify-content:center;align-items:center;font-size:13px;display:inline-flex}.WK4grq_reopenItemKill:hover{background:var(--aion-bg-3,#e5e6eb);color:var(--aion-text-primary,#000)}.WK4grq_addToChatBtn{z-index:60;border:1px solid var(--aion-border-base,#e5e6eb);background:var(--aion-bg-base,#fff);height:28px;color:var(--aion-text-primary,#1f2329);cursor:pointer;white-space:nowrap;border-radius:14px;align-items:center;gap:4px;padding:0 10px;font-size:12px;font-weight:500;display:inline-flex;position:absolute;box-shadow:0 2px 8px #0000001a}.WK4grq_addToChatBtn:hover{background:var(--aion-bg-2,#f2f3f5);border-color:var(--aion-primary,#1677ff);color:var(--aion-primary,#1677ff)}";
|
|
7366
7447
|
const tagId = "@lijian-ui/dsh-term/term.module.css";
|
|
7367
7448
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
7368
7449
|
const tag = document.createElement("style");
|
|
@@ -7373,9 +7454,17 @@ window.__ModuleLoader__.load({
|
|
|
7373
7454
|
}
|
|
7374
7455
|
var term_module_css_default = {
|
|
7375
7456
|
"addTab": "WK4grq_addTab",
|
|
7457
|
+
"addToChatBtn": "WK4grq_addToChatBtn",
|
|
7376
7458
|
"col": "WK4grq_col",
|
|
7377
7459
|
"collapse": "WK4grq_collapse",
|
|
7378
7460
|
"emptyHint": "WK4grq_emptyHint",
|
|
7461
|
+
"reopenBtn": "WK4grq_reopenBtn",
|
|
7462
|
+
"reopenDropdown": "WK4grq_reopenDropdown",
|
|
7463
|
+
"reopenHeader": "WK4grq_reopenHeader",
|
|
7464
|
+
"reopenItem": "WK4grq_reopenItem",
|
|
7465
|
+
"reopenItemBtn": "WK4grq_reopenItemBtn",
|
|
7466
|
+
"reopenItemKill": "WK4grq_reopenItemKill",
|
|
7467
|
+
"shellSelect": "WK4grq_shellSelect",
|
|
7379
7468
|
"spacer": "WK4grq_spacer",
|
|
7380
7469
|
"stage": "WK4grq_stage",
|
|
7381
7470
|
"tab": "WK4grq_tab",
|
|
@@ -7399,10 +7488,57 @@ window.__ModuleLoader__.load({
|
|
|
7399
7488
|
* the web shell's frame, beside the file-manager panels (preview/explorer)
|
|
7400
7489
|
* when present. The panel itself only owns its inner content (header + stage).
|
|
7401
7490
|
*
|
|
7402
|
-
*
|
|
7403
|
-
*
|
|
7491
|
+
* Features: shell selector with availability detection, terminal reuse
|
|
7492
|
+
* (close detaches — PTY keeps running; reopen re-attaches), 16-color ANSI
|
|
7493
|
+
* palette, safe cwd fallback, selection → add to conversation.
|
|
7404
7494
|
* @module dsh-term/client/term/TerminalPanel
|
|
7405
7495
|
*/
|
|
7496
|
+
/** All shell kinds in display order (used when detection fails). */
|
|
7497
|
+
const FALLBACK_SHELLS = [
|
|
7498
|
+
"zsh",
|
|
7499
|
+
"bash",
|
|
7500
|
+
"gitbash",
|
|
7501
|
+
"powershell",
|
|
7502
|
+
"cmd"
|
|
7503
|
+
];
|
|
7504
|
+
/** 16-color ANSI palette for dark backgrounds (One Dark inspired). */
|
|
7505
|
+
const DARK_PALETTE = {
|
|
7506
|
+
black: "#282c34",
|
|
7507
|
+
red: "#e06c75",
|
|
7508
|
+
green: "#98c379",
|
|
7509
|
+
yellow: "#e5c07b",
|
|
7510
|
+
blue: "#61afef",
|
|
7511
|
+
magenta: "#c678dd",
|
|
7512
|
+
cyan: "#56b6c2",
|
|
7513
|
+
white: "#abb2bf",
|
|
7514
|
+
brightBlack: "#5c6370",
|
|
7515
|
+
brightRed: "#ef6e7e",
|
|
7516
|
+
brightGreen: "#a3d978",
|
|
7517
|
+
brightYellow: "#f0c674",
|
|
7518
|
+
brightBlue: "#7ab8f5",
|
|
7519
|
+
brightMagenta: "#d68adf",
|
|
7520
|
+
brightCyan: "#6fd0dc",
|
|
7521
|
+
brightWhite: "#d7dae0"
|
|
7522
|
+
};
|
|
7523
|
+
/** 16-color ANSI palette for light backgrounds (Nord inspired). */
|
|
7524
|
+
const LIGHT_PALETTE = {
|
|
7525
|
+
black: "#3b4252",
|
|
7526
|
+
red: "#bf616a",
|
|
7527
|
+
green: "#a3be8c",
|
|
7528
|
+
yellow: "#ebcb8b",
|
|
7529
|
+
blue: "#81a1c1",
|
|
7530
|
+
magenta: "#b48ead",
|
|
7531
|
+
cyan: "#8fbcbb",
|
|
7532
|
+
white: "#4c566a",
|
|
7533
|
+
brightBlack: "#4c566a",
|
|
7534
|
+
brightRed: "#bf616a",
|
|
7535
|
+
brightGreen: "#a3be8c",
|
|
7536
|
+
brightYellow: "#ebcb8b",
|
|
7537
|
+
brightBlue: "#81a1c1",
|
|
7538
|
+
brightMagenta: "#b48ead",
|
|
7539
|
+
brightCyan: "#8fbcbb",
|
|
7540
|
+
brightWhite: "#2e3440"
|
|
7541
|
+
};
|
|
7406
7542
|
/** Inject the xterm core styles once (idempotent). */
|
|
7407
7543
|
const XTERM_STYLE_ID = "dsh-term-xterm-style";
|
|
7408
7544
|
function adoptXtermStyles() {
|
|
@@ -7418,29 +7554,15 @@ window.__ModuleLoader__.load({
|
|
|
7418
7554
|
background: "#0e0e0e",
|
|
7419
7555
|
foreground: "#ced3da",
|
|
7420
7556
|
cursor: "#ced3da",
|
|
7421
|
-
selectionBackground: "rgba(255,255,255,0.2)"
|
|
7557
|
+
selectionBackground: "rgba(255,255,255,0.2)",
|
|
7558
|
+
...DARK_PALETTE
|
|
7422
7559
|
};
|
|
7423
7560
|
return {
|
|
7424
7561
|
background: "#ffffff",
|
|
7425
7562
|
foreground: "#1f2329",
|
|
7426
7563
|
cursor: "#1f2329",
|
|
7427
7564
|
selectionBackground: "rgba(22,93,255,0.25)",
|
|
7428
|
-
|
|
7429
|
-
red: "#d4393b",
|
|
7430
|
-
green: "#167c2e",
|
|
7431
|
-
yellow: "#b58900",
|
|
7432
|
-
blue: "#0a4d8c",
|
|
7433
|
-
magenta: "#a020a0",
|
|
7434
|
-
cyan: "#0379a6",
|
|
7435
|
-
white: "#5a5f66",
|
|
7436
|
-
brightBlack: "#6a6f76",
|
|
7437
|
-
brightRed: "#e5585a",
|
|
7438
|
-
brightGreen: "#1a9c3a",
|
|
7439
|
-
brightYellow: "#d6a200",
|
|
7440
|
-
brightBlue: "#1a6fcf",
|
|
7441
|
-
brightMagenta: "#c040c0",
|
|
7442
|
-
brightCyan: "#1aa0d6",
|
|
7443
|
-
brightWhite: "#2f353b"
|
|
7565
|
+
...LIGHT_PALETTE
|
|
7444
7566
|
};
|
|
7445
7567
|
}
|
|
7446
7568
|
/** Current workspace cwd from the session list ('' when none). */
|
|
@@ -7450,16 +7572,46 @@ window.__ModuleLoader__.load({
|
|
|
7450
7572
|
const cwd = sessionId === void 0 ? void 0 : snapshot.byId[sessionId]?.cwd;
|
|
7451
7573
|
return typeof cwd === "string" && cwd !== "" ? cwd : "";
|
|
7452
7574
|
}
|
|
7453
|
-
/** The docked panel: header (title / tabs / new / collapse) + xterm stage. */
|
|
7454
|
-
function TerminalPanel({ ctx, api, onClose }) {
|
|
7575
|
+
/** The docked panel: header (title / shell / tabs / new / reopen / collapse) + xterm stage. */
|
|
7576
|
+
function TerminalPanel({ ctx, api, onClose, t }) {
|
|
7577
|
+
(0, react.useSyncExternalStore)((cb) => ctx.locale.subscribe(cb), () => ctx.locale.getSnapshot().revision);
|
|
7455
7578
|
const [tabs, setTabs] = (0, react.useState)([]);
|
|
7456
7579
|
const [active, setActive] = (0, react.useState)(null);
|
|
7580
|
+
const [shells, setShells] = (0, react.useState)(null);
|
|
7581
|
+
const [selectedShell, setSelectedShell] = (0, react.useState)("bash");
|
|
7582
|
+
const [detachedSessions, setDetachedSessions] = (0, react.useState)([]);
|
|
7583
|
+
const [showReopen, setShowReopen] = (0, react.useState)(false);
|
|
7584
|
+
const [reopenLeft, setReopenLeft] = (0, react.useState)(6);
|
|
7585
|
+
const [floatBtn, setFloatBtn] = (0, react.useState)(null);
|
|
7457
7586
|
const stageRef = (0, react.useRef)(null);
|
|
7587
|
+
const panelRef = (0, react.useRef)(null);
|
|
7458
7588
|
const tabsRef = (0, react.useRef)(tabs);
|
|
7459
7589
|
tabsRef.current = tabs;
|
|
7590
|
+
const floatBtnRef = (0, react.useRef)(null);
|
|
7460
7591
|
(0, react.useEffect)(() => {
|
|
7461
7592
|
adoptXtermStyles();
|
|
7462
7593
|
}, []);
|
|
7594
|
+
(0, react.useEffect)(() => {
|
|
7595
|
+
let cancelled = false;
|
|
7596
|
+
api.shells().then((result) => {
|
|
7597
|
+
if (cancelled) return;
|
|
7598
|
+
const list = result.shells;
|
|
7599
|
+
setShells(list);
|
|
7600
|
+
if (list.length > 0) {
|
|
7601
|
+
const preferred = navigator.userAgent.includes("Windows") ? "powershell" : "zsh";
|
|
7602
|
+
const initial = list.some((s) => s.id === preferred) ? preferred : list[0].id;
|
|
7603
|
+
setSelectedShell(initial);
|
|
7604
|
+
}
|
|
7605
|
+
}).catch(() => {
|
|
7606
|
+
if (!cancelled) setShells(FALLBACK_SHELLS.map((id) => ({
|
|
7607
|
+
id,
|
|
7608
|
+
labelKey: `ui.shell.${id}`
|
|
7609
|
+
})));
|
|
7610
|
+
});
|
|
7611
|
+
return () => {
|
|
7612
|
+
cancelled = true;
|
|
7613
|
+
};
|
|
7614
|
+
}, [api]);
|
|
7463
7615
|
(0, react.useEffect)(() => {
|
|
7464
7616
|
const applyTheme = () => {
|
|
7465
7617
|
const theme = themeOf();
|
|
@@ -7476,17 +7628,24 @@ window.__ModuleLoader__.load({
|
|
|
7476
7628
|
(0, react.useEffect)(() => {
|
|
7477
7629
|
return api.subscribe((event) => {
|
|
7478
7630
|
if (event.kind === "output") {
|
|
7479
|
-
tabsRef.current.find((
|
|
7631
|
+
tabsRef.current.find((tb) => tb.sessionId === event.id)?.term.write(event.data);
|
|
7480
7632
|
return;
|
|
7481
7633
|
}
|
|
7482
7634
|
if (event.kind === "exit") {
|
|
7483
|
-
const tab = tabsRef.current.find((
|
|
7484
|
-
if (tab !== void 0) tab.term.write(`\r\n[dsh-term]
|
|
7635
|
+
const tab = tabsRef.current.find((tb) => tb.sessionId === event.id);
|
|
7636
|
+
if (tab !== void 0) tab.term.write(`\r\n${event.message ?? `\r\n[dsh-term] Process exited (code ${event.exitCode})`}\r\n`);
|
|
7637
|
+
setDetachedSessions((prev) => prev.filter((s) => s.id !== event.id));
|
|
7638
|
+
return;
|
|
7639
|
+
}
|
|
7640
|
+
if (event.kind === "detached") return;
|
|
7641
|
+
if (event.kind === "reattached") {
|
|
7642
|
+
setDetachedSessions((prev) => prev.filter((s) => s.id !== event.session.id));
|
|
7643
|
+
return;
|
|
7485
7644
|
}
|
|
7486
7645
|
});
|
|
7487
7646
|
}, [api]);
|
|
7488
7647
|
/** Create a tab: xterm instance + host spawn, then attach output routing. */
|
|
7489
|
-
const openTab = (0, react.useCallback)(async () => {
|
|
7648
|
+
const openTab = (0, react.useCallback)(async (shell) => {
|
|
7490
7649
|
if (stageRef.current === null) return;
|
|
7491
7650
|
const wrap = document.createElement("div");
|
|
7492
7651
|
wrap.className = term_module_css_default.termWrap;
|
|
@@ -7505,18 +7664,19 @@ window.__ModuleLoader__.load({
|
|
|
7505
7664
|
fit.fit();
|
|
7506
7665
|
} catch {}
|
|
7507
7666
|
term.onData((data) => {
|
|
7508
|
-
const tab = tabsRef.current.find((
|
|
7667
|
+
const tab = tabsRef.current.find((tb) => tb.term === term);
|
|
7509
7668
|
if (tab !== void 0) api.write(tab.sessionId, data);
|
|
7510
7669
|
});
|
|
7511
7670
|
let session;
|
|
7512
7671
|
try {
|
|
7513
7672
|
session = await api.spawn({
|
|
7673
|
+
shell: shell ?? selectedShell,
|
|
7514
7674
|
cwd: currentCwd(ctx) || void 0,
|
|
7515
7675
|
cols: term.cols,
|
|
7516
7676
|
rows: term.rows
|
|
7517
7677
|
});
|
|
7518
7678
|
} catch (error) {
|
|
7519
|
-
term.write(`\r\n
|
|
7679
|
+
term.write(`\r\n${t("msg.spawnFailed", { 0: String(error) })}\r\n`);
|
|
7520
7680
|
return;
|
|
7521
7681
|
}
|
|
7522
7682
|
const tab = {
|
|
@@ -7530,15 +7690,62 @@ window.__ModuleLoader__.load({
|
|
|
7530
7690
|
setActive(session.id);
|
|
7531
7691
|
api.resize(session.id, term.cols, term.rows);
|
|
7532
7692
|
term.focus();
|
|
7533
|
-
}, [
|
|
7693
|
+
}, [
|
|
7694
|
+
api,
|
|
7695
|
+
ctx,
|
|
7696
|
+
selectedShell,
|
|
7697
|
+
t
|
|
7698
|
+
]);
|
|
7699
|
+
/** Reattach to a detached session: create a fresh xterm and wire it up. */
|
|
7700
|
+
const reopenTab = (0, react.useCallback)(async (sessionId) => {
|
|
7701
|
+
if (stageRef.current === null) return;
|
|
7702
|
+
let session;
|
|
7703
|
+
try {
|
|
7704
|
+
session = await api.reattach(sessionId);
|
|
7705
|
+
} catch {
|
|
7706
|
+
return;
|
|
7707
|
+
}
|
|
7708
|
+
const wrap = document.createElement("div");
|
|
7709
|
+
wrap.className = term_module_css_default.termWrap;
|
|
7710
|
+
stageRef.current.appendChild(wrap);
|
|
7711
|
+
const term = new import_xterm.Terminal({
|
|
7712
|
+
fontSize: 13,
|
|
7713
|
+
fontFamily: "ui-monospace, \"SF Mono\", Menlo, Consolas, monospace",
|
|
7714
|
+
cursorBlink: true,
|
|
7715
|
+
theme: themeOf(),
|
|
7716
|
+
scrollback: 5e3
|
|
7717
|
+
});
|
|
7718
|
+
const fit = new import_addon_fit.FitAddon();
|
|
7719
|
+
term.loadAddon(fit);
|
|
7720
|
+
term.open(wrap);
|
|
7721
|
+
try {
|
|
7722
|
+
fit.fit();
|
|
7723
|
+
} catch {}
|
|
7724
|
+
term.onData((data) => {
|
|
7725
|
+
const tab = tabsRef.current.find((tb) => tb.term === term);
|
|
7726
|
+
if (tab !== void 0) api.write(tab.sessionId, data);
|
|
7727
|
+
});
|
|
7728
|
+
const tab = {
|
|
7729
|
+
sessionId: session.id,
|
|
7730
|
+
title: session.title,
|
|
7731
|
+
term,
|
|
7732
|
+
fit,
|
|
7733
|
+
wrap
|
|
7734
|
+
};
|
|
7735
|
+
setTabs((prev) => [...prev, tab]);
|
|
7736
|
+
setActive(session.id);
|
|
7737
|
+
api.resize(session.id, term.cols, term.rows);
|
|
7738
|
+
setShowReopen(false);
|
|
7739
|
+
term.focus();
|
|
7740
|
+
}, [api]);
|
|
7534
7741
|
(0, react.useEffect)(() => {
|
|
7535
|
-
if (tabs.length === 0) openTab();
|
|
7536
|
-
}, []);
|
|
7742
|
+
if (tabs.length === 0 && shells !== null) openTab();
|
|
7743
|
+
}, [shells]);
|
|
7537
7744
|
(0, react.useEffect)(() => {
|
|
7538
7745
|
const stage = stageRef.current;
|
|
7539
7746
|
if (stage === null) return;
|
|
7540
7747
|
const refit = () => {
|
|
7541
|
-
const tab = tabsRef.current.find((
|
|
7748
|
+
const tab = tabsRef.current.find((tb) => tb.sessionId === active);
|
|
7542
7749
|
if (tab === void 0) return;
|
|
7543
7750
|
try {
|
|
7544
7751
|
tab.fit.fit();
|
|
@@ -7552,7 +7759,7 @@ window.__ModuleLoader__.load({
|
|
|
7552
7759
|
(0, react.useEffect)(() => {
|
|
7553
7760
|
for (const tab of tabsRef.current) tab.wrap.classList.toggle(term_module_css_default.termWrapActive, tab.sessionId === active);
|
|
7554
7761
|
if (active !== null) {
|
|
7555
|
-
const tab = tabsRef.current.find((
|
|
7762
|
+
const tab = tabsRef.current.find((tb) => tb.sessionId === active);
|
|
7556
7763
|
if (tab !== void 0) {
|
|
7557
7764
|
try {
|
|
7558
7765
|
tab.fit.fit();
|
|
@@ -7566,16 +7773,38 @@ window.__ModuleLoader__.load({
|
|
|
7566
7773
|
api,
|
|
7567
7774
|
tabs
|
|
7568
7775
|
]);
|
|
7776
|
+
/** Close a tab: detach the PTY (keep it alive) and dispose the xterm locally. */
|
|
7569
7777
|
const closeTab = (sessionId) => {
|
|
7570
|
-
const tab = tabsRef.current.find((
|
|
7778
|
+
const tab = tabsRef.current.find((tb) => tb.sessionId === sessionId);
|
|
7571
7779
|
if (tab !== void 0) {
|
|
7572
|
-
api.
|
|
7780
|
+
api.detach(sessionId);
|
|
7573
7781
|
tab.term.dispose();
|
|
7574
7782
|
tab.wrap.remove();
|
|
7575
7783
|
}
|
|
7576
|
-
const next = tabsRef.current.filter((
|
|
7784
|
+
const next = tabsRef.current.filter((tb) => tb.sessionId !== sessionId);
|
|
7577
7785
|
setTabs(next);
|
|
7578
7786
|
if (active === sessionId) setActive(next[0]?.sessionId ?? null);
|
|
7787
|
+
setDetachedSessions((prev) => {
|
|
7788
|
+
if (prev.some((s) => s.id === sessionId)) return prev;
|
|
7789
|
+
const tab2 = tabsRef.current.find((tb) => tb.sessionId === sessionId);
|
|
7790
|
+
if (tab2 === void 0) return prev;
|
|
7791
|
+
return [...prev, {
|
|
7792
|
+
id: sessionId,
|
|
7793
|
+
title: tab2.title,
|
|
7794
|
+
cwd: "",
|
|
7795
|
+
cols: 80,
|
|
7796
|
+
rows: 24,
|
|
7797
|
+
alive: true,
|
|
7798
|
+
exitCode: null,
|
|
7799
|
+
shell: "bash",
|
|
7800
|
+
detached: true
|
|
7801
|
+
}];
|
|
7802
|
+
});
|
|
7803
|
+
};
|
|
7804
|
+
/** Kill a detached session permanently (remove from background list). */
|
|
7805
|
+
const killDetached = (sessionId) => {
|
|
7806
|
+
api.close(sessionId);
|
|
7807
|
+
setDetachedSessions((prev) => prev.filter((s) => s.id !== sessionId));
|
|
7579
7808
|
};
|
|
7580
7809
|
(0, react.useEffect)(() => {
|
|
7581
7810
|
const current = tabsRef.current;
|
|
@@ -7587,57 +7816,185 @@ window.__ModuleLoader__.load({
|
|
|
7587
7816
|
}
|
|
7588
7817
|
};
|
|
7589
7818
|
}, []);
|
|
7819
|
+
(0, react.useEffect)(() => {
|
|
7820
|
+
const stage = stageRef.current;
|
|
7821
|
+
const panel = panelRef.current;
|
|
7822
|
+
if (stage === null || panel === null) return;
|
|
7823
|
+
const onMouseUp = (e) => {
|
|
7824
|
+
if (floatBtnRef.current !== null && floatBtnRef.current.contains(e.target)) return;
|
|
7825
|
+
requestAnimationFrame(() => {
|
|
7826
|
+
const sel = tabsRef.current.find((tb) => tb.sessionId === active)?.term.getSelection() ?? "";
|
|
7827
|
+
if (!sel.trim()) {
|
|
7828
|
+
setFloatBtn(null);
|
|
7829
|
+
return;
|
|
7830
|
+
}
|
|
7831
|
+
const panelRect = panel.getBoundingClientRect();
|
|
7832
|
+
const top = Math.max(e.clientY - panelRect.top, 44);
|
|
7833
|
+
const left = Math.min(Math.max(e.clientX - panelRect.left, 70), panelRect.width - 70);
|
|
7834
|
+
setFloatBtn({
|
|
7835
|
+
top,
|
|
7836
|
+
left,
|
|
7837
|
+
content: sel
|
|
7838
|
+
});
|
|
7839
|
+
});
|
|
7840
|
+
};
|
|
7841
|
+
const onDocMouseDown = (e) => {
|
|
7842
|
+
if (floatBtnRef.current !== null && floatBtnRef.current.contains(e.target)) return;
|
|
7843
|
+
setFloatBtn(null);
|
|
7844
|
+
};
|
|
7845
|
+
stage.addEventListener("mouseup", onMouseUp);
|
|
7846
|
+
document.addEventListener("mousedown", onDocMouseDown);
|
|
7847
|
+
return () => {
|
|
7848
|
+
stage.removeEventListener("mouseup", onMouseUp);
|
|
7849
|
+
document.removeEventListener("mousedown", onDocMouseDown);
|
|
7850
|
+
};
|
|
7851
|
+
}, [active]);
|
|
7852
|
+
(0, react.useEffect)(() => {
|
|
7853
|
+
const tab = tabs.find((tb) => tb.sessionId === active);
|
|
7854
|
+
if (tab === void 0) return;
|
|
7855
|
+
const d = tab.term.onSelectionChange(() => {
|
|
7856
|
+
if (!tab.term.hasSelection()) setFloatBtn(null);
|
|
7857
|
+
});
|
|
7858
|
+
return () => d.dispose();
|
|
7859
|
+
}, [active, tabs]);
|
|
7860
|
+
(0, react.useEffect)(() => {
|
|
7861
|
+
if (!showReopen) return;
|
|
7862
|
+
const onDown = (e) => {
|
|
7863
|
+
const target = e.target;
|
|
7864
|
+
if (target.closest(`[data-reopen-dropdown]`) !== null) return;
|
|
7865
|
+
if (target.closest(`[data-reopen-btn]`) !== null) return;
|
|
7866
|
+
setShowReopen(false);
|
|
7867
|
+
};
|
|
7868
|
+
document.addEventListener("mousedown", onDown);
|
|
7869
|
+
return () => document.removeEventListener("mousedown", onDown);
|
|
7870
|
+
}, [showReopen]);
|
|
7871
|
+
const shellOptions = shells ?? FALLBACK_SHELLS.map((id) => ({
|
|
7872
|
+
id,
|
|
7873
|
+
labelKey: `ui.shell.${id}`
|
|
7874
|
+
}));
|
|
7590
7875
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7591
7876
|
className: term_module_css_default.col,
|
|
7592
7877
|
"data-dsh-term": "",
|
|
7593
|
-
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
|
|
7597
|
-
|
|
7598
|
-
|
|
7599
|
-
|
|
7600
|
-
|
|
7601
|
-
|
|
7602
|
-
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
7606
|
-
|
|
7607
|
-
|
|
7608
|
-
|
|
7609
|
-
|
|
7610
|
-
|
|
7611
|
-
|
|
7612
|
-
closeTab(tab.sessionId);
|
|
7878
|
+
ref: panelRef,
|
|
7879
|
+
children: [
|
|
7880
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7881
|
+
className: term_module_css_default.toolbar,
|
|
7882
|
+
children: [
|
|
7883
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7884
|
+
className: term_module_css_default.title,
|
|
7885
|
+
children: t("ui.panel.title")
|
|
7886
|
+
}),
|
|
7887
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: term_module_css_default.tabDivider }),
|
|
7888
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
|
|
7889
|
+
className: term_module_css_default.shellSelect,
|
|
7890
|
+
value: selectedShell,
|
|
7891
|
+
onChange: (e) => {
|
|
7892
|
+
const next = e.target.value;
|
|
7893
|
+
if (next === selectedShell) return;
|
|
7894
|
+
setSelectedShell(next);
|
|
7895
|
+
if (active !== null) closeTab(active);
|
|
7896
|
+
openTab(next);
|
|
7613
7897
|
},
|
|
7614
|
-
|
|
7615
|
-
|
|
7616
|
-
|
|
7617
|
-
|
|
7618
|
-
|
|
7619
|
-
|
|
7620
|
-
|
|
7621
|
-
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
|
|
7625
|
-
|
|
7626
|
-
|
|
7627
|
-
|
|
7628
|
-
|
|
7629
|
-
|
|
7630
|
-
|
|
7898
|
+
title: t("ui.panel.shellTitle"),
|
|
7899
|
+
disabled: shells === null,
|
|
7900
|
+
children: shellOptions.map((s) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
7901
|
+
value: s.id,
|
|
7902
|
+
children: t(s.labelKey)
|
|
7903
|
+
}, s.id))
|
|
7904
|
+
}),
|
|
7905
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: term_module_css_default.tabDivider }),
|
|
7906
|
+
tabs.map((tab) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
7907
|
+
type: "button",
|
|
7908
|
+
className: `${term_module_css_default.tab}${tab.sessionId === active ? ` ${term_module_css_default.tabActive}` : ""}`,
|
|
7909
|
+
onClick: () => setActive(tab.sessionId),
|
|
7910
|
+
title: tab.title,
|
|
7911
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: tab.title }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7912
|
+
role: "button",
|
|
7913
|
+
"aria-label": t("ui.tab.closeAria", { 0: tab.title }),
|
|
7914
|
+
className: term_module_css_default.tabClose,
|
|
7915
|
+
onClick: (event) => {
|
|
7916
|
+
event.stopPropagation();
|
|
7917
|
+
closeTab(tab.sessionId);
|
|
7918
|
+
},
|
|
7919
|
+
children: "×"
|
|
7920
|
+
})]
|
|
7921
|
+
}, tab.sessionId)),
|
|
7922
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7923
|
+
type: "button",
|
|
7924
|
+
className: term_module_css_default.addTab,
|
|
7925
|
+
title: t("ui.panel.addTabTitle"),
|
|
7926
|
+
onClick: () => void openTab(),
|
|
7927
|
+
children: "+"
|
|
7928
|
+
}),
|
|
7929
|
+
detachedSessions.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
7930
|
+
type: "button",
|
|
7931
|
+
"data-reopen-btn": "",
|
|
7932
|
+
className: term_module_css_default.reopenBtn,
|
|
7933
|
+
title: t("ui.panel.reopenTitle"),
|
|
7934
|
+
onClick: (e) => {
|
|
7935
|
+
setReopenLeft(e.currentTarget.offsetLeft);
|
|
7936
|
+
setShowReopen((v) => !v);
|
|
7937
|
+
},
|
|
7938
|
+
children: ["↻", detachedSessions.length]
|
|
7939
|
+
}),
|
|
7940
|
+
showReopen && detachedSessions.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7941
|
+
"data-reopen-dropdown": "",
|
|
7942
|
+
className: term_module_css_default.reopenDropdown,
|
|
7943
|
+
style: { left: reopenLeft },
|
|
7944
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7945
|
+
className: term_module_css_default.reopenHeader,
|
|
7946
|
+
children: t("ui.panel.backgroundSessions")
|
|
7947
|
+
}), detachedSessions.map((s) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7948
|
+
className: term_module_css_default.reopenItem,
|
|
7949
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7950
|
+
type: "button",
|
|
7951
|
+
className: term_module_css_default.reopenItemBtn,
|
|
7952
|
+
onClick: () => void reopenTab(s.id),
|
|
7953
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: s.title })
|
|
7954
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7955
|
+
type: "button",
|
|
7956
|
+
className: term_module_css_default.reopenItemKill,
|
|
7957
|
+
title: "×",
|
|
7958
|
+
onClick: () => killDetached(s.id),
|
|
7959
|
+
children: "×"
|
|
7960
|
+
})]
|
|
7961
|
+
}, s.id))]
|
|
7962
|
+
}),
|
|
7963
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: term_module_css_default.spacer }),
|
|
7964
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7965
|
+
type: "button",
|
|
7966
|
+
className: term_module_css_default.collapse,
|
|
7967
|
+
title: t("ui.panel.collapseTitle"),
|
|
7968
|
+
onClick: onClose,
|
|
7969
|
+
children: "—"
|
|
7970
|
+
})
|
|
7971
|
+
]
|
|
7972
|
+
}),
|
|
7973
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7974
|
+
className: term_module_css_default.stage,
|
|
7975
|
+
ref: stageRef,
|
|
7976
|
+
children: tabs.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7977
|
+
className: term_module_css_default.emptyHint,
|
|
7978
|
+
children: t("ui.panel.emptyHint")
|
|
7631
7979
|
})
|
|
7632
|
-
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7638
|
-
|
|
7980
|
+
}),
|
|
7981
|
+
floatBtn !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
7982
|
+
ref: floatBtnRef,
|
|
7983
|
+
className: term_module_css_default.addToChatBtn,
|
|
7984
|
+
style: {
|
|
7985
|
+
top: floatBtn.top,
|
|
7986
|
+
left: floatBtn.left
|
|
7987
|
+
},
|
|
7988
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
7989
|
+
onClick: () => {
|
|
7990
|
+
appendToConversationDraft(ctx, floatBtn.content.replace(/\s+$/, ""));
|
|
7991
|
+
setFloatBtn(null);
|
|
7992
|
+
tabsRef.current.find((tb) => tb.sessionId === active)?.term.clearSelection();
|
|
7993
|
+
},
|
|
7994
|
+
title: t("ui.panel.addToChat"),
|
|
7995
|
+
children: t("ui.panel.addToChat")
|
|
7639
7996
|
})
|
|
7640
|
-
|
|
7997
|
+
]
|
|
7641
7998
|
});
|
|
7642
7999
|
}
|
|
7643
8000
|
//#endregion
|
|
@@ -7667,6 +8024,16 @@ window.__ModuleLoader__.load({
|
|
|
7667
8024
|
});
|
|
7668
8025
|
}
|
|
7669
8026
|
//#endregion
|
|
8027
|
+
//#region src/client/i18n-seat.ts
|
|
8028
|
+
let _t = null;
|
|
8029
|
+
function bindI18n(t) {
|
|
8030
|
+
_t = t;
|
|
8031
|
+
}
|
|
8032
|
+
function getT() {
|
|
8033
|
+
if (_t === null) return ((key) => key);
|
|
8034
|
+
return _t;
|
|
8035
|
+
}
|
|
8036
|
+
//#endregion
|
|
7670
8037
|
//#region src/client/term/AnimatedDock.tsx
|
|
7671
8038
|
/**
|
|
7672
8039
|
* AnimatedDock — the terminal dock button rendered into the conversation
|
|
@@ -7688,6 +8055,7 @@ window.__ModuleLoader__.load({
|
|
|
7688
8055
|
terminalState: "dsh-dock:terminal-state"
|
|
7689
8056
|
};
|
|
7690
8057
|
function AnimatedDock() {
|
|
8058
|
+
const t = getT();
|
|
7691
8059
|
const [active, setActive] = (0, react.useState)(false);
|
|
7692
8060
|
(0, react.useEffect)(() => {
|
|
7693
8061
|
const onState = (e) => setActive(Boolean(e.detail));
|
|
@@ -7696,7 +8064,7 @@ window.__ModuleLoader__.load({
|
|
|
7696
8064
|
}, []);
|
|
7697
8065
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DockItem, {
|
|
7698
8066
|
active,
|
|
7699
|
-
label: "
|
|
8067
|
+
label: t("ui.dock.label"),
|
|
7700
8068
|
onClick: () => window.dispatchEvent(new CustomEvent(EV$1.toggleTerminal)),
|
|
7701
8069
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
7702
8070
|
width: "16",
|
|
@@ -7738,8 +8106,13 @@ window.__ModuleLoader__.load({
|
|
|
7738
8106
|
* whole boot when a plugin apply throws.
|
|
7739
8107
|
* @module dsh-term/client
|
|
7740
8108
|
*/
|
|
7741
|
-
/** Required services: sessions (for the workspace cwd). */
|
|
7742
|
-
const inject = [
|
|
8109
|
+
/** Required services: sessions (for the workspace cwd), conversation (for add-to-chat). */
|
|
8110
|
+
const inject = [
|
|
8111
|
+
"sessions",
|
|
8112
|
+
"locale",
|
|
8113
|
+
"slots",
|
|
8114
|
+
"conversation"
|
|
8115
|
+
];
|
|
7743
8116
|
/** Cross-plugin event names shared with the AnimatedDock header group. */
|
|
7744
8117
|
const EV = {
|
|
7745
8118
|
toggleTerminal: "dsh-dock:toggle-terminal",
|
|
@@ -7844,6 +8217,13 @@ window.__ModuleLoader__.load({
|
|
|
7844
8217
|
}
|
|
7845
8218
|
/** Apply the browser half. */
|
|
7846
8219
|
function apply(ctx) {
|
|
8220
|
+
const I18N_NS = "dsh-term";
|
|
8221
|
+
ctx.effect(() => ctx.locale.register(I18N_NS, {
|
|
8222
|
+
zh,
|
|
8223
|
+
en
|
|
8224
|
+
}), "dsh-term: client dictionaries");
|
|
8225
|
+
const t = ctx.locale.bind(I18N_NS);
|
|
8226
|
+
bindI18n(t);
|
|
7847
8227
|
ctx.inject(["slots"], (scope) => {
|
|
7848
8228
|
scope.slots.inject("conversation.session.header.utilities", () => scope.slots.register({
|
|
7849
8229
|
name: "conversation.session.header.utilities",
|
|
@@ -7983,7 +8363,8 @@ window.__ModuleLoader__.load({
|
|
|
7983
8363
|
root.render((0, react.createElement)(TerminalPanel, {
|
|
7984
8364
|
ctx,
|
|
7985
8365
|
api,
|
|
7986
|
-
onClose: () => setVisible(false)
|
|
8366
|
+
onClose: () => setVisible(false),
|
|
8367
|
+
t
|
|
7987
8368
|
}));
|
|
7988
8369
|
disposeFrame = () => {
|
|
7989
8370
|
keepLast.disconnect();
|