dsh-multi-folder 0.1.1 → 0.1.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.
package/README.md CHANGED
@@ -13,7 +13,8 @@ A [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) plugin bun
13
13
  - Under **Workspace Write** mode the agent gains the **same read / write / edit / execute permissions** on the configured secondary directories as on the primary workspace — enforced by re-rooting the session's own sandbox policy, so every mode keeps its semantics (`read-only` still denies, `workspace-write` allows, `danger-full-access` allows).
14
14
  - The directory list is **injected into the system prompt** and re-rendered per session assembly.
15
15
  - Configuration changes notify the agent through a **non-interrupting message queue** — delivered at the next message boundary (user send or tool-call end), and **only when the directory set actually changed**.
16
- - Configurable **before the session starts**: the session-creation page (new-session screen) offers a 「多工作目录」 entry that reads and edits the same per-workspace configuration through a **sessionless remote API** (`multiFolder/*` endpoints) — no session id required.
16
+ - Configurable **before the session starts**: the session-creation page (new-session screen) offers a Multi-folder entry (「多工作目录」 in the Chinese UI) that reads and edits the same per-workspace configuration through a **sessionless remote API** (`multiFolder/*` endpoints) — no session id required.
17
+ - **Localized UI.** The button, panel, and creation-page entries follow the DSH locale (the browser language or the Language setting in Settings): "Multi-folder" in English, 「多工作目录」 in Chinese.
17
18
  - **No new tools.** Everything is a framework-level change (tool-pipeline interception) plus a UI-level change (a session-scoped header entry).
18
19
 
19
20
  ## Requirements
@@ -33,7 +34,7 @@ Then **restart the DSH backend** (host composition loads at process start) and *
33
34
 
34
35
  ## Usage
35
36
 
36
- A **「多工作目录 / Multi-folder」** button appears in the session header, and a second entry appears on the **session-creation page** (fixed launcher in the bottom-right corner while the new-session screen is shown; an inline chip beside the workspace picker once the upstream `conversation.hero.workspaceExtras` slot is available). The panel lets you:
37
+ A Multi-folder button (「多工作目录」 in the Chinese UI) appears in the session header, and a second entry appears on the **session-creation page** (fixed launcher in the bottom-right corner while the new-session screen is shown; an inline chip beside the workspace picker once the upstream `conversation.hero.workspaceExtras` slot is available). The panel lets you:
37
38
 
38
39
  | Action | Behavior |
39
40
  | ------ | -------- |
package/README.zh.md CHANGED
@@ -13,7 +13,8 @@
13
13
  - 在 **Workspace Write** 模式下,Agent 对配置的副工作目录拥有与主工作目录**同等的读取、写入、编辑与命令执行权限**——实现方式是重定向会话自身的沙箱策略根,因此每种模式语义都自然保持(`read-only` 依旧拒绝、`workspace-write` 放行、`danger-full-access` 放行);
14
14
  - 目录列表**注入系统提示词**,每次组装按会话求值;
15
15
  - 配置变更通过**不打断的消息队列**通知 Agent——在下一次消息边界(用户发送或工具调用结束)送达,且**仅在目录集合实际变化时**发送;
16
- - **会话开始前即可配置**:会话创建页(新会话界面)提供「多工作目录」入口,通过**无会话远程 API**(`multiFolder/*` 端点)读写同一份 per-workspace 配置——无需 session id;
16
+ - **会话开始前即可配置**:会话创建页(新会话界面)提供「多工作目录」入口(英文界面显示 "Multi-folder"),通过**无会话远程 API**(`multiFolder/*` 端点)读写同一份 per-workspace 配置——无需 session id;
17
+ - **界面本地化**:按钮、面板与创建页入口跟随 DSH 的语言设置(浏览器语言或设置中的 Language 选项):英文界面显示 "Multi-folder",中文界面显示「多工作目录」。
17
18
  - **不新增任何工具**:改动全部位于框架级(工具流水线拦截)与 UI 级(会话级头部入口)。
18
19
 
19
20
  ## 环境要求
@@ -33,7 +34,7 @@ dsh plugin --profile web add dsh-multi-folder
33
34
 
34
35
  ## 使用
35
36
 
36
- 会话头部出现「多工作目录」按钮;**会话创建页**也有入口(新会话界面右下角的浮动按钮;当上游 DSH 声明 `conversation.hero.workspaceExtras` 插槽后,还会在工作区选择器旁显示内联 chip)。打开面板即可:
37
+ 会话头部出现「多工作目录」按钮(英文界面显示 "Multi-folder");**会话创建页**也有入口(新会话界面右下角的浮动按钮;当上游 DSH 声明 `conversation.hero.workspaceExtras` 插槽后,还会在工作区选择器旁显示内联 chip)。打开面板即可:
37
38
 
38
39
  | 操作 | 行为 |
39
40
  | ---- | ---- |
package/docs/design.md CHANGED
@@ -145,14 +145,28 @@ window.__ModuleLoader__.load({
145
145
  })
146
146
  ```
147
147
 
148
- - `inject: ['remote', 'remote.commands', 'slots', 'workspaces', 'connection', 'sessions']`; the package's
148
+ - `inject: ['remote', 'remote.commands', 'slots', 'workspaces', 'connection', 'sessions', 'locale']`; the package's
149
149
  `dsh.client.inject` lists the packages providing them
150
150
  (`@deepseek-ai/dsh-api-gateway`, `@deepseek-ai/dsh-api-remotes`,
151
- `@deepseek-ai/dsh-client-connection`, `@deepseek-ai/dsh-client-runtime`).
151
+ `@deepseek-ai/dsh-client-connection`, `@deepseek-ai/dsh-client-locale`,
152
+ `@deepseek-ai/dsh-client-runtime`).
152
153
  - UI registrations: `conversation.session.header.actions` (session-scoped button),
153
154
  `shell.overlay` panel, `shell.overlay` hero launcher (root-scoped, fixed
154
155
  position), and `conversation.hero.workspaceExtras` (upstream slot; see
155
156
  below). One module-level store is shared by all of them.
157
+ - **Localization (zh / en).** All client copy goes through
158
+ `@deepseek-ai/dsh-client-locale` (always composed by the standard web
159
+ profile). The bundle registers a `multi-folder` dictionary namespace with
160
+ `ctx.effect(() => locale.register(NS, { zh, en }))` — the locale service
161
+ enforces bilingual balance, and the effect ties the dictionaries to the
162
+ plugin fiber. Every slot registration declares `locale: 'multi-folder'`,
163
+ so the renderer synthesizes the `t` seat on component props and
164
+ re-renders mounted outlets on locale switch; list-entry `label`s are
165
+ thunks (`() => t('label')`) that `resolveSlotLabel` re-evaluates per read,
166
+ so registration-time text follows the active locale without
167
+ re-registering. The active locale is the browser language or the user's
168
+ Language preference in Settings; the English UI reads "Multi-folder", the
169
+ Chinese UI keeps 「多工作目录」.
156
170
  - Host communication, two channels:
157
171
  - session mode: `ctx.remote.commands.execute(sessionId, line)`. The return
158
172
  value is the RPC envelope `{ ok, value }` where `value` is the
@@ -78,17 +78,23 @@ above is only authorized once it is listed):
78
78
 
79
79
  ## 4. Plugin side (already implemented)
80
80
 
81
- `lib/client.js` registers into the slot the moment it is declared:
81
+ `lib/client.js` registers into the slot the moment it is declared. The entry
82
+ declares the plugin's `multi-folder` locale namespace, so the chip renders
83
+ through the framework `t` seat and its `label` is a thunk that follows the
84
+ active locale (English: "Multi-folder", Chinese: 「多工作目录」):
82
85
 
83
86
  ```js
84
87
  slots.inject('conversation.hero.workspaceExtras', function () {
85
88
  return slots.register(
86
- { name: 'conversation.hero.workspaceExtras', id: 'multi-folder', order: 30, label: '多工作目录' },
89
+ { name: 'conversation.hero.workspaceExtras', id: 'multi-folder', order: 30, label: () => t('label'), locale: NS },
87
90
  function (props) { return React.createElement(HeroChip, props); },
88
91
  );
89
92
  });
90
93
  ```
91
94
 
95
+ (`NS` is the plugin's `multi-folder` namespace and `t` its bound translator —
96
+ see the localization section of [design.md](design.md).)
97
+
92
98
  `HeroChip` prefers the owner-supplied `props.workspacePath` and falls back to
93
99
  the store-derived hero workspace. It opens the same overlay panel in workspace
94
100
  mode, which reads and writes the configuration through the sessionless
@@ -115,6 +121,7 @@ bundle; the same change there is three touchpoints:
115
121
  - Unit: the plugin's `test/smoke-client.mjs` registers and drives `HeroChip`
116
122
  with a mock owner share (`workspacePath`), asserting the panel opens in
117
123
  workspace mode and reuses the per-workspace cache.
118
- - Manual: with the patched build, the 「多工作目录」 chip renders in the hero
119
- workspace row between the preset chip and the composer; clicking it lists
120
- the workspace's secondary directories before any message is sent.
124
+ - Manual: with the patched build, the Multi-folder chip (「多工作目录」 in the
125
+ Chinese UI) renders in the hero workspace row between the preset chip and
126
+ the composer; clicking it lists the workspace's secondary directories
127
+ before any message is sent.
package/lib/client.js CHANGED
@@ -1,9 +1,11 @@
1
1
  /**
2
2
  * dsh-multi-folder — client half (hand-written factory bundle, no build step).
3
3
  *
4
- * Session-scoped UI: a "多工作目录" button in the conversation session header
5
- * (`conversation.session.header.actions`, scope: session) that opens a panel in
6
- * `shell.overlay` listing the project's secondary working directories.
4
+ * Session-scoped UI: a localized "Multi-folder" (「多工作目录」) button in the
5
+ * conversation session header (`conversation.session.header.actions`, scope:
6
+ * session) that opens a panel in `shell.overlay` listing the project's
7
+ * secondary working directories. All copy goes through the framework locale
8
+ * service (`@deepseek-ai/dsh-client-locale`) — see the "i18n" section below.
7
9
  * Mutations go through the Host `/multi-folder` command via the Remote BFF
8
10
  * (`ctx.remote.commands.execute(sessionId, line)`); the Host answers with a
9
11
  * human-readable result carrying a `[MF:JSON]` line the panel parses for
@@ -62,9 +64,60 @@ window.__ModuleLoader__.load({
62
64
  try { return JSON.parse(m[1]); } catch (_) { return null; }
63
65
  }
64
66
 
67
+ // ------------------------------------------------------------- i18n
68
+ /** Locale namespace owned by this plugin. Dictionaries are registered
69
+ * with the `locale` service (`@deepseek-ai/dsh-client-locale`, always
70
+ * composed by the standard web profile), which enforces bilingual
71
+ * balance: both shipped locales (zh, en) must be registered together. */
72
+ var NS = 'multi-folder';
73
+ /** Simplified Chinese dictionary — the key-set source of truth. */
74
+ var zhDict = {
75
+ 'label': '多工作目录',
76
+ 'label.open': '多工作目录 ▾',
77
+ 'label.heroLauncher': '多工作目录(新会话)',
78
+ 'title.header': '多工作目录(副工作目录)',
79
+ 'title.remove': '移除此副工作目录',
80
+ 'title.close': '关闭',
81
+ 'title.heroLauncher.hasWorkspace': '配置此项目的副工作目录(多工作目录)',
82
+ 'title.heroLauncher.noWorkspace': '请先选择工作区,再配置多工作目录',
83
+ 'title.heroChip': '配置此项目的副工作目录(多工作目录)',
84
+ 'panel.title': '多工作目录(副工作目录)',
85
+ 'panel.project': '项目:{path}',
86
+ 'panel.noWorkspaceHint': '尚未选择工作区。请先在上方选择项目,再配置多工作目录。',
87
+ 'panel.empty': '尚未配置副工作目录。',
88
+ 'panel.pickWorkspaceHint': '选择工作区后可在此添加副工作目录。',
89
+ 'panel.add': '+ 添加目录',
90
+ 'panel.adding': '处理中…',
91
+ 'panel.remove': '移除',
92
+ 'panel.refresh': '刷新',
93
+ 'panel.footnote': 'Agent 的主工作目录不变;在 Workspace Write 模式下,Agent 对上述目录拥有与主工作目录同等的读写与命令执行权限。配置变更会在下一条消息或工具调用结束时通知 Agent。',
94
+ };
95
+ /** English dictionary — checked complete against the zh key set. */
96
+ var enDict = {
97
+ 'label': 'Multi-folder',
98
+ 'label.open': 'Multi-folder ▾',
99
+ 'label.heroLauncher': 'Multi-folder (new session)',
100
+ 'title.header': 'Multi-folder (secondary working directories)',
101
+ 'title.remove': 'Remove this secondary working directory',
102
+ 'title.close': 'Close',
103
+ 'title.heroLauncher.hasWorkspace': 'Configure secondary working directories for this project (Multi-folder)',
104
+ 'title.heroLauncher.noWorkspace': 'Pick a workspace first, then configure Multi-folder',
105
+ 'title.heroChip': 'Configure secondary working directories for this project (Multi-folder)',
106
+ 'panel.title': 'Multi-folder (secondary working directories)',
107
+ 'panel.project': 'Workspace: {path}',
108
+ 'panel.noWorkspaceHint': 'No workspace selected yet. Pick a project above first, then configure secondary directories.',
109
+ 'panel.empty': 'No secondary working directories configured yet.',
110
+ 'panel.pickWorkspaceHint': 'Pick a workspace first — secondary directories can be added here afterwards.',
111
+ 'panel.add': '+ Add directory',
112
+ 'panel.adding': 'Working…',
113
+ 'panel.remove': 'Remove',
114
+ 'panel.refresh': 'Refresh',
115
+ 'panel.footnote': "The agent's primary working directory stays unchanged; under Workspace Write mode the agent has the same read/write and command-execution rights on the listed directories as on the primary workspace. Configuration changes are announced at the next message or tool-call boundary.",
116
+ };
117
+
65
118
  // ------------------------------------------------------------- plugin
66
119
  var name = 'dsh-multi-folder';
67
- var inject = ['remote', 'remote.commands', 'slots', 'workspaces', 'connection', 'sessions'];
120
+ var inject = ['remote', 'remote.commands', 'slots', 'workspaces', 'connection', 'sessions', 'locale'];
68
121
 
69
122
  function apply(ctx) {
70
123
  var slots = ctx.slots;
@@ -72,6 +125,22 @@ window.__ModuleLoader__.load({
72
125
  var workspaces = ctx.workspaces;
73
126
  var connection = ctx.connection;
74
127
  var sessions = ctx.sessions;
128
+ var locale = ctx.locale;
129
+
130
+ /** Register this plugin's dictionaries for every shipped locale
131
+ * (bilingual balance is enforced by the locale service). The
132
+ * registration is an effect on this plugin's fiber, so unloading the
133
+ * plugin withdraws the dictionaries. */
134
+ ctx.effect(function () {
135
+ return locale.register(NS, { zh: zhDict, en: enDict });
136
+ }, 'dsh-multi-folder: client dictionaries');
137
+
138
+ /** Bound translator for slot labels: label thunks re-evaluate per read
139
+ * (`resolveSlotLabel`), so registration-time text follows the active
140
+ * locale without re-registering. Components themselves render through
141
+ * the `t` seat the renderer synthesizes from the declared
142
+ * `locale:` namespace (which also re-renders them on locale switch). */
143
+ var t = locale.bind(NS);
75
144
 
76
145
  // -------------------------------------------------- sessionless RPC
77
146
  /** Call one `multiFolder/*` endpoint over the shared /api channel.
@@ -232,6 +301,7 @@ window.__ModuleLoader__.load({
232
301
  function HeaderButton(props) {
233
302
  var store = useStore();
234
303
  var sessionId = props.sessionId;
304
+ var t = props.t;
235
305
  var open = store.open && store.sessionId === sessionId;
236
306
  // Session switch: keep the panel in sync with the session this
237
307
  // header belongs to. On a changed sessionId (or first mount) with
@@ -250,7 +320,7 @@ window.__ModuleLoader__.load({
250
320
  'button',
251
321
  {
252
322
  type: 'button',
253
- title: '多工作目录(副工作目录)',
323
+ title: t('title.header'),
254
324
  onClick: function () {
255
325
  var current = getSnapshot();
256
326
  var isOpen = current.open && current.sessionId === sessionId;
@@ -273,14 +343,15 @@ window.__ModuleLoader__.load({
273
343
  cursor: 'pointer',
274
344
  },
275
345
  },
276
- open ? '多工作目录 ▾' : '多工作目录',
346
+ open ? t('label.open') : t('label'),
277
347
  );
278
348
  return btn;
279
349
  }
280
350
 
281
351
  // Overlay panel ------------------------------------------------------
282
- function Panel() {
352
+ function Panel(props) {
283
353
  var store = useStore();
354
+ var t = props.t;
284
355
  if (!store.open || !store.mode) return null;
285
356
  var sessionMode = store.mode === 'session';
286
357
  var usable = sessionMode || !!store.workspace;
@@ -311,7 +382,7 @@ window.__ModuleLoader__.load({
311
382
  'button',
312
383
  {
313
384
  type: 'button',
314
- title: '移除此副工作目录',
385
+ title: t('title.remove'),
315
386
  disabled: !usable,
316
387
  onClick: function () {
317
388
  if (sessionMode) {
@@ -322,7 +393,7 @@ window.__ModuleLoader__.load({
322
393
  },
323
394
  style: { padding: '2px 8px', borderRadius: 6, border: '1px solid transparent', background: 'transparent', color: 'var(--color-danger, #c62828)', cursor: 'pointer' },
324
395
  },
325
- '移除',
396
+ t('panel.remove'),
326
397
  ),
327
398
  );
328
399
  });
@@ -348,12 +419,12 @@ window.__ModuleLoader__.load({
348
419
  React.createElement(
349
420
  'div',
350
421
  { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10 } },
351
- React.createElement('div', { style: { fontWeight: 600, fontSize: 14 } }, '多工作目录(副工作目录)'),
422
+ React.createElement('div', { style: { fontWeight: 600, fontSize: 14 } }, t('panel.title')),
352
423
  React.createElement(
353
424
  'button',
354
425
  {
355
426
  type: 'button',
356
- title: '关闭',
427
+ title: t('title.close'),
357
428
  onClick: function () { patch({ open: false }); },
358
429
  style: { padding: '2px 8px', borderRadius: 6, border: '1px solid transparent', background: 'transparent', cursor: 'pointer' },
359
430
  },
@@ -364,18 +435,18 @@ window.__ModuleLoader__.load({
364
435
  ? React.createElement(
365
436
  'div',
366
437
  { style: { marginBottom: 8, color: 'var(--color-text-muted, #6b6b6b)', fontSize: 12, wordBreak: 'break-all' } },
367
- '项目:' + store.workspace,
438
+ t('panel.project', { path: store.workspace }),
368
439
  )
369
440
  : (!sessionMode
370
441
  ? React.createElement(
371
442
  'div',
372
443
  { style: { marginBottom: 8, color: 'var(--color-text-muted, #6b6b6b)' } },
373
- '尚未选择工作区。请先在上方选择项目,再配置多工作目录。',
444
+ t('panel.noWorkspaceHint'),
374
445
  )
375
446
  : null),
376
447
  usable
377
- ? (rows.length > 0 ? rows : React.createElement('div', { style: { marginBottom: 8, color: 'var(--color-text-muted, #6b6b6b)' } }, '尚未配置副工作目录。'))
378
- : React.createElement('div', { style: { marginBottom: 8, color: 'var(--color-text-muted, #6b6b6b)' } }, '选择工作区后可在此添加副工作目录。'),
448
+ ? (rows.length > 0 ? rows : React.createElement('div', { style: { marginBottom: 8, color: 'var(--color-text-muted, #6b6b6b)' } }, t('panel.empty')))
449
+ : React.createElement('div', { style: { marginBottom: 8, color: 'var(--color-text-muted, #6b6b6b)' } }, t('panel.pickWorkspaceHint')),
379
450
  store.error
380
451
  ? React.createElement('div', { style: { marginBottom: 8, color: 'var(--color-danger, #c62828)', whiteSpace: 'pre-wrap' } }, String(store.error))
381
452
  : null,
@@ -399,7 +470,7 @@ window.__ModuleLoader__.load({
399
470
  opacity: store.busy || !usable ? 0.6 : 1,
400
471
  },
401
472
  },
402
- store.busy ? '处理中…' : '+ 添加目录',
473
+ store.busy ? t('panel.adding') : t('panel.add'),
403
474
  ),
404
475
  React.createElement(
405
476
  'button',
@@ -423,7 +494,7 @@ window.__ModuleLoader__.load({
423
494
  opacity: store.busy || !usable ? 0.6 : 1,
424
495
  },
425
496
  },
426
- '刷新',
497
+ t('panel.refresh'),
427
498
  ),
428
499
  ),
429
500
  React.createElement(
@@ -436,7 +507,7 @@ window.__ModuleLoader__.load({
436
507
  color: 'var(--color-text-muted, #6b6b6b)',
437
508
  },
438
509
  },
439
- 'Agent 的主工作目录不变;在 Workspace Write 模式下,Agent 对上述目录拥有与主工作目录同等的读写与命令执行权限。配置变更会在下一条消息或工具调用结束时通知 Agent。',
510
+ t('panel.footnote'),
440
511
  ),
441
512
  );
442
513
  }
@@ -477,8 +548,9 @@ window.__ModuleLoader__.load({
477
548
 
478
549
  /** Fixed-position hero launcher (B2): visible only while the
479
550
  * conversation root reports the hero phase. */
480
- function HeroLauncher() {
551
+ function HeroLauncher(props) {
481
552
  var store = useStore();
553
+ var t = props.t;
482
554
  React.useEffect(
483
555
  function () {
484
556
  var disposers = [];
@@ -511,7 +583,7 @@ window.__ModuleLoader__.load({
511
583
  'button',
512
584
  {
513
585
  type: 'button',
514
- title: store.heroWorkspace ? '配置此项目的副工作目录(多工作目录)' : '请先选择工作区,再配置多工作目录',
586
+ title: store.heroWorkspace ? t('title.heroLauncher.hasWorkspace') : t('title.heroLauncher.noWorkspace'),
515
587
  onClick: function () { openForWorkspace(store.heroWorkspace); },
516
588
  style: {
517
589
  position: 'fixed',
@@ -532,7 +604,7 @@ window.__ModuleLoader__.load({
532
604
  opacity: store.heroWorkspace ? 1 : 0.7,
533
605
  },
534
606
  },
535
- '多工作目录',
607
+ t('label'),
536
608
  );
537
609
  }
538
610
 
@@ -541,12 +613,13 @@ window.__ModuleLoader__.load({
541
613
  * declares the slot; a no-op registration until then. */
542
614
  function HeroChip(props) {
543
615
  var store = useStore();
616
+ var t = props.t;
544
617
  var workspacePath = props && props.workspacePath ? props.workspacePath : store.heroWorkspace;
545
618
  return React.createElement(
546
619
  'button',
547
620
  {
548
621
  type: 'button',
549
- title: '配置此项目的副工作目录(多工作目录)',
622
+ title: t('title.heroChip'),
550
623
  onClick: function () { openForWorkspace(workspacePath); },
551
624
  style: {
552
625
  display: 'inline-flex',
@@ -561,34 +634,34 @@ window.__ModuleLoader__.load({
561
634
  cursor: 'pointer',
562
635
  },
563
636
  },
564
- '多工作目录',
637
+ t('label'),
565
638
  );
566
639
  }
567
640
 
568
641
  // Registrations ------------------------------------------------------
569
642
  slots.inject('conversation.session.header.actions', function () {
570
643
  return slots.register(
571
- { name: 'conversation.session.header.actions', id: 'multi-folder', order: 30, label: '多工作目录' },
644
+ { name: 'conversation.session.header.actions', id: 'multi-folder', order: 30, label: function () { return t('label'); }, locale: NS },
572
645
  function (props) { return React.createElement(HeaderButton, props); },
573
646
  );
574
647
  });
575
648
  slots.inject('shell.overlay', function () {
576
649
  return slots.register(
577
- { name: 'shell.overlay', id: 'multi-folder', order: 100, label: '多工作目录' },
578
- function () { return React.createElement(Panel); },
650
+ { name: 'shell.overlay', id: 'multi-folder', order: 100, label: function () { return t('label'); }, locale: NS },
651
+ function (props) { return React.createElement(Panel, props); },
579
652
  );
580
653
  });
581
654
  slots.inject('shell.overlay', function () {
582
655
  return slots.register(
583
- { name: 'shell.overlay', id: 'multi-folder-hero', order: 200, label: '多工作目录(新会话)' },
584
- function () { return React.createElement(HeroLauncher); },
656
+ { name: 'shell.overlay', id: 'multi-folder-hero', order: 200, label: function () { return t('label.heroLauncher'); }, locale: NS },
657
+ function (props) { return React.createElement(HeroLauncher, props); },
585
658
  );
586
659
  });
587
660
  // Upstream slot (B1): the callback fires only once a DSH build declares
588
661
  // `conversation.hero.workspaceExtras`; until then this contributes nothing.
589
662
  slots.inject('conversation.hero.workspaceExtras', function () {
590
663
  return slots.register(
591
- { name: 'conversation.hero.workspaceExtras', id: 'multi-folder', order: 30, label: '多工作目录' },
664
+ { name: 'conversation.hero.workspaceExtras', id: 'multi-folder', order: 30, label: function () { return t('label'); }, locale: NS },
592
665
  function (props) { return React.createElement(HeroChip, props); },
593
666
  );
594
667
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-multi-folder",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "DeepSeek Harness plugin: secondary working directories for a project. The agent keeps the primary workspace as cwd, gains equal write/exec permissions on configured secondary directories under workspace-write mode, and is notified of configuration changes at the next message boundary. Configurable from the session header AND from the session-creation page (before the first message) through a sessionless multiFolder remote API.",
5
5
  "keywords": [
6
6
  "dsh-plugin",
@@ -48,6 +48,7 @@
48
48
  "@deepseek-ai/dsh-api-gateway",
49
49
  "@deepseek-ai/dsh-api-remotes",
50
50
  "@deepseek-ai/dsh-client-connection",
51
+ "@deepseek-ai/dsh-client-locale",
51
52
  "@deepseek-ai/dsh-client-runtime"
52
53
  ]
53
54
  }