@shendeguize/dsh-agent-sidecar 0.1.0 → 0.2.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/README.md +78 -11
- package/lib/client.js +4708 -2932
- package/lib/client.js.map +1 -1
- package/package.json +3 -1
- package/src/client/analysis/AnalysisPanel.tsx +19 -27
- package/src/client/analysis/analysis.module.css +36 -97
- package/src/client/board/Board.tsx +255 -48
- package/src/client/board/board.module.css +113 -92
- package/src/client/board/logic.ts +99 -21
- package/src/client/board/project-view-logic.ts +27 -34
- package/src/client/board/project-view.module.css +46 -83
- package/src/client/board/project-view.tsx +50 -8
- package/src/client/board/strings.ts +70 -85
- package/src/client/commands.ts +30 -15
- package/src/client/controller.ts +27 -7
- package/src/client/detail/SessionDetail.tsx +234 -37
- package/src/client/detail/detail.module.css +100 -153
- package/src/client/detail/logic.ts +220 -29
- package/src/client/detail/strings.ts +66 -77
- package/src/client/detail-glue.ts +33 -0
- package/src/client/detail-view.module.css +43 -35
- package/src/client/detail-view.tsx +138 -118
- package/src/client/dsh-tools/LineageTree.tsx +22 -13
- package/src/client/dsh-tools/SearchPanel.tsx +18 -11
- package/src/client/dsh-tools/dsh-tools.module.css +53 -140
- package/src/client/dsh-tools/strings.ts +42 -77
- package/src/client/index.ts +285 -124
- package/src/client/inject/InjectPanel.tsx +81 -61
- package/src/client/inject/inject.module.css +97 -197
- package/src/client/inject/logic.ts +38 -0
- package/src/client/lifecycle/handoff.ts +83 -0
- package/src/client/locales/en.ts +91 -4
- package/src/client/locales/host.ts +131 -0
- package/src/client/locales/index.ts +196 -8
- package/src/client/locales/react.ts +10 -0
- package/src/client/locales/view.ts +38 -0
- package/src/client/locales/zh.ts +200 -125
- package/src/client/mount.tsx +75 -41
- package/src/client/navigation/CenterOverlay.tsx +40 -0
- package/src/client/navigation/center-overlay.module.css +51 -0
- package/src/client/navigation/center.ts +45 -0
- package/src/client/navigation/modal-isolation.ts +152 -0
- package/src/client/navigation/modal-surface-anchor.ts +72 -0
- package/src/client/navigation/sidebar-entry.module.css +73 -0
- package/src/client/navigation/sidebar-entry.ts +309 -0
- package/src/client/primitives/StaticPill.tsx +21 -0
- package/src/client/settings-card.module.css +35 -119
- package/src/client/settings-card.tsx +31 -153
- package/src/client/settings-fields.tsx +131 -0
- package/src/client/sidebar/SidebarTab.tsx +156 -0
- package/src/client/sidebar/model.ts +65 -0
- package/src/client/sidebar/sidebar-tab.module.css +118 -0
- package/src/client/sidebar-tab.tsx +46 -320
- package/src/client/theme/agsc.module.css +31 -0
- package/src/client/theme/parts.ts +56 -0
- package/src/client/ui-integration.ts +86 -0
- package/src/client/widget.tsx +42 -16
- package/src/client/inject/overlay.module.css +0 -22
package/src/client/locales/zh.ts
CHANGED
|
@@ -8,28 +8,21 @@
|
|
|
8
8
|
* {@link SidecarLocaleDomain}. `settings.*` is owned by the settings card
|
|
9
9
|
* (T2.3); `inject.*` by the inject panel (T4.5, src/client/inject/);
|
|
10
10
|
* `board.*` by the board-tab chrome (view switcher); `detail.*` /
|
|
11
|
-
* `dshtools.*` / `project.*`
|
|
11
|
+
* `dshtools.*` / `project.*` by their corresponding feature surfaces;
|
|
12
12
|
* `analysis.*` is owned by the analysis panel (T5.10b); `command.*` by the
|
|
13
13
|
* `/sidecar` slash command (T4.6, re-exported via ./command.ts);
|
|
14
|
-
* `sidebar.*` by the optional better-sidebar
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* `sidebar.*` by the plain-DOM Agent Center entry and optional better-sidebar
|
|
15
|
+
* mini tab (T6.3) — keeping the prefixes in one flat namespace avoids
|
|
16
|
+
* cross-task collisions.
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* them entry-by-entry so `t()` covers every M3 string with zero copy drift
|
|
22
|
-
* (parity is pinned by test/locales.test.ts). en translations for those
|
|
23
|
-
* domains live in ./en.ts (the module tables are zh-only).
|
|
18
|
+
* This main dictionary is the source of truth for shipped Chinese copy.
|
|
19
|
+
* Feature-local modules expose dynamic, view-shaped facades over these flat
|
|
20
|
+
* keys; this locale core never depends back on UI modules.
|
|
24
21
|
*
|
|
25
22
|
* Copy sources: the schemastery `.description()` strings in src/config.ts
|
|
26
23
|
* (the authoritative zh copy for each field) and design doc §4.a/§5.3/§6/§8.
|
|
27
24
|
*/
|
|
28
25
|
|
|
29
|
-
import { DETAIL_STRINGS } from '../detail/strings.ts'
|
|
30
|
-
import { DSH_TOOLS_STRINGS } from '../dsh-tools/strings.ts'
|
|
31
|
-
import { PROJECT_VIEW_STRINGS } from '../board/project-view-logic.ts'
|
|
32
|
-
|
|
33
26
|
/** Locale key domains (one owner surface per domain, see module doc). */
|
|
34
27
|
export type SidecarLocaleDomain =
|
|
35
28
|
| 'settings'
|
|
@@ -42,10 +35,6 @@ export type SidecarLocaleDomain =
|
|
|
42
35
|
| 'command'
|
|
43
36
|
| 'sidebar'
|
|
44
37
|
|
|
45
|
-
const D = DETAIL_STRINGS
|
|
46
|
-
const Q = DSH_TOOLS_STRINGS
|
|
47
|
-
const P = PROJECT_VIEW_STRINGS
|
|
48
|
-
|
|
49
38
|
export const zh = {
|
|
50
39
|
// ── card chrome ────────────────────────────────────────────────────────
|
|
51
40
|
'settings.cardTitle': 'Agent Sidecar',
|
|
@@ -107,7 +96,7 @@ export const zh = {
|
|
|
107
96
|
'settings.sectionInject': '消息注入',
|
|
108
97
|
'settings.injectEnabledLabel': '启用注入',
|
|
109
98
|
'settings.injectEnabledHint':
|
|
110
|
-
'
|
|
99
|
+
'关闭后注入面板为只读禁用态,写接口在服务端同步拒绝。',
|
|
111
100
|
'settings.injectDefaultModeLabel': '默认注入模式',
|
|
112
101
|
'settings.injectDefaultModeHint': '注入面板打开时预选的模式。',
|
|
113
102
|
'settings.injectModeQueue': 'queue(排队下一轮)',
|
|
@@ -179,6 +168,7 @@ export const zh = {
|
|
|
179
168
|
'结果未知:消息可能已投递。请勿重试;请前往目标会话核对后再决定下一步。',
|
|
180
169
|
'inject.resultReplayed': '幂等重放:返回的是此前同一请求的结果,未发生二次注入。',
|
|
181
170
|
'inject.reprepare': '重新准备',
|
|
171
|
+
'inject.observeListen': '开启监听观察反应',
|
|
182
172
|
|
|
183
173
|
// ── inject panel: error vocabulary (gateway + transport) ─────────────
|
|
184
174
|
'inject.errInjectDisabled': '注入功能已在服务端关闭;请在设置中开启注入。',
|
|
@@ -198,120 +188,203 @@ export const zh = {
|
|
|
198
188
|
'inject.errParse': '服务端响应无法解析。',
|
|
199
189
|
'inject.errGeneric': '请求失败({code})。',
|
|
200
190
|
|
|
201
|
-
// ── board tab chrome
|
|
191
|
+
// ── board tab chrome + session board (design §5.1 / §5.3) ──────────────
|
|
202
192
|
'board.viewBoard': '会话看板',
|
|
203
193
|
'board.viewProjects': '项目视图',
|
|
194
|
+
'board.status.working': '工作中',
|
|
195
|
+
'board.status.waiting': '等待中',
|
|
196
|
+
'board.status.idle': '空闲',
|
|
197
|
+
'board.status.dead': '已结束',
|
|
198
|
+
'board.status.unknown': '未知',
|
|
199
|
+
'board.attention.gap': '事件缺口',
|
|
200
|
+
'board.daemon.probe': '探测中',
|
|
201
|
+
'board.daemon.adopted': '已连接 · 领养',
|
|
202
|
+
'board.daemon.defer': '等待系统服务',
|
|
203
|
+
'board.daemon.reprobe': '重新探测中',
|
|
204
|
+
'board.daemon.hosting': '正在启动',
|
|
205
|
+
'board.daemon.hosted': '已连接 · 托管',
|
|
206
|
+
'board.daemon.backoff': '重启退避中',
|
|
207
|
+
'board.daemon.failed': '离线',
|
|
208
|
+
'board.stream.ok': '实时流正常',
|
|
209
|
+
'board.stream.degraded': '实时流重连中',
|
|
210
|
+
'board.stream.unknown': '实时流未建立',
|
|
211
|
+
'board.banner.daemonFailed': 'sidecar 离线:看板显示最后一次快照,数据不再更新',
|
|
212
|
+
'board.banner.streamDegraded': '实时流重连中,数据可能滞后',
|
|
213
|
+
'board.empty.daemonFailedTitle': 'sidecar 已离线',
|
|
214
|
+
'board.empty.daemonFailedHint':
|
|
215
|
+
'daemon 连续启动失败已熔断。可在设置卡重试,或手动运行 agent-sidecar daemon start 后等待自动领养。',
|
|
216
|
+
'board.empty.daemonDeferTitle': '等待系统服务拉起 daemon',
|
|
217
|
+
'board.empty.daemonDeferHint':
|
|
218
|
+
'检测到 LaunchAgent 托管,插件不会自行启动 daemon;服务拉起后看板会自动出数。',
|
|
219
|
+
'board.empty.filteredTitle': '当前过滤条件下没有会话',
|
|
220
|
+
'board.empty.filteredHint': '试试放宽时间窗、取消状态过滤,或打开「显示已结束」。',
|
|
221
|
+
'board.empty.noSessionsTitle': '暂无被观测的会话',
|
|
222
|
+
'board.empty.noSessionsHint':
|
|
223
|
+
'本机 agent(claude / codex / cursor / dsh …)开始工作后会自动出现在这里。',
|
|
224
|
+
'board.topbar.title': 'Sidecar 多 agent 看板',
|
|
225
|
+
'board.topbar.refresh': '刷新',
|
|
226
|
+
'board.topbar.refreshing': '刷新中…',
|
|
227
|
+
'board.topbar.refreshTitle': '手动拉取最新快照',
|
|
228
|
+
'board.topbar.refreshFailed': '手动刷新失败,看板仍显示原有快照',
|
|
229
|
+
'board.topbar.dismiss': '知道了',
|
|
230
|
+
'board.topbar.showDead': '显示已结束',
|
|
231
|
+
'board.topbar.timeWindow': '时间窗',
|
|
232
|
+
'board.topbar.countWorking': '{n} 工作中',
|
|
233
|
+
'board.topbar.countWaiting': '{n} 等待中',
|
|
234
|
+
'board.topbar.countTotal': '共 {n} 个会话',
|
|
235
|
+
'board.topbar.filterByStatusTitle': '只看「{label}」的会话',
|
|
236
|
+
'board.topbar.clearStatusFilterTitle': '取消状态过滤,显示全部会话',
|
|
237
|
+
'board.group.collapseTitle': '收起该分组',
|
|
238
|
+
'board.group.expandTitle': '展开该分组',
|
|
239
|
+
'board.group.showAll': '展开全部 {n} 个会话',
|
|
240
|
+
'board.group.showLess': '只看前 {n} 个',
|
|
241
|
+
'board.card.noEvent': '暂无事件',
|
|
242
|
+
'board.card.untitled': '(无标题)',
|
|
243
|
+
'board.card.observedDisclaimer': '状态为从持久化数据推断的观察值,可能滞后',
|
|
244
|
+
'board.card.observedValue': '观察值: {status}',
|
|
245
|
+
'board.card.lastReconcile': '最近对账: {time}',
|
|
246
|
+
'board.card.neverReconciled': '尚未对账',
|
|
247
|
+
'board.card.copyId': '点击复制完整会话 id',
|
|
248
|
+
'board.card.copied': '已复制',
|
|
249
|
+
'board.time.justNow': '刚刚',
|
|
250
|
+
'board.time.minutesAgo': '{n} 分钟前',
|
|
251
|
+
'board.time.hoursAgo': '{n} 小时前',
|
|
252
|
+
'board.timeWindow.hours': '{n} 小时',
|
|
253
|
+
'board.timeWindow.days': '{n} 天',
|
|
254
|
+
'board.groupCount': '{n} 个会话',
|
|
255
|
+
'board.unknownProject': '未知项目',
|
|
256
|
+
'board.widget.label': 'Sidecar',
|
|
257
|
+
'board.widget.connection.ok': '已连接',
|
|
258
|
+
'board.widget.connection.degraded': '连接不稳定',
|
|
259
|
+
'board.widget.connection.off': '离线',
|
|
260
|
+
'board.widget.working': '{n} 个会话工作中',
|
|
204
261
|
|
|
205
|
-
// ── session detail (T5.3
|
|
206
|
-
'detail.header.close':
|
|
207
|
-
'detail.header.listenOn':
|
|
208
|
-
'detail.header.listenOff':
|
|
209
|
-
'detail.header.listenHint':
|
|
210
|
-
'detail.header.
|
|
211
|
-
'detail.header.
|
|
212
|
-
'detail.header.
|
|
213
|
-
'detail.
|
|
214
|
-
'detail.
|
|
215
|
-
'detail.
|
|
216
|
-
'detail.
|
|
217
|
-
'detail.
|
|
218
|
-
'detail.
|
|
219
|
-
'detail.
|
|
220
|
-
'detail.
|
|
221
|
-
'detail.
|
|
222
|
-
'detail.
|
|
223
|
-
'detail.sources.
|
|
224
|
-
'detail.
|
|
225
|
-
'detail.
|
|
226
|
-
'detail.
|
|
227
|
-
'detail.
|
|
228
|
-
'detail.
|
|
229
|
-
'detail.kind.
|
|
230
|
-
'detail.kind.
|
|
231
|
-
'detail.kind.
|
|
232
|
-
'detail.kind.
|
|
233
|
-
'detail.
|
|
234
|
-
'detail.
|
|
235
|
-
'detail.
|
|
236
|
-
'detail.
|
|
237
|
-
'detail.
|
|
238
|
-
'detail.
|
|
239
|
-
'detail.
|
|
240
|
-
'detail.
|
|
241
|
-
'detail.
|
|
242
|
-
'detail.timeline.
|
|
243
|
-
'detail.
|
|
244
|
-
'detail.
|
|
245
|
-
'detail.
|
|
246
|
-
'detail.
|
|
247
|
-
'detail.
|
|
248
|
-
'detail.
|
|
249
|
-
'detail.
|
|
250
|
-
'detail.
|
|
251
|
-
'detail.
|
|
252
|
-
'detail.states.
|
|
253
|
-
'detail.
|
|
254
|
-
'detail.
|
|
255
|
-
'detail.
|
|
256
|
-
'detail.
|
|
262
|
+
// ── session detail (T5.3) ──────────────────────────────────────────────
|
|
263
|
+
'detail.header.close': '返回看板',
|
|
264
|
+
'detail.header.listenOn': '监听中',
|
|
265
|
+
'detail.header.listenOff': '监听',
|
|
266
|
+
'detail.header.listenHint': '开启后新事件将实时追加并高亮',
|
|
267
|
+
'detail.header.refresh': '刷新',
|
|
268
|
+
'detail.header.refreshing': '刷新中…',
|
|
269
|
+
'detail.header.refreshHint': '手动拉取最新时间线窗口',
|
|
270
|
+
'detail.header.copyIdTitle': '点击复制会话 ID',
|
|
271
|
+
'detail.header.copied': '已复制',
|
|
272
|
+
'detail.header.untitled': '(无标题)',
|
|
273
|
+
'detail.header.unknownProject': '未知项目',
|
|
274
|
+
'detail.header.observedDisclaimer': '状态为从持久化数据推断的观察值,可能滞后',
|
|
275
|
+
'detail.status.working': '工作中',
|
|
276
|
+
'detail.status.waiting': '等待中',
|
|
277
|
+
'detail.status.idle': '空闲',
|
|
278
|
+
'detail.status.dead': '已结束',
|
|
279
|
+
'detail.status.unknown': '未知',
|
|
280
|
+
'detail.sources.title': '数据来源',
|
|
281
|
+
'detail.sources.dshLive': 'dsh 实时',
|
|
282
|
+
'detail.sources.dshCold': 'dsh 冷读',
|
|
283
|
+
'detail.sources.sidecarReplay': 'sidecar 重放',
|
|
284
|
+
'detail.sources.sidecarBuffer': 'sidecar 缓冲',
|
|
285
|
+
'detail.sources.none': '来源未知',
|
|
286
|
+
'detail.kind.user': '用户消息',
|
|
287
|
+
'detail.kind.assistant': '助手回复',
|
|
288
|
+
'detail.kind.thinking': '思考',
|
|
289
|
+
'detail.kind.toolCall': '工具调用',
|
|
290
|
+
'detail.kind.toolResult': '工具结果',
|
|
291
|
+
'detail.kind.turn': '回合',
|
|
292
|
+
'detail.kind.step': '步骤',
|
|
293
|
+
'detail.kind.error': '错误',
|
|
294
|
+
'detail.kind.other': '事件',
|
|
295
|
+
'detail.gap.label': '缺口:可能有 {n} 条事件未捕获(256 队列上限或未持久化)',
|
|
296
|
+
'detail.filter.conversation': '只看对话',
|
|
297
|
+
'detail.filter.all': '全部事件',
|
|
298
|
+
'detail.filter.hiddenNotice': '已隐藏 {n} 条协议事件',
|
|
299
|
+
'detail.timeline.loadMore': '加载更多历史',
|
|
300
|
+
'detail.timeline.loadingMore': '加载中…',
|
|
301
|
+
'detail.timeline.noMore': '已到时间线起点',
|
|
302
|
+
'detail.timeline.expand': '展开',
|
|
303
|
+
'detail.timeline.collapse': '收起',
|
|
304
|
+
'detail.timeline.newBadge': '新',
|
|
305
|
+
'detail.timeline.seq': 'seq {n}',
|
|
306
|
+
'detail.timeline.hiddenNotice': '为保持流畅,较早的 {n} 条已折叠',
|
|
307
|
+
'detail.timeline.showAll': '全部显示',
|
|
308
|
+
'detail.timeline.chunkRun': '{n} 个流式分块',
|
|
309
|
+
'detail.states.loadingTitle': '正在加载时间线…',
|
|
310
|
+
'detail.states.emptyTitle': '暂无事件',
|
|
311
|
+
'detail.states.emptyHint': '该会话还没有可展示的规范化事件。',
|
|
312
|
+
'detail.states.errorTitle': '时间线加载失败',
|
|
313
|
+
'detail.states.errorFallback': '错误码:{reason}',
|
|
314
|
+
'detail.states.errors.session_not_found': '会话不存在或已不可见',
|
|
315
|
+
'detail.states.errors.invalid_cursor': '分页游标无效,请重新打开详情',
|
|
316
|
+
'detail.states.errors.fusion_not_wired': '当前 host 未启用时间线能力',
|
|
317
|
+
'detail.states.errors.network_error': '网络错误,无法联系 dsh host',
|
|
318
|
+
'detail.states.errors.request_timeout': '请求超时',
|
|
319
|
+
'detail.time.justNow': '刚刚',
|
|
320
|
+
'detail.time.minutesAgo': '{n} 分钟前',
|
|
321
|
+
'detail.time.hoursAgo': '{n} 小时前',
|
|
322
|
+
'detail.time.daysAgo': '{n} 天前',
|
|
257
323
|
|
|
258
324
|
// ── session detail: integration chrome (T5.10b literals) ───────────────
|
|
259
325
|
'detail.actions.inject': '注入',
|
|
260
326
|
'detail.actions.analyze': 'AI 分析',
|
|
261
327
|
'detail.actions.analyzeDisabledHint': '在设置中开启「启用 AI 旁路分析」后可用',
|
|
328
|
+
'detail.tools.title': '谱系与检索',
|
|
329
|
+
'detail.tools.show': '展开',
|
|
330
|
+
'detail.tools.hide': '收起',
|
|
262
331
|
|
|
263
|
-
// ── dsh deep-query tools (T5.4
|
|
264
|
-
'dshtools.lineage.title':
|
|
265
|
-
'dshtools.lineage.loading':
|
|
266
|
-
'dshtools.lineage.error':
|
|
267
|
-
'dshtools.lineage.empty':
|
|
268
|
-
'dshtools.lineage.currentBadge':
|
|
269
|
-
'dshtools.lineage.liveBadge':
|
|
270
|
-
'dshtools.lineage.notPersistedBadge':
|
|
271
|
-
'dshtools.lineage.role.ancestor':
|
|
272
|
-
'dshtools.lineage.role.target':
|
|
273
|
-
'dshtools.lineage.role.descendant':
|
|
274
|
-
'dshtools.lineage.jumpTitle':
|
|
275
|
-
'dshtools.lineage.currentTitle':
|
|
276
|
-
'dshtools.lineage.expand':
|
|
277
|
-
'dshtools.lineage.collapse':
|
|
278
|
-
'dshtools.lineage.nodeCount':
|
|
279
|
-
'dshtools.lineage.incompleteWithId':
|
|
280
|
-
'dshtools.lineage.incomplete':
|
|
281
|
-
'dshtools.lineage.degrade.notDshTitle':
|
|
282
|
-
'dshtools.lineage.degrade.notDshBody':
|
|
283
|
-
'dshtools.lineage.degrade.queryUnavailableTitle':
|
|
284
|
-
'dshtools.lineage.degrade.queryUnavailableBody':
|
|
285
|
-
|
|
286
|
-
'dshtools.lineage.degrade.
|
|
287
|
-
'dshtools.lineage.degrade.
|
|
288
|
-
'dshtools.lineage.degrade.
|
|
289
|
-
'dshtools.
|
|
290
|
-
'dshtools.search.
|
|
291
|
-
'dshtools.search.
|
|
292
|
-
'dshtools.search.
|
|
293
|
-
'dshtools.search.
|
|
294
|
-
'dshtools.search.
|
|
295
|
-
'dshtools.search.
|
|
296
|
-
'dshtools.search.
|
|
297
|
-
'dshtools.search.
|
|
298
|
-
'dshtools.search.matchedBy.
|
|
299
|
-
'dshtools.search.matchedBy.
|
|
300
|
-
'dshtools.search.matchedBy.
|
|
301
|
-
'dshtools.search.
|
|
332
|
+
// ── dsh deep-query tools (T5.4) ────────────────────────────────────────
|
|
333
|
+
'dshtools.lineage.title': '会话谱系',
|
|
334
|
+
'dshtools.lineage.loading': '谱系加载中…',
|
|
335
|
+
'dshtools.lineage.error': '谱系加载失败',
|
|
336
|
+
'dshtools.lineage.empty': '暂无谱系数据',
|
|
337
|
+
'dshtools.lineage.currentBadge': '当前会话',
|
|
338
|
+
'dshtools.lineage.liveBadge': '运行中',
|
|
339
|
+
'dshtools.lineage.notPersistedBadge': '未持久化',
|
|
340
|
+
'dshtools.lineage.role.ancestor': '祖先',
|
|
341
|
+
'dshtools.lineage.role.target': '目标',
|
|
342
|
+
'dshtools.lineage.role.descendant': '子会话',
|
|
343
|
+
'dshtools.lineage.jumpTitle': '跳转到该会话',
|
|
344
|
+
'dshtools.lineage.currentTitle': '正在查看该会话',
|
|
345
|
+
'dshtools.lineage.expand': '展开',
|
|
346
|
+
'dshtools.lineage.collapse': '折叠',
|
|
347
|
+
'dshtools.lineage.nodeCount': '{n} 个会话',
|
|
348
|
+
'dshtools.lineage.incompleteWithId': '谱系不完整:父会话 {id} 无法解析',
|
|
349
|
+
'dshtools.lineage.incomplete': '谱系不完整:部分父链无法解析',
|
|
350
|
+
'dshtools.lineage.degrade.notDshTitle': '谱系/溯源为 dsh 会话专属',
|
|
351
|
+
'dshtools.lineage.degrade.notDshBody': '当前会话来自外部 agent,dsh 的谱系与溯源能力不适用。',
|
|
352
|
+
'dshtools.lineage.degrade.queryUnavailableTitle': 'dsh 谱系服务不可用',
|
|
353
|
+
'dshtools.lineage.degrade.queryUnavailableBody':
|
|
354
|
+
'当前 dsh 组合未挂载 sessionQuery,谱系与溯源暂不可用。',
|
|
355
|
+
'dshtools.lineage.degrade.traceFailedTitle': '谱系追溯失败',
|
|
356
|
+
'dshtools.lineage.degrade.traceFailedBody': 'dsh 无法解析该会话的谱系。',
|
|
357
|
+
'dshtools.lineage.degrade.unknownTitle': '谱系不可用',
|
|
358
|
+
'dshtools.lineage.degrade.unknownBody': '后端报告谱系不可用(原因: {reason})。',
|
|
359
|
+
'dshtools.search.title': '会话检索',
|
|
360
|
+
'dshtools.search.placeholder': '检索会话(标题 / 项目 / 全文)',
|
|
361
|
+
'dshtools.search.submit': '检索',
|
|
362
|
+
'dshtools.search.loading': '检索中…',
|
|
363
|
+
'dshtools.search.error': '检索失败',
|
|
364
|
+
'dshtools.search.empty': '没有匹配的会话',
|
|
365
|
+
'dshtools.search.filterOnlyNotice': 'dsh 全文检索不可用,已降级为标题/项目过滤',
|
|
366
|
+
'dshtools.search.projectFilter': '项目过滤: {project}',
|
|
367
|
+
'dshtools.search.matchedBy.full-text': '全文',
|
|
368
|
+
'dshtools.search.matchedBy.title': '标题',
|
|
369
|
+
'dshtools.search.matchedBy.project': '项目',
|
|
370
|
+
'dshtools.search.matchedBy.other': '其他',
|
|
371
|
+
'dshtools.search.untitled': '(无标题)',
|
|
302
372
|
|
|
303
|
-
// ── project correlation view (T5.5
|
|
304
|
-
'project.title':
|
|
305
|
-
'project.summary':
|
|
306
|
-
'project.crossAgent':
|
|
307
|
-
'project.sessionCount':
|
|
308
|
-
'project.lastActive':
|
|
309
|
-
'project.liveChip':
|
|
310
|
-
'project.untitled':
|
|
311
|
-
'project.
|
|
312
|
-
'project.
|
|
313
|
-
'project.
|
|
314
|
-
'project.
|
|
373
|
+
// ── project correlation view (T5.5) ────────────────────────────────────
|
|
374
|
+
'project.title': '项目关联',
|
|
375
|
+
'project.summary': '{projects} 个项目 · {sessions} 个会话',
|
|
376
|
+
'project.crossAgent': '{n} 种 agent',
|
|
377
|
+
'project.sessionCount': '{n} 个会话',
|
|
378
|
+
'project.lastActive': '最近活跃 {time}',
|
|
379
|
+
'project.liveChip': '实时',
|
|
380
|
+
'project.untitled': '(无标题)',
|
|
381
|
+
'project.showAllSessions': '展开全部 {n} 个会话',
|
|
382
|
+
'project.showLessSessions': '只看前 {n} 个',
|
|
383
|
+
'project.empty.title': '暂无项目关联',
|
|
384
|
+
'project.empty.hint':
|
|
385
|
+
'时间窗内没有跨 agent 的项目活动;agent 在某个项目目录下开始工作后会出现在这里。',
|
|
386
|
+
'project.loading': '正在加载项目关联…',
|
|
387
|
+
'project.errorTitle': '项目关联加载失败',
|
|
315
388
|
|
|
316
389
|
// ── AI bypass analysis panel (T5.10b, design §4.e.3 / §5.1) ────────────
|
|
317
390
|
'analysis.title': 'AI 分析',
|
|
@@ -371,7 +444,7 @@ export const zh = {
|
|
|
371
444
|
'command.noSessions': '暂无被观测的会话',
|
|
372
445
|
'command.unknownProject': '未知项目',
|
|
373
446
|
'command.untitled': '(无标题)',
|
|
374
|
-
'command.truncated': '还有 {n}
|
|
447
|
+
'command.truncated': '还有 {n} 个会话未列出',
|
|
375
448
|
'command.boardHint': '打开会话视图的「Sidecar」Tab 查看完整看板',
|
|
376
449
|
'command.unreachable': 'sidecar 未连接',
|
|
377
450
|
'command.unreachableHint':
|
|
@@ -387,7 +460,9 @@ export const zh = {
|
|
|
387
460
|
'command.time.hoursAgo': '{n} 小时前',
|
|
388
461
|
'command.time.daysAgo': '{n} 天前',
|
|
389
462
|
|
|
390
|
-
// ──
|
|
463
|
+
// ── sidebar navigation (plain DOM + optional better-sidebar) ───────────
|
|
464
|
+
'sidebar.centerEntryLabel': 'Agent 中心',
|
|
465
|
+
'sidebar.centerEntryAria': '打开 Agent 中心',
|
|
391
466
|
'sidebar.tabTitle': 'Sidecar',
|
|
392
467
|
'sidebar.countsRow': '{working} 工作中 · {waiting} 等待中',
|
|
393
468
|
'sidebar.recentTitle': '最近活跃',
|
package/src/client/mount.tsx
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* React bindings for the board tab, footer widget, and settings card.
|
|
3
|
+
* Factories close over the controller so subscribe/getSnapshot identities
|
|
4
|
+
* stay stable across renders. The board depends only on {@link BoardUiPort}
|
|
5
|
+
* and passes its nested detail port to the detail container.
|
|
6
|
+
* Each exported root factory's top-level component subscribes once to the
|
|
7
|
+
* active locale, refreshing its complete descendant tree without leaf subscriptions.
|
|
7
8
|
*
|
|
8
|
-
* The settings card
|
|
9
|
-
*
|
|
9
|
+
* The settings card owns the staged-edit lifecycle over a bound
|
|
10
|
+
* `SettingsScope` (browser mirror of the host settings namespace):
|
|
10
11
|
* resolved values come from the scope snapshot, edits stage locally, save
|
|
11
12
|
* writes one complete top-level group per changed group (see
|
|
12
13
|
* settings-glue.ts for the write-granularity rationale), and success is
|
|
@@ -25,13 +26,15 @@ import { SettingsCard, type SettingsCardValues, type SidecarDaemonStatus } from
|
|
|
25
26
|
import { countWorking, deriveWidgetConnection } from './board/logic.ts'
|
|
26
27
|
import type { BoardFilterState } from './board/logic.ts'
|
|
27
28
|
import type { SidecarController, SidecarViewState } from './controller.ts'
|
|
28
|
-
import
|
|
29
|
-
import type { InjectActions } from './inject-glue.ts'
|
|
30
|
-
import { SidecarDetailView, type SidecarUiIntegration } from './detail-view.tsx'
|
|
29
|
+
import { SidecarDetailView } from './detail-view.tsx'
|
|
31
30
|
import { findCardHint, type DetailHeaderHint } from './detail-glue.ts'
|
|
32
|
-
import { findProjectSessionHint
|
|
31
|
+
import { findProjectSessionHint } from './project-glue.ts'
|
|
32
|
+
import type { BoardUiPort, ProjectsStorePort } from './ui-integration.ts'
|
|
33
33
|
import { detailErrorText } from './detail/logic.ts'
|
|
34
34
|
import { t } from './locales/index.ts'
|
|
35
|
+
import { useActiveLocale } from './locales/react.ts'
|
|
36
|
+
import { CenterOverlay } from './navigation/CenterOverlay.tsx'
|
|
37
|
+
import type { CenterNavigation, CenterNavigationStore } from './navigation/center.ts'
|
|
35
38
|
import css from './detail-view.module.css'
|
|
36
39
|
import {
|
|
37
40
|
DEFAULT_CONFIG_VIEW,
|
|
@@ -41,21 +44,6 @@ import {
|
|
|
41
44
|
type SidecarConfigView,
|
|
42
45
|
} from './settings-glue.ts'
|
|
43
46
|
|
|
44
|
-
/**
|
|
45
|
-
* What the board tab needs to host the inject panel (S5 wiring, T4.9).
|
|
46
|
-
* Since T5.10b the panel opens from the detail view's 注入 button; this
|
|
47
|
-
* shape rides {@link SidecarUiIntegration.inject} unchanged.
|
|
48
|
-
*/
|
|
49
|
-
export interface BoardInjectIntegration {
|
|
50
|
-
/** onPrepare/onExecute over the action transport (inject-glue.ts). */
|
|
51
|
-
actions: InjectActions
|
|
52
|
-
/**
|
|
53
|
-
* Late-bound `inject.default-mode` reader: the settings scope resolves
|
|
54
|
-
* after the tab mounts, so the value is read at panel-open time.
|
|
55
|
-
*/
|
|
56
|
-
getDefaultMode: () => InjectMode
|
|
57
|
-
}
|
|
58
|
-
|
|
59
47
|
/** Board-tab main views (detail is an overlay route on top of either). */
|
|
60
48
|
type MainView = 'board' | 'projects'
|
|
61
49
|
|
|
@@ -65,7 +53,7 @@ type MainView = 'board' | 'projects'
|
|
|
65
53
|
*/
|
|
66
54
|
function ProjectsContainer(props: {
|
|
67
55
|
controller: SidecarController
|
|
68
|
-
store:
|
|
56
|
+
store: ProjectsStorePort
|
|
69
57
|
onSelectSession: (sessionId: string) => void
|
|
70
58
|
}): ReactElement {
|
|
71
59
|
const { controller, store } = props
|
|
@@ -85,28 +73,27 @@ function ProjectsContainer(props: {
|
|
|
85
73
|
}
|
|
86
74
|
|
|
87
75
|
/**
|
|
88
|
-
* Cross-agent board
|
|
89
|
-
* shell of the whole M3 information architecture (design §5.1):
|
|
76
|
+
* Cross-agent board content and its project/detail routes:
|
|
90
77
|
*
|
|
91
78
|
* - view 1: the session board, with a 「会话看板 / 项目视图」 switcher
|
|
92
79
|
* (ProjectView over `GET projects`);
|
|
93
80
|
* - view 2: clicking a session card in EITHER view routes to the full-tab
|
|
94
81
|
* session-detail view (timeline + 注入 + AI 分析 + dsh 谱系/检索);
|
|
95
82
|
* detail-internal jumps (lineage nodes, search hits) re-route in place;
|
|
96
|
-
* - view 3: the
|
|
83
|
+
* - view 3: the inject panel opens as a modal from the detail view.
|
|
97
84
|
*
|
|
98
85
|
* Without an integration the board renders read-only and inert (no detail
|
|
99
|
-
* routing)
|
|
86
|
+
* routing).
|
|
100
87
|
*/
|
|
101
|
-
|
|
88
|
+
function createBoardContent(
|
|
102
89
|
controller: SidecarController,
|
|
103
|
-
integration?:
|
|
90
|
+
integration?: BoardUiPort,
|
|
104
91
|
): () => ReactElement {
|
|
105
92
|
const subscribe = (cb: () => void): (() => void) => controller.subscribe(cb)
|
|
106
93
|
const getState = (): SidecarViewState => controller.getState()
|
|
107
94
|
const getFilters = (): BoardFilterState => controller.getFilters()
|
|
108
95
|
|
|
109
|
-
return function
|
|
96
|
+
return function BoardContent(): ReactElement {
|
|
110
97
|
// Third argument (server snapshot) keeps the components renderable under
|
|
111
98
|
// react-dom/server (DOM-level verification harness); same source.
|
|
112
99
|
const state = useSyncExternalStore(subscribe, getState, getState)
|
|
@@ -115,9 +102,9 @@ export function createBoardTab(
|
|
|
115
102
|
const [detail, setDetail] = useState<{ id: string; hint: DetailHeaderHint | null } | null>(
|
|
116
103
|
null,
|
|
117
104
|
)
|
|
118
|
-
// One
|
|
105
|
+
// One project store per tab mount, created lazily with the integration
|
|
119
106
|
// seam; state survives board↔projects↔detail switches within the tab.
|
|
120
|
-
const [projectsStore] = useState<
|
|
107
|
+
const [projectsStore] = useState<ProjectsStorePort | null>(
|
|
121
108
|
() => integration?.createProjectsStore() ?? null,
|
|
122
109
|
)
|
|
123
110
|
useEffect(() => () => { projectsStore?.dispose() }, [projectsStore])
|
|
@@ -140,7 +127,7 @@ export function createBoardTab(
|
|
|
140
127
|
sessionId={detail.id}
|
|
141
128
|
hint={detail.hint}
|
|
142
129
|
controller={controller}
|
|
143
|
-
integration={integration}
|
|
130
|
+
integration={integration.detail}
|
|
144
131
|
onClose={() => { setDetail(null) }}
|
|
145
132
|
onSelectSession={openDetail}
|
|
146
133
|
/>
|
|
@@ -154,6 +141,7 @@ export function createBoardTab(
|
|
|
154
141
|
type="button"
|
|
155
142
|
className={css['switcherButton']}
|
|
156
143
|
data-active={mainView === 'board' || undefined}
|
|
144
|
+
aria-pressed={mainView === 'board'}
|
|
157
145
|
onClick={() => { setMainView('board') }}
|
|
158
146
|
>
|
|
159
147
|
{t('board.viewBoard')}
|
|
@@ -162,6 +150,7 @@ export function createBoardTab(
|
|
|
162
150
|
type="button"
|
|
163
151
|
className={css['switcherButton']}
|
|
164
152
|
data-active={mainView === 'projects' || undefined}
|
|
153
|
+
aria-pressed={mainView === 'projects'}
|
|
165
154
|
onClick={() => { setMainView('projects') }}
|
|
166
155
|
>
|
|
167
156
|
{t('board.viewProjects')}
|
|
@@ -186,9 +175,9 @@ export function createBoardTab(
|
|
|
186
175
|
onFiltersChange={(next) => {
|
|
187
176
|
controller.setFilters(next)
|
|
188
177
|
}}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
178
|
+
// Hand the promise through so the board can render the
|
|
179
|
+
// in-flight state and a failure notice (UX-07).
|
|
180
|
+
onRefresh={() => controller.refresh()}
|
|
192
181
|
onSelectSession={openDetail}
|
|
193
182
|
/>
|
|
194
183
|
)}
|
|
@@ -197,16 +186,60 @@ export function createBoardTab(
|
|
|
197
186
|
}
|
|
198
187
|
}
|
|
199
188
|
|
|
189
|
+
/** Bind board content to its independent React root and locale subscription. */
|
|
190
|
+
export function createBoardTab(
|
|
191
|
+
controller: SidecarController,
|
|
192
|
+
integration?: BoardUiPort,
|
|
193
|
+
): () => ReactElement {
|
|
194
|
+
const BoardContent = createBoardContent(controller, integration)
|
|
195
|
+
return function SidecarBoardTab(): ReactElement {
|
|
196
|
+
useActiveLocale()
|
|
197
|
+
return <BoardContent />
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** Bind the shared navigation source to the shell overlay and existing board. */
|
|
202
|
+
export function createCenterOverlay(
|
|
203
|
+
controller: SidecarController,
|
|
204
|
+
integration: BoardUiPort,
|
|
205
|
+
navigation: CenterNavigationStore,
|
|
206
|
+
): () => ReactElement {
|
|
207
|
+
const BoardContent = createBoardContent(controller, integration)
|
|
208
|
+
return function SidecarCenterOverlay(): ReactElement {
|
|
209
|
+
useActiveLocale()
|
|
210
|
+
const open = useSyncExternalStore(
|
|
211
|
+
navigation.subscribe,
|
|
212
|
+
navigation.getSnapshot,
|
|
213
|
+
navigation.getSnapshot,
|
|
214
|
+
)
|
|
215
|
+
return (
|
|
216
|
+
<CenterOverlay
|
|
217
|
+
open={open}
|
|
218
|
+
onClose={navigation.close}
|
|
219
|
+
title={t('board.topbar.title')}
|
|
220
|
+
closeLabel={t('inject.close')}
|
|
221
|
+
>
|
|
222
|
+
{open ? <BoardContent /> : null}
|
|
223
|
+
</CenterOverlay>
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
200
228
|
/** Footer connection dot + working counter bound to the controller. */
|
|
201
|
-
export function createFooterWidget(
|
|
229
|
+
export function createFooterWidget(
|
|
230
|
+
controller: SidecarController,
|
|
231
|
+
onOpen: CenterNavigation,
|
|
232
|
+
): () => ReactElement {
|
|
202
233
|
const subscribe = (cb: () => void): (() => void) => controller.subscribe(cb)
|
|
203
234
|
const getState = (): SidecarViewState => controller.getState()
|
|
204
235
|
return function SidecarFooterWidget(): ReactElement {
|
|
236
|
+
useActiveLocale()
|
|
205
237
|
const state = useSyncExternalStore(subscribe, getState, getState)
|
|
206
238
|
return (
|
|
207
239
|
<SidecarWidget
|
|
208
240
|
connection={deriveWidgetConnection(state.daemonState, state.streamHealth)}
|
|
209
241
|
workingCount={countWorking(state.sessions)}
|
|
242
|
+
onOpen={onOpen}
|
|
210
243
|
/>
|
|
211
244
|
)
|
|
212
245
|
}
|
|
@@ -230,6 +263,7 @@ export function createSettingsCardEntry(
|
|
|
230
263
|
const getSnapshot = (): ReturnType<typeof scope.getSnapshot> => scope.getSnapshot()
|
|
231
264
|
|
|
232
265
|
return function SidecarSettingsCardEntry(): ReactElement {
|
|
266
|
+
useActiveLocale()
|
|
233
267
|
const snapshot = useSyncExternalStore(subscribeScope, getSnapshot, getSnapshot)
|
|
234
268
|
const state = useSyncExternalStore(subscribeState, getState, getState)
|
|
235
269
|
const [staged, setStaged] = useState<Partial<SettingsCardValues>>({})
|