dsh-bash-terminal-ts 0.6.0 → 0.6.2

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.
@@ -90,7 +90,7 @@ function ShellPreferenceRow({ t, useStore, setShell }) {
90
90
  style: !writable ? { opacity: 0.5, cursor: "not-allowed" } : void 0,
91
91
  children: [
92
92
  t("shell." + shell),
93
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconChevronDownOutline14, { className: "btChevron" })
93
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconChevronDownOutlineMedium, { className: "btChevron" })
94
94
  ]
95
95
  }
96
96
  )
@@ -98,6 +98,7 @@ function ShellPreferenceRow({ t, useStore, setShell }) {
98
98
  )
99
99
  ] });
100
100
  }
101
+ var zhDictTitle = () => zh["shell.title"] ?? "\u7EC8\u7AEF";
101
102
  function apply(ctx) {
102
103
  ctx.effect(() => ctx.locale.register(SETTINGS_NS, { zh, en }), "bash-terminal: settings dictionaries");
103
104
  const scope = ctx.configForms.get(ENTRY_ID);
@@ -117,12 +118,13 @@ function apply(ctx) {
117
118
  bound?.sync(snap.value?.defaultShell, snap.revision, snap.writable);
118
119
  };
119
120
  ctx.slots.inject(
120
- "settings.general.item",
121
+ "dsh-family.tab",
121
122
  () => ctx.slots.register(
122
123
  {
123
- name: "settings.general.item",
124
+ name: "dsh-family.tab",
124
125
  id: "bash-terminal-shell",
125
- order: 20,
126
+ order: 60,
127
+ label: () => zhDictTitle(),
126
128
  store,
127
129
  locale: SETTINGS_NS,
128
130
  inject: (actions) => {
package/dist/client.js CHANGED
@@ -95,7 +95,7 @@ function ShellPreferenceRow({ t, useStore, setShell }) {
95
95
  style: !writable ? { opacity: 0.5, cursor: "not-allowed" } : void 0,
96
96
  children: [
97
97
  t("shell." + shell),
98
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconChevronDownOutline14, { className: "btChevron" })
98
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconChevronDownOutlineMedium, { className: "btChevron" })
99
99
  ]
100
100
  }
101
101
  )
@@ -103,6 +103,7 @@ function ShellPreferenceRow({ t, useStore, setShell }) {
103
103
  )
104
104
  ] });
105
105
  }
106
+ var zhDictTitle = () => zh["shell.title"] ?? "\u7EC8\u7AEF";
106
107
  function apply(ctx) {
107
108
  ctx.effect(() => ctx.locale.register(SETTINGS_NS, { zh, en }), "bash-terminal: settings dictionaries");
108
109
  const scope = ctx.configForms.get(ENTRY_ID);
@@ -122,12 +123,13 @@ function apply(ctx) {
122
123
  bound?.sync(snap.value?.defaultShell, snap.revision, snap.writable);
123
124
  };
124
125
  ctx.slots.inject(
125
- "settings.general.item",
126
+ "dsh-family.tab",
126
127
  () => ctx.slots.register(
127
128
  {
128
- name: "settings.general.item",
129
+ name: "dsh-family.tab",
129
130
  id: "bash-terminal-shell",
130
- order: 20,
131
+ order: 60,
132
+ label: () => zhDictTitle(),
131
133
  store,
132
134
  locale: SETTINGS_NS,
133
135
  inject: (actions) => {
package/dsh.plugin.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "dsh-bash-terminal",
3
3
  "name": "dsh-bash-terminal",
4
- "version": "0.3.18",
4
+ "version": "0.6.1",
5
5
  "description": {
6
6
  "en": "One shell tool that runs commands through PowerShell, Git Bash, MSYS2 or WSL on Windows, with a user-chosen default terminal in the Web UI settings.",
7
7
  "zh": "一个 shell 工具,可在 Windows 上通过 PowerShell / Git Bash / MSYS2 / WSL 执行命令,默认终端在 Web UI 设置中选择。"
@@ -13,7 +13,7 @@
13
13
  },
14
14
  "license": "MIT",
15
15
  "engines": {
16
- "dsh": ">=0.1.5-alpha.1 <0.2.0-0"
16
+ "dsh": ">=0.1.7-rc.1 <0.1.8-0"
17
17
  },
18
18
  "components": {
19
19
  "host": "lib/index.js",
package/lib/client.js CHANGED
@@ -95,7 +95,7 @@ function ShellPreferenceRow({ t, useStore, setShell }) {
95
95
  style: !writable ? { opacity: 0.5, cursor: "not-allowed" } : void 0,
96
96
  children: [
97
97
  t("shell." + shell),
98
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconChevronDownOutline14, { className: "btChevron" })
98
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconChevronDownOutlineMedium, { className: "btChevron" })
99
99
  ]
100
100
  }
101
101
  )
@@ -103,6 +103,7 @@ function ShellPreferenceRow({ t, useStore, setShell }) {
103
103
  )
104
104
  ] });
105
105
  }
106
+ var zhDictTitle = () => zh["shell.title"] ?? "\u7EC8\u7AEF";
106
107
  function apply(ctx) {
107
108
  ctx.effect(() => ctx.locale.register(SETTINGS_NS, { zh, en }), "bash-terminal: settings dictionaries");
108
109
  const scope = ctx.configForms.get(ENTRY_ID);
@@ -122,12 +123,13 @@ function apply(ctx) {
122
123
  bound?.sync(snap.value?.defaultShell, snap.revision, snap.writable);
123
124
  };
124
125
  ctx.slots.inject(
125
- "settings.general.item",
126
+ "dsh-family.tab",
126
127
  () => ctx.slots.register(
127
128
  {
128
- name: "settings.general.item",
129
+ name: "dsh-family.tab",
129
130
  id: "bash-terminal-shell",
130
- order: 20,
131
+ order: 60,
132
+ label: () => zhDictTitle(),
131
133
  store,
132
134
  locale: SETTINGS_NS,
133
135
  inject: (actions) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-bash-terminal-ts",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "publishConfig": {
5
5
  "tag": "dsh-0.1.7"
6
6
  },
@@ -29,7 +29,7 @@
29
29
  ],
30
30
  "engines": {
31
31
  "node": ">=22",
32
- "dsh": ">=0.1.7-rc.1 <0.2.0-0"
32
+ "dsh": ">=0.1.7-rc.1 <0.1.8-0"
33
33
  },
34
34
  "type": "module",
35
35
  "main": "lib/index.js",
@@ -74,17 +74,17 @@
74
74
  },
75
75
  "peerDependencies": {
76
76
  "@deepseek-ai/cordis": "^4.0.2",
77
- "@deepseek-ai/dsh-tools": ">=0.1.7-rc.1 <0.2.0-0",
78
- "@deepseek-ai/dsh-settings": ">=0.1.7-rc.1 <0.2.0-0",
79
- "@deepseek-ai/dsh-llm": ">=0.1.7-rc.1 <0.2.0-0",
80
- "@deepseek-ai/dsh-shell": ">=0.1.7-rc.1 <0.2.0-0",
81
- "@deepseek-ai/dsh-sandbox": ">=0.1.7-rc.1 <0.2.0-0",
82
- "@deepseek-ai/dsh-timeout": ">=0.1.7-rc.1 <0.2.0-0",
83
- "@deepseek-ai/dsh-client-store": ">=0.1.7-rc.1 <0.2.0-0",
84
- "@deepseek-ai/dsh-client-locale": ">=0.1.7-rc.1 <0.2.0-0",
85
- "@deepseek-ai/dsh-client-ui-settings": ">=0.1.7-rc.1 <0.2.0-0",
86
- "@deepseek-ai/dsh-client-ui-slots": ">=0.1.7-rc.1 <0.2.0-0",
87
- "@deepseek-ai/dsh-client-ui-primitives": ">=0.1.7-rc.1 <0.2.0-0",
77
+ "@deepseek-ai/dsh-tools": ">=0.1.7-rc.1 <0.1.8-0",
78
+ "@deepseek-ai/dsh-settings": ">=0.1.7-rc.1 <0.1.8-0",
79
+ "@deepseek-ai/dsh-llm": ">=0.1.7-rc.1 <0.1.8-0",
80
+ "@deepseek-ai/dsh-shell": ">=0.1.7-rc.1 <0.1.8-0",
81
+ "@deepseek-ai/dsh-sandbox": ">=0.1.7-rc.1 <0.1.8-0",
82
+ "@deepseek-ai/dsh-timeout": ">=0.1.7-rc.1 <0.1.8-0",
83
+ "@deepseek-ai/dsh-client-store": ">=0.1.7-rc.1 <0.1.8-0",
84
+ "@deepseek-ai/dsh-client-locale": ">=0.1.7-rc.1 <0.1.8-0",
85
+ "@deepseek-ai/dsh-client-ui-settings": ">=0.1.7-rc.1 <0.1.8-0",
86
+ "@deepseek-ai/dsh-client-ui-slots": ">=0.1.7-rc.1 <0.1.8-0",
87
+ "@deepseek-ai/dsh-client-ui-primitives": ">=0.1.7-rc.1 <0.1.8-0",
88
88
  "react": "^18.2.0"
89
89
  },
90
90
  "dependencies": {
@@ -93,23 +93,27 @@
93
93
  },
94
94
  "devDependencies": {
95
95
  "@deepseek-ai/cordis": "^4.0.2",
96
- "@deepseek-ai/dsh-client-locale": "0.1.5-rc.2",
97
- "@deepseek-ai/dsh-client-store": "0.1.5-rc.2",
98
- "@deepseek-ai/dsh-client-ui-primitives": "0.1.5-rc.2",
99
- "@deepseek-ai/dsh-client-ui-settings": "0.1.5-rc.2",
100
- "@deepseek-ai/dsh-client-ui-slots": "0.1.5-rc.2",
101
- "@deepseek-ai/dsh-llm": "0.1.5-rc.2",
102
- "@deepseek-ai/dsh-sandbox": "0.1.5-rc.2",
103
- "@deepseek-ai/dsh-settings": "0.1.5-rc.2",
104
- "@deepseek-ai/dsh-shell": "0.1.5-rc.2",
105
- "@deepseek-ai/dsh-timeout": "0.1.5-rc.2",
106
- "@deepseek-ai/dsh-tools": "0.1.5-rc.2",
96
+ "@deepseek-ai/dsh-client-locale": "0.1.7-rc.2",
97
+ "@deepseek-ai/dsh-client-store": "0.1.7-rc.2",
98
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.7-rc.2",
99
+ "@deepseek-ai/dsh-client-ui-settings": "0.1.7-rc.2",
100
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.2",
101
+ "@deepseek-ai/dsh-llm": "0.1.7-rc.2",
102
+ "@deepseek-ai/dsh-sandbox": "0.1.7-rc.2",
103
+ "@deepseek-ai/dsh-settings": "0.1.7-rc.2",
104
+ "@deepseek-ai/dsh-shell": "0.1.7-rc.2",
105
+ "@deepseek-ai/dsh-timeout": "0.1.7-rc.2",
106
+ "@deepseek-ai/dsh-tools": "0.1.7-rc.2",
107
107
  "@types/node": "^22.10.0",
108
108
  "@types/react": "^18.2.0",
109
109
  "@types/react-dom": "^18.2.0",
110
110
  "esbuild": "^0.28.2",
111
111
  "react": "^18.2.0",
112
112
  "react-dom": "^18.2.0",
113
- "typescript": "^5.9.0"
113
+ "typescript": "^5.9.0",
114
+ "@deepseek-ai/dsh-scope": "0.1.7-rc.2",
115
+ "@deepseek-ai/dsh-subprocess": "0.1.7-rc.2",
116
+ "@deepseek-ai/dsh-sandbox-policy": "0.1.7-rc.2",
117
+ "@deepseek-ai/dsh-http-proxy": "0.1.7-rc.2"
114
118
  }
115
119
  }
package/screenshots.json CHANGED
@@ -1,5 +1,5 @@
1
- {
2
- "screenshots": [
3
- "assets/shells.png"
4
- ]
5
- }
1
+ {
2
+ "screenshots": [
3
+ "assets/shells.png"
4
+ ]
5
+ }
package/src/client.tsx CHANGED
@@ -1,186 +1,192 @@
1
- // dsh-bash-terminal client plugin: a "Default terminal" preference row in the
2
- // Web UI General settings, mirroring the shipped EnterBehaviorRow grammar
3
- // (row layout, capsule selector with chevron, --dsw-* tokens).
4
- //
5
- // The row offers one entry per host shell id — powershell / gitbash / msys2 /
6
- // wsl — in the same order as SHELLS in ./index.ts.
7
- //
8
- // DSH >= 0.1.5: the browser module table (PLATFORM_MODULES) seeds react,
9
- // @deepseek-ai/cordis, @deepseek-ai/dsh-client-store, @deepseek-ai/dsh-client-ui-slots,
10
- // @deepseek-ai/dsh-client-ui-primitives and @deepseek-ai/dsh-client-ui-dockkit by
11
- // exact bare specifier. The old dsh-client-runtime name is gone from that table
12
- // (and so is its /client subpath), so this bundle requests dsh-client-store.
13
-
14
- import { useState } from "react";
15
- import { defineStore } from "@deepseek-ai/dsh-client-store";
16
- import { IconChevronDownOutline14, Menu } from "@deepseek-ai/dsh-client-ui-primitives";
17
-
18
- const SETTINGS_NS = "settings.bash-terminal";
19
- /** Entry id of this plugin in the active profile (cordis.patch.yml). */
20
- const ENTRY_ID = "tool-bash-terminal";
21
- const SHELLS = ["powershell", "gitbash", "msys2", "wsl"] as const;
22
-
23
- type ShellOption = (typeof SHELLS)[number];
24
-
25
- // Injected once when the browser loads the bundle (node tests guard on document).
26
- const ROW_CSS =
27
- ".btRow{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:16px 0;display:flex}" +
28
- ".btRowText{flex-direction:column;flex:1;gap:4px;min-width:0;padding-right:48px;display:flex}" +
29
- ".btTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}" +
30
- ".btDesc{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px}" +
31
- ".btSelector{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}" +
32
- ".btSelector:hover{background:var(--dsw-alias-interactive-bg-hover)}" +
33
- ".btChevron{flex:none}";
34
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"bash-terminal-row\"]") === null) {
35
- const tag = document.createElement("style");
36
- tag.dataset.plugin = "dsh-bash-terminal";
37
- tag.dataset.pluginCss = "bash-terminal-row";
38
- tag.textContent = ROW_CSS;
39
- document.head.appendChild(tag);
40
- }
41
-
42
- const zh: Record<string, string> = {
43
- "shell.title": "默认终端",
44
- "shell.description": "shell 工具执行命令时使用的终端",
45
- "shell.powershell": "PowerShell",
46
- "shell.gitbash": "Git Bash",
47
- "shell.msys2": "MSYS2",
48
- "shell.wsl": "WSL"
49
- };
50
- const en: Record<string, string> = {
51
- "shell.title": "Default terminal",
52
- "shell.description": "Terminal used by the shell tool",
53
- "shell.powershell": "PowerShell",
54
- "shell.gitbash": "Git Bash",
55
- "shell.msys2": "MSYS2",
56
- "shell.wsl": "WSL"
57
- };
58
-
59
- export const inject = ["slots", "locale", "configForms"];
60
-
61
- /** Row state snapshot served through the settings store. */
62
- interface RowState {
63
- shell: string;
64
- revision: number;
65
- writable: boolean;
66
- }
67
-
68
- interface ShellPreferenceRowProps {
69
- t: (key: string) => string;
70
- useStore: <S>(selector: (state: RowState) => S) => S;
71
- setShell: (id: string) => void;
72
- }
73
-
74
- function ShellPreferenceRow({ t, useStore, setShell }: ShellPreferenceRowProps) {
75
- const shell = useStore((s) => s.shell);
76
- const writable = useStore((s) => s.writable);
77
- const [open, setOpen] = useState(false);
78
- const items = SHELLS.map((id) => ({ id, label: t("shell." + id) }));
79
- return (
80
- <div className="btRow">
81
- <div className="btRowText">
82
- <div className="btTitle">{t("shell.title")}</div>
83
- <div className="btDesc">{t("shell.description")}</div>
84
- </div>
85
- <Menu
86
- open={open}
87
- onClose={() => setOpen(false)}
88
- items={items}
89
- selectedId={shell}
90
- onSelect={(id) => {
91
- setOpen(false);
92
- setShell(id);
93
- }}
94
- align="end"
95
- portal
96
- anchor={
97
- <button
98
- type="button"
99
- className="btSelector"
100
- aria-haspopup="menu"
101
- aria-expanded={open}
102
- disabled={!writable}
103
- onClick={() => setOpen(!open)}
104
- style={!writable ? { opacity: 0.5, cursor: "not-allowed" } : undefined}
105
- >
106
- {t("shell." + shell)}
107
- <IconChevronDownOutline14 className="btChevron" />
108
- </button>
109
- }
110
- />
111
- </div>
112
- );
113
- }
114
-
115
- interface SettingsSnapshot {
116
- status: "loading" | "ready" | "unavailable";
117
- value?: { defaultShell?: string };
118
- revision: number | undefined;
119
- writable: boolean;
120
- }
121
-
122
- interface ConfigFormBinding {
123
- getSnapshot(): SettingsSnapshot;
124
- subscribe(fn: () => void): () => void;
125
- set(field: string, value: string): Promise<boolean>;
126
- unset(field: string): Promise<boolean>;
127
- }
128
-
129
- interface ClientContext {
130
- effect(fn: () => unknown, name?: string): unknown;
131
- locale: { register(ns: string, dicts: Record<string, Record<string, string>>): unknown };
132
- configForms: { get(entryId: string): ConfigFormBinding };
133
- slots: {
134
- inject(slot: string, fn: () => unknown, name?: string): void;
135
- register(options: Record<string, unknown>, Component: unknown): Record<string, unknown>;
136
- };
137
- }
138
-
139
- interface RowStoreActions {
140
- sync(shell?: string, revision?: number, writable?: boolean): void;
141
- }
142
-
143
- interface RowStore {
144
- actions: RowStoreActions;
145
- }
146
-
147
- export function apply(ctx: ClientContext): void {
148
- ctx.effect(() => ctx.locale.register(SETTINGS_NS, { zh, en }), "bash-terminal: settings dictionaries");
149
- const scope = ctx.configForms.get(ENTRY_ID);
150
- const store = defineStore<RowState, { sync: (draft: RowState, shell?: string, revision?: number, writable?: boolean) => void }>({
151
- init: (): RowState => ({ shell: "powershell", revision: -1, writable: false }),
152
- actions: {
153
- sync: (d, shell, revision, writable) => {
154
- if (revision !== undefined && revision <= d.revision) return;
155
- if (shell !== undefined) d.shell = shell;
156
- if (revision !== undefined) d.revision = revision;
157
- if (writable !== undefined) d.writable = writable;
158
- }
159
- }
160
- });
161
- let bound: RowStoreActions | undefined;
162
- const push = (snap: SettingsSnapshot): void => {
163
- bound?.sync(snap.value?.defaultShell, snap.revision, snap.writable);
164
- };
165
- ctx.slots.inject(
166
- "settings.general.item",
167
- () =>
168
- ctx.slots.register(
169
- {
170
- name: "settings.general.item",
171
- id: "bash-terminal-shell",
172
- order: 20,
173
- store,
174
- locale: SETTINGS_NS,
175
- inject: (actions: unknown) => {
176
- bound = actions as RowStoreActions;
177
- push(scope.getSnapshot());
178
- return { setShell: (value: string) => void scope.set("defaultShell", value) };
179
- }
180
- },
181
- ShellPreferenceRow
182
- ),
183
- "bash-terminal: settings row"
184
- );
185
- ctx.effect(() => scope.subscribe(() => push(scope.getSnapshot())), "bash-terminal: settings watch");
186
- }
1
+ // dsh-bash-terminal client plugin: a "Default terminal" preference row in the
2
+ // Web UI General settings, mirroring the shipped EnterBehaviorRow grammar
3
+ // (row layout, capsule selector with chevron, --dsw-* tokens).
4
+ //
5
+ // The row offers one entry per host shell id — powershell / gitbash / msys2 /
6
+ // wsl — in the same order as SHELLS in ./index.ts.
7
+ //
8
+ // DSH >= 0.1.5: the browser module table (PLATFORM_MODULES) seeds react,
9
+ // @deepseek-ai/cordis, @deepseek-ai/dsh-client-store, @deepseek-ai/dsh-client-ui-slots,
10
+ // @deepseek-ai/dsh-client-ui-primitives and @deepseek-ai/dsh-client-ui-dockkit by
11
+ // exact bare specifier. The old dsh-client-runtime name is gone from that table
12
+ // (and so is its /client subpath), so this bundle requests dsh-client-store.
13
+
14
+ import { useState } from "react";
15
+ import { defineStore } from "@deepseek-ai/dsh-client-store";
16
+ import { IconChevronDownOutlineMedium, Menu } from "@deepseek-ai/dsh-client-ui-primitives";
17
+
18
+ const SETTINGS_NS = "settings.bash-terminal";
19
+ /** Entry id of this plugin in the active profile (cordis.patch.yml). */
20
+ const ENTRY_ID = "tool-bash-terminal";
21
+ const SHELLS = ["powershell", "gitbash", "msys2", "wsl"] as const;
22
+
23
+ type ShellOption = (typeof SHELLS)[number];
24
+
25
+ // Injected once when the browser loads the bundle (node tests guard on document).
26
+ const ROW_CSS =
27
+ ".btRow{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:16px 0;display:flex}" +
28
+ ".btRowText{flex-direction:column;flex:1;gap:4px;min-width:0;padding-right:48px;display:flex}" +
29
+ ".btTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}" +
30
+ ".btDesc{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px}" +
31
+ ".btSelector{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}" +
32
+ ".btSelector:hover{background:var(--dsw-alias-interactive-bg-hover)}" +
33
+ ".btChevron{flex:none}";
34
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"bash-terminal-row\"]") === null) {
35
+ const tag = document.createElement("style");
36
+ tag.dataset.plugin = "dsh-bash-terminal";
37
+ tag.dataset.pluginCss = "bash-terminal-row";
38
+ tag.textContent = ROW_CSS;
39
+ document.head.appendChild(tag);
40
+ }
41
+
42
+ const zh: Record<string, string> = {
43
+ "shell.title": "默认终端",
44
+ "shell.description": "shell 工具执行命令时使用的终端",
45
+ "shell.powershell": "PowerShell",
46
+ "shell.gitbash": "Git Bash",
47
+ "shell.msys2": "MSYS2",
48
+ "shell.wsl": "WSL"
49
+ };
50
+ const en: Record<string, string> = {
51
+ "shell.title": "Default terminal",
52
+ "shell.description": "Terminal used by the shell tool",
53
+ "shell.powershell": "PowerShell",
54
+ "shell.gitbash": "Git Bash",
55
+ "shell.msys2": "MSYS2",
56
+ "shell.wsl": "WSL"
57
+ };
58
+
59
+ export const inject = ["slots", "locale", "configForms"];
60
+
61
+ /** Row state snapshot served through the settings store. */
62
+ interface RowState {
63
+ shell: string;
64
+ revision: number;
65
+ writable: boolean;
66
+ }
67
+
68
+ interface ShellPreferenceRowProps {
69
+ t: (key: string) => string;
70
+ useStore: <S>(selector: (state: RowState) => S) => S;
71
+ setShell: (id: string) => void;
72
+ }
73
+
74
+ function ShellPreferenceRow({ t, useStore, setShell }: ShellPreferenceRowProps) {
75
+ const shell = useStore((s) => s.shell);
76
+ const writable = useStore((s) => s.writable);
77
+ const [open, setOpen] = useState(false);
78
+ const items = SHELLS.map((id) => ({ id, label: t("shell." + id) }));
79
+ return (
80
+ <div className="btRow">
81
+ <div className="btRowText">
82
+ <div className="btTitle">{t("shell.title")}</div>
83
+ <div className="btDesc">{t("shell.description")}</div>
84
+ </div>
85
+ <Menu
86
+ open={open}
87
+ onClose={() => setOpen(false)}
88
+ items={items}
89
+ selectedId={shell}
90
+ onSelect={(id) => {
91
+ setOpen(false);
92
+ setShell(id);
93
+ }}
94
+ align="end"
95
+ portal
96
+ anchor={
97
+ <button
98
+ type="button"
99
+ className="btSelector"
100
+ aria-haspopup="menu"
101
+ aria-expanded={open}
102
+ disabled={!writable}
103
+ onClick={() => setOpen(!open)}
104
+ style={!writable ? { opacity: 0.5, cursor: "not-allowed" } : undefined}
105
+ >
106
+ {t("shell." + shell)}
107
+ <IconChevronDownOutlineMedium className="btChevron" />
108
+ </button>
109
+ }
110
+ />
111
+ </div>
112
+ );
113
+ }
114
+
115
+ interface SettingsSnapshot {
116
+ status: "loading" | "ready" | "unavailable";
117
+ value?: { defaultShell?: string };
118
+ revision: number | undefined;
119
+ writable: boolean;
120
+ }
121
+
122
+ interface ConfigFormBinding {
123
+ getSnapshot(): SettingsSnapshot;
124
+ subscribe(fn: () => void): () => void;
125
+ set(field: string, value: string): Promise<boolean>;
126
+ unset(field: string): Promise<boolean>;
127
+ }
128
+
129
+ interface ClientContext {
130
+ effect(fn: () => unknown, name?: string): unknown;
131
+ locale: { register(ns: string, dicts: Record<string, Record<string, string>>): unknown };
132
+ configForms: { get(entryId: string): ConfigFormBinding };
133
+ slots: {
134
+ inject(slot: string, fn: () => unknown, name?: string): void;
135
+ register(options: Record<string, unknown>, Component: unknown): Record<string, unknown>;
136
+ };
137
+ }
138
+
139
+ interface RowStoreActions {
140
+ sync(shell?: string, revision?: number, writable?: boolean): void;
141
+ }
142
+
143
+ interface RowStore {
144
+ actions: RowStoreActions;
145
+ }
146
+
147
+ /** 账本 label:从 zh/en 字典取「默认终端」标题(无宿主 locale 依赖)。 */
148
+ const zhDictTitle = (): string => (zh as Record<string, string>)["shell.title"] ?? "终端";
149
+
150
+ export function apply(ctx: ClientContext): void {
151
+ ctx.effect(() => ctx.locale.register(SETTINGS_NS, { zh, en }), "bash-terminal: settings dictionaries");
152
+ const scope = ctx.configForms.get(ENTRY_ID);
153
+ const store = defineStore<RowState, { sync: (draft: RowState, shell?: string, revision?: number, writable?: boolean) => void }>({
154
+ init: (): RowState => ({ shell: "powershell", revision: -1, writable: false }),
155
+ actions: {
156
+ sync: (d, shell, revision, writable) => {
157
+ if (revision !== undefined && revision <= d.revision) return;
158
+ if (shell !== undefined) d.shell = shell;
159
+ if (revision !== undefined) d.revision = revision;
160
+ if (writable !== undefined) d.writable = writable;
161
+ }
162
+ }
163
+ });
164
+ let bound: RowStoreActions | undefined;
165
+ const push = (snap: SettingsSnapshot): void => {
166
+ bound?.sync(snap.value?.defaultShell, snap.revision, snap.writable);
167
+ };
168
+ // 插件族共用设置 tab(dsh-thinking-levels 顶级「起子插件设置」节声明该子席位)。
169
+ // thinking-levels 缺席时本 inject 静默等待,不阻塞客户端半。
170
+ ctx.slots.inject(
171
+ "dsh-family.tab",
172
+ () =>
173
+ ctx.slots.register(
174
+ {
175
+ name: "dsh-family.tab",
176
+ id: "bash-terminal-shell",
177
+ order: 60,
178
+ label: () => zhDictTitle(),
179
+ store,
180
+ locale: SETTINGS_NS,
181
+ inject: (actions: unknown) => {
182
+ bound = actions as RowStoreActions;
183
+ push(scope.getSnapshot());
184
+ return { setShell: (value: string) => void scope.set("defaultShell", value) };
185
+ }
186
+ },
187
+ ShellPreferenceRow
188
+ ),
189
+ "bash-terminal: settings row"
190
+ );
191
+ ctx.effect(() => scope.subscribe(() => push(scope.getSnapshot())), "bash-terminal: settings watch");
192
+ }