dsh-super-code 0.0.9 → 0.1.1

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/client/index.js CHANGED
@@ -12,20 +12,74 @@ const { useEffect, useLayoutEffect, useRef, useMemo, useState, useSyncExternalSt
12
12
  const { IconRefreshOutline16 } = require('@deepseek-ai/dsh-client-ui-primitives')
13
13
  const { SessionEventStream } = require('@deepseek-ai/dsh-api-session-controller')
14
14
 
15
- function TaskMemoryPanel({ state }) {
15
+ const LOCALE_NS = 'dshSuperCode'
16
+ const zh = {
17
+ 'preset.description': '统一编码预设,按需协作。', 'preset.refresh': '刷新状态',
18
+ 'preset.state.available': '预设可用', 'preset.state.installed': '用户预设已安装', 'preset.state.conflict': '已有同名预设',
19
+ 'preset.state.missing': '尚未安装', 'preset.state.broken': '预设不可用',
20
+ 'preset.availableHint': '可在新对话中选择此预设。用户副本不会自动覆盖或更新。',
21
+ 'preset.installHint': '安装一份用户预设;已有内容会保留,重名时请换个名称。',
22
+ 'preset.userConflict': '用户目录中已有同名预设,已保留。可换名安装独立副本。',
23
+ 'preset.readonly': '此宿主未提供用户预设目录,无法安装副本。',
24
+ 'preset.name': '预设名称', 'preset.nameHint': '使用小写字母、数字和连字符,最多 64 个字符。安装后在新对话中选择。',
25
+ 'preset.install': '安装用户预设', 'preset.working': '处理中…',
26
+ 'preset.error.invalid-name': '名称格式不正确,请使用小写字母、数字和连字符。',
27
+ 'preset.error.name-taken': '该名称已被占用,原预设未改动。请换个名称。',
28
+ 'preset.error.no-user-root': '此宿主没有可写的用户预设目录。',
29
+ 'preset.error.install-failed': '安装未完成,请检查预设目录的写入权限后重试。',
30
+ 'preset.error.connection': '暂时无法读取预设状态,请刷新重试。',
31
+ 'task.memory': '任务档案', 'task.other': '其他任务', 'task.history': '任务记录',
32
+ 'status.active': '进行中', 'status.paused': '已暂停', 'status.completed': '已完成', 'status.cancelled': '已取消',
33
+ 'next': '下一步:', 'tree.title': '协作执行', 'tree.count': '显示 {shown} / {total} · 运行中 {running}',
34
+ 'tree.refresh': '刷新执行记录', 'tree.legend.active': '运行中', 'tree.legend.idle': '待命', 'tree.legend.stopped': '已停止', 'tree.legend.unavailable': '记录不可用',
35
+ 'tree.empty': '暂无会话', 'tree.zoom.out': '缩小节点图', 'tree.zoom.in': '放大节点图', 'tree.fit': '适应窗口',
36
+ 'tree.organize': '自动整理', 'tree.collapsed': '已收起 {count}', 'tree.more': '显示更多', 'tree.canvas': '拖动平移,滚轮缩放,双击空白适应窗口',
37
+ 'tree.aria': 'Agent 执行树', 'tree.status': '状态颜色', 'tree.usage': '本节点及所有已知下级合计,包含本节点。{partial}Token {tokens};缓存读取 {cache};{agents} 个 Agent',
38
+ 'usage.unknown': '用量 —', 'usage.cache': '缓存 {value}%', 'usage.partial': '部分', 'usage.incomplete': '部分记录或用量尚不可用;仅显示已知值。',
39
+ 'history.aria': '已收起的 Agent', 'history.search': '搜索 Agent 或上级任务', 'history.find': '查找收起的 Agent', 'history.created': '创建时间', 'history.sort': '排序',
40
+ 'history.all': '全部时间', 'history.today': '今天', 'history.week': '近 7 天', 'history.month': '近 30 天', 'history.custom': '自定义', 'history.latest': '最新创建', 'history.earliest': '最早创建', 'history.tokensHigh': 'Token 从多到少', 'history.tokensLow': 'Token 从少到多', 'history.titleAsc': '标题正序', 'history.titleDesc': '标题倒序',
41
+ 'history.start': '开始日期', 'history.end': '结束日期', 'history.summary': '{shown} / {total} 个已收起节点 · Token 含下级', 'history.invalid': '请选择有效的起止日期', 'history.empty': '没有匹配的节点', 'history.view': '查看 {title} 的详情', 'history.unknownTime': '时间未知', 'history.unknownUsage': '用量未知',
42
+ 'detail.path': 'Agent 路径', 'detail.title': 'Agent 详情', 'detail.refresh': '刷新对话详情', 'detail.task': '任务', 'detail.agent': 'Agent', 'detail.interrupted': 'Agent · 已中断', 'detail.error': '执行错误', 'detail.incomplete': '执行未完成,请稍后重试。', 'detail.input': '输入', 'detail.output': '输出', 'detail.cacheRead': '缓存读取', 'detail.cacheWrite': '缓存写入', 'detail.total': 'Token 总量', 'detail.hitRate': '缓存命中率', 'detail.moreUsage': '用量详情', 'detail.unavailable': '未提供', 'detail.reading': '正在读取对话…', 'detail.read': '读取中…', 'detail.older': '加载更早记录', 'detail.latest': '回到最新', 'detail.empty': '暂无执行内容', 'detail.new': '有新内容 · 查看最新', 'detail.loadError': '对话详情读取失败', 'detail.connection': '连接中断,正在重连', 'detail.historyError': '历史记录读取失败', 'detail.open': '展开全文', 'detail.noAccess': '无法打开详情,请重试', 'tree.loadError': '部分执行记录读取失败,请刷新重试', 'detail.unknown': '用量未知', 'main.agent': '主 Agent', 'tree.open': '点击查看详情', 'tree.expand': '展开', 'tree.collapse': '收起', 'image': '[图片]'
43
+ }
44
+ const en = {
45
+ 'preset.description': 'One coding preset with on-demand collaboration.', 'preset.refresh': 'Refresh status',
46
+ 'preset.state.available': 'Preset available', 'preset.state.installed': 'User preset installed', 'preset.state.conflict': 'Preset name already exists',
47
+ 'preset.state.missing': 'Not installed', 'preset.state.broken': 'Preset unavailable',
48
+ 'preset.availableHint': 'Select this preset in a new conversation. User copies are never overwritten or updated automatically.',
49
+ 'preset.installHint': 'Install a user copy. Existing content is preserved; choose another name if it is taken.',
50
+ 'preset.userConflict': 'A user preset with this name already exists and was preserved. Choose another name for a separate copy.',
51
+ 'preset.readonly': 'This host does not provide a user preset directory.',
52
+ 'preset.name': 'Preset name', 'preset.nameHint': 'Lowercase letters, digits and hyphens, up to 64 characters. Select it in a new conversation after installing.',
53
+ 'preset.install': 'Install user preset', 'preset.working': 'Working…',
54
+ 'preset.error.invalid-name': 'Use lowercase letters, digits and hyphens for the name.',
55
+ 'preset.error.name-taken': 'This name is taken. The existing preset was preserved. Choose another name.',
56
+ 'preset.error.no-user-root': 'This host has no writable user preset directory.',
57
+ 'preset.error.install-failed': 'Installation failed. Check directory write permissions and retry.',
58
+ 'preset.error.connection': 'Preset status is unavailable. Refresh to try again.',
59
+ 'task.memory': 'Task memory', 'task.other': 'Other tasks', 'task.history': 'Task history',
60
+ 'status.active': 'Active', 'status.paused': 'Paused', 'status.completed': 'Completed', 'status.cancelled': 'Cancelled', 'next': 'Next: ',
61
+ 'tree.title': 'Collaborative execution', 'tree.count': 'Showing {shown} / {total} · Running {running}', 'tree.refresh': 'Refresh execution records', 'tree.legend.active': 'Running', 'tree.legend.idle': 'Idle', 'tree.legend.stopped': 'Stopped', 'tree.legend.unavailable': 'Unavailable', 'tree.empty': 'No sessions', 'tree.zoom.out': 'Zoom out', 'tree.zoom.in': 'Zoom in', 'tree.fit': 'Fit to window', 'tree.organize': 'Organize automatically', 'tree.collapsed': '{count} collapsed', 'tree.more': 'Show more', 'tree.canvas': 'Drag to pan, scroll to zoom, double-click empty space to fit', 'tree.aria': 'Agent execution tree', 'tree.status': 'Status colors', 'tree.usage': 'This node and all known descendants. {partial}Tokens {tokens}; cache reads {cache}; {agents} agents', 'usage.unknown': 'Usage —', 'usage.cache': 'Cache {value}%', 'usage.partial': 'partial', 'usage.incomplete': 'Some records or usage are unavailable; showing known values only.',
62
+ 'history.aria': 'Collapsed agents', 'history.search': 'Search agents or parent tasks', 'history.find': 'Find collapsed agents', 'history.created': 'Created', 'history.sort': 'Sort', 'history.all': 'All time', 'history.today': 'Today', 'history.week': 'Last 7 days', 'history.month': 'Last 30 days', 'history.custom': 'Custom', 'history.latest': 'Newest first', 'history.earliest': 'Oldest first', 'history.tokensHigh': 'Most tokens first', 'history.tokensLow': 'Fewest tokens first', 'history.titleAsc': 'Title A–Z', 'history.titleDesc': 'Title Z–A', 'history.start': 'Start date', 'history.end': 'End date', 'history.summary': '{shown} / {total} collapsed nodes · Includes descendants', 'history.invalid': 'Choose a valid date range', 'history.empty': 'No matching nodes', 'history.view': 'View details for {title}', 'history.unknownTime': 'Unknown time', 'history.unknownUsage': 'Usage unknown',
63
+ 'detail.path': 'Agent path', 'detail.title': 'Agent details', 'detail.refresh': 'Refresh conversation details', 'detail.task': 'Task', 'detail.agent': 'Agent', 'detail.interrupted': 'Agent · Interrupted', 'detail.error': 'Execution error', 'detail.incomplete': 'Execution did not finish. Try again later.', 'detail.input': 'Input', 'detail.output': 'Output', 'detail.cacheRead': 'Cache reads', 'detail.cacheWrite': 'Cache writes', 'detail.total': 'Total tokens', 'detail.hitRate': 'Cache hit rate', 'detail.moreUsage': 'Usage details', 'detail.unavailable': 'Unavailable', 'detail.reading': 'Reading conversation…', 'detail.read': 'Reading…', 'detail.older': 'Load earlier records', 'detail.latest': 'Back to latest', 'detail.empty': 'No execution content', 'detail.new': 'New content · View latest', 'detail.loadError': 'Could not read conversation details', 'detail.connection': 'Connection interrupted, reconnecting', 'detail.historyError': 'Could not read history', 'detail.open': 'Expand', 'detail.noAccess': 'Could not open details. Try again.', 'tree.loadError': 'Some execution records failed to load. Refresh to retry.', 'detail.unknown': 'Usage unknown', 'main.agent': 'Main agent', 'tree.open': 'Click to view details', 'tree.expand': 'Expand', 'tree.collapse': 'Collapse', 'image': '[Image]'
64
+ }
65
+ function formatText(template, values = {}) { return template.replace(/\{(\w+)\}/g, (_, key) => values[key] ?? '') }
66
+ function text(t, key, values) { return formatText(t(key), values) }
67
+ function localeCode(t) { return text(t, 'status.active') === en['status.active'] ? 'en-US' : 'zh-CN' }
68
+
69
+ function TaskMemoryPanel({ state, t = key => zh[key] || key }) {
16
70
  const tasks = state?.tasks || []
17
71
  if (!tasks.length) return null
18
72
  const current = state.current?.kind === 'task' ? state.current : undefined
19
- const labels = { active: '进行中', paused: '已暂停', completed: '已完成', cancelled: '已取消' }
20
- return React.createElement('section', { className: 'dsh-super-code-task-memory', 'aria-label': '任务档案' },
73
+ const labels = { active: text(t, 'status.active'), paused: text(t, 'status.paused'), completed: text(t, 'status.completed'), cancelled: text(t, 'status.cancelled') }
74
+ return React.createElement('section', { className: 'dsh-super-code-task-memory', 'aria-label': text(t, 'task.memory') },
21
75
  current && React.createElement(React.Fragment, null,
22
76
  React.createElement('div', { className: 'dsh-super-code-task-heading' },
23
77
  React.createElement('strong', null, current.title),
24
78
  React.createElement('span', null, labels[current.status] || current.status)),
25
79
  current.goal && React.createElement('p', null, current.goal),
26
- current.next && React.createElement('p', null, '下一步:', current.next)),
80
+ current.next && React.createElement('p', null, text(t, 'next'), current.next)),
27
81
  tasks.some(task => task.id !== current?.id) && React.createElement('details', null,
28
- React.createElement('summary', null, current ? '其他任务' : '任务记录'),
82
+ React.createElement('summary', null, current ? text(t, 'task.other') : text(t, 'task.history')),
29
83
  React.createElement('ul', null, tasks.filter(task => task.id !== current?.id).map(task => React.createElement('li', { key: task.id }, `${task.title} · ${labels[task.status] || task.status}`)))),
30
84
  )
31
85
  }
@@ -151,7 +205,6 @@ function agentTreeTotals(view, catalogs = {}) {
151
205
 
152
206
  const historyRowHeight = 56
153
207
  const historyCollator = new Intl.Collator('zh-CN', { numeric: true, sensitivity: 'base' })
154
- const historyDateFormat = new Intl.DateTimeFormat('zh-CN', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' })
155
208
 
156
209
  function agentHistoryRange(range, from = '', to = '', now = Date.now()) {
157
210
  const today = new Date(now)
@@ -181,7 +234,7 @@ function agentHistoryRows(view, hidden, totals, options = {}) {
181
234
  const rows = []
182
235
  for (const id of hidden) {
183
236
  const node = view.nodes.get(id), title = node.title || node.displayTitle || id
184
- const parent = view.nodes.get(node.parentId)?.title || '主 Agent', usage = totals.get(id)
237
+ const parent = view.nodes.get(node.parentId)?.title || text(options.t || (key => zh[key] || key), 'main.agent'), usage = totals.get(id)
185
238
  const time = Number.isFinite(node.createdAt) && node.createdAt >= 0 && node.createdAt <= 8640000000000000 ? node.createdAt : -1
186
239
  if (query && !`${title} ${parent}`.toLocaleLowerCase().includes(query)) continue
187
240
  if (options.range && options.range !== 'all' && (time < 0 || time < range.start || time >= range.end)) continue
@@ -208,11 +261,12 @@ function agentHistoryWindow(count, scrollTop, height) {
208
261
  }
209
262
 
210
263
  /** Local scroll state keeps the graph out of the scrolling render path. */
211
- function AgentHistory({ view, hidden, totals, open }) {
264
+ function AgentHistory({ view, hidden, totals, open, t = key => zh[key] || key }) {
212
265
  const [filters, setFilters] = useState({ query: '', range: 'all', from: '', to: '', sort: 'time-desc' })
213
266
  const [scroll, setScroll] = useState({ top: 0, height: 168 })
214
267
  const viewport = useRef(null), focusTarget = useRef('')
215
- const records = useMemo(() => agentHistoryRows(view, hidden, totals, filters), [view, hidden, totals, filters])
268
+ const records = useMemo(() => agentHistoryRows(view, hidden, totals, { ...filters, t }), [view, hidden, totals, filters, t])
269
+ const dateFormat = useMemo(() => new Intl.DateTimeFormat(localeCode(t), { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }), [t])
216
270
  const window = agentHistoryWindow(records.length, scroll.top, scroll.height)
217
271
  const range = agentHistoryRange(filters.range, filters.from, filters.to)
218
272
  const change = (key, value) => setFilters(previous => ({ ...previous, [key]: value }))
@@ -249,33 +303,33 @@ function AgentHistory({ view, hidden, totals, open }) {
249
303
  const select = (key, label, choices) => React.createElement('label', null, label,
250
304
  React.createElement('select', { value: filters[key], 'aria-label': label, onChange: event => change(key, event.target.value) },
251
305
  choices.map(([value, text]) => React.createElement('option', { key: value, value }, text))))
252
- return React.createElement('section', { className: 'dsh-super-code-history', 'aria-label': '已收起的 Agent' },
253
- React.createElement('input', { type: 'search', value: filters.query, 'aria-label': '查找收起的 Agent', placeholder: '搜索 Agent 或上级任务', onChange: event => change('query', event.target.value) }),
306
+ return React.createElement('section', { className: 'dsh-super-code-history', 'aria-label': text(t, 'history.aria') },
307
+ React.createElement('input', { type: 'search', value: filters.query, 'aria-label': text(t, 'history.find'), placeholder: text(t, 'history.search'), onChange: event => change('query', event.target.value) }),
254
308
  React.createElement('div', { className: 'dsh-super-code-history-filters' },
255
- select('range', '创建时间', [['all', '全部时间'], ['today', '今天'], ['week', '近 7 天'], ['month', '近 30 天'], ['custom', '自定义']]),
256
- select('sort', '排序', [['time-desc', '最新创建'], ['time-asc', '最早创建'], ['tokens-desc', 'Token 从多到少'], ['tokens-asc', 'Token 从少到多'], ['title-asc', '标题正序'], ['title-desc', '标题倒序']])),
309
+ select('range', text(t, 'history.created'), [['all', text(t, 'history.all')], ['today', text(t, 'history.today')], ['week', text(t, 'history.week')], ['month', text(t, 'history.month')], ['custom', text(t, 'history.custom')]]),
310
+ select('sort', text(t, 'history.sort'), [['time-desc', text(t, 'history.latest')], ['time-asc', text(t, 'history.earliest')], ['tokens-desc', text(t, 'history.tokensHigh')], ['tokens-asc', text(t, 'history.tokensLow')], ['title-asc', text(t, 'history.titleAsc')], ['title-desc', text(t, 'history.titleDesc')]])),
257
311
  filters.range === 'custom' && React.createElement('div', { className: 'dsh-super-code-history-filters' },
258
- ['from', 'to'].map((key, index) => React.createElement('label', { key }, index ? '结束日期' : '开始日期',
259
- React.createElement('input', { type: 'date', value: filters[key], 'aria-label': index ? '结束日期' : '开始日期', onChange: event => change(key, event.target.value) })))),
260
- React.createElement('p', { className: 'dsh-super-code-history-summary', role: 'status' }, range.valid ? `${records.length} / ${hidden.length} 个已收起节点 · Token 含下级` : '请选择有效的起止日期'),
261
- React.createElement('div', { ref: viewport, className: 'dsh-super-code-history-list', role: 'list', 'aria-label': '收起节点列表',
312
+ ['from', 'to'].map((key, index) => React.createElement('label', { key }, index ? text(t, 'history.end') : text(t, 'history.start'),
313
+ React.createElement('input', { type: 'date', value: filters[key], 'aria-label': index ? text(t, 'history.end') : text(t, 'history.start'), onChange: event => change(key, event.target.value) })))),
314
+ React.createElement('p', { className: 'dsh-super-code-history-summary', role: 'status' }, range.valid ? text(t, 'history.summary', { shown: records.length, total: hidden.length }) : text(t, 'history.invalid')),
315
+ React.createElement('div', { ref: viewport, className: 'dsh-super-code-history-list', role: 'list', 'aria-label': text(t, 'history.aria'),
262
316
  onScroll: event => { const top = event.currentTarget.scrollTop; setScroll(previous => previous.top === top ? previous : { ...previous, top }) } },
263
317
  React.createElement('div', { style: { height: window.height, position: 'relative' } }, records.slice(window.start, window.end).map((record, offset) => {
264
- const index = window.start + offset, status = agentExecutionStatus(record.node, false)
265
- const date = record.time < 0 ? '时间未知' : historyDateFormat.format(record.time)
318
+ const index = window.start + offset, status = agentExecutionStatus(record.node, false, t)
319
+ const date = record.time < 0 ? text(t, 'history.unknownTime') : dateFormat.format(record.time)
266
320
  return React.createElement('div', { key: record.id, role: 'listitem', 'aria-posinset': index + 1, 'aria-setsize': records.length,
267
321
  style: { position: 'absolute', top: index * historyRowHeight, height: historyRowHeight, width: '100%' } },
268
322
  React.createElement('button', { type: 'button', 'data-history-id': record.id, onClick: () => open(record.node), onKeyDown: event => keyDown(event, index),
269
- title: `${record.title} · ${record.parent} · ${record.time < 0 ? date : new Date(record.time).toLocaleString('zh-CN')} · ${record.tokens < 0 ? '用量未知' : record.tokens + ' Token' + (record.partial ? '(部分)' : '')}`,
270
- 'aria-label': `查看 ${record.title} 的详情` },
323
+ title: `${record.title} · ${record.parent} · ${record.time < 0 ? date : new Date(record.time).toLocaleString()} · ${record.tokens < 0 ? text(t, 'history.unknownUsage') : record.tokens + ' Token' + (record.partial ? ' (' + text(t, 'usage.partial') + ')' : '')}`,
324
+ 'aria-label': text(t, 'history.view', { title: record.title }) },
271
325
  React.createElement('i', { className: 'dsh-super-code-circle ' + status.tone, 'aria-hidden': true }),
272
326
  React.createElement('span', null, React.createElement('strong', null, record.title), React.createElement('small', null, `${status.label} · ${date} · ${record.parent}`)),
273
- React.createElement('small', null, record.tokens < 0 ? '—' : `${record.partial ? '≈' : ''}${record.tokens.toLocaleString('zh-CN', { notation: 'compact', maximumFractionDigits: 1 })} tok`)))
327
+ React.createElement('small', null, record.tokens < 0 ? '—' : `${record.partial ? '≈' : ''}${record.tokens.toLocaleString(localeCode(t), { notation: 'compact', maximumFractionDigits: 1 })} tok`)))
274
328
  })),
275
- !records.length && React.createElement('p', { className: 'dsh-super-code-history-empty' }, '没有匹配的节点')))
329
+ !records.length && React.createElement('p', { className: 'dsh-super-code-history-empty' }, text(t, 'history.empty'))))
276
330
  }
277
331
 
278
- function AgentTree({ useSessions, useTabInfo, treeStates, openDetail, refresh, watchCatalog }) {
332
+ function AgentTree({ useSessions, useTabInfo, treeStates, openDetail, refresh, watchCatalog, t = key => zh[key] || key }) {
279
333
  const state = useSessions(s => s)
280
334
  const { tab } = useTabInfo()
281
335
  const view = useMemo(() => buildAgentView(state), [state.byId, state.subagentsByParent, state.current, state.currentAddress])
@@ -287,7 +341,7 @@ function AgentTree({ useSessions, useTabInfo, treeStates, openDetail, refresh, w
287
341
  }
288
342
  return value
289
343
  }, [treeStates, tab.signal, view.root])
290
- return React.createElement(AgentTreeBody, { key: view.root, state, view, saved, openDetail, refresh, watchCatalog })
344
+ return React.createElement(AgentTreeBody, { key: view.root, state, view, saved, openDetail, refresh, watchCatalog, t })
291
345
  }
292
346
 
293
347
  function AgentCatalogWatch({ id, watchCatalog }) {
@@ -316,13 +370,13 @@ function agentTreeLayout(view, limit) {
316
370
  height: Math.max(140, ...rows.map(row => row.level * 128)) }
317
371
  }
318
372
 
319
- function agentExecutionStatus(node, isRoot) {
320
- if (!node || node.unavailable || typeof node.running !== 'boolean') return { tone: 'unavailable', label: '记录不可用' }
321
- if (node.running) return { tone: 'active', label: '运行中' }
322
- return isRoot || node.mode === 'continuable' ? { tone: 'idle', label: '待命' } : { tone: 'stopped', label: '已停止' }
373
+ function agentExecutionStatus(node, isRoot, t = key => zh[key] || key) {
374
+ if (!node || node.unavailable || typeof node.running !== 'boolean') return { tone: 'unavailable', label: text(t, 'tree.legend.unavailable') }
375
+ if (node.running) return { tone: 'active', label: text(t, 'tree.legend.active') }
376
+ return isRoot || node.mode === 'continuable' ? { tone: 'idle', label: text(t, 'tree.legend.idle') } : { tone: 'stopped', label: text(t, 'tree.legend.stopped') }
323
377
  }
324
378
 
325
- function AgentTreeBody({ state, view, saved, openDetail, refresh, watchCatalog }) {
379
+ function AgentTreeBody({ state, view, saved, openDetail, refresh, watchCatalog, t = key => zh[key] || key }) {
326
380
  const [pan, setPan] = useState(saved.pan || { x: 0, y: 0 })
327
381
  const [zoom, setZoom] = useState(saved.zoom || 1)
328
382
  const viewport = useRef(null), graph = useRef(null)
@@ -402,12 +456,12 @@ function AgentTreeBody({ state, view, saved, openDetail, refresh, watchCatalog }
402
456
  ...Object.keys(catalogs).filter(id => view.nodes.has(id))])]
403
457
  const open = node => {
404
458
  setError('')
405
- Promise.resolve().then(() => openDetail(node)).catch(() => setError('无法打开详情,请重试'))
459
+ Promise.resolve().then(() => openDetail(node)).catch(() => setError(text(t, 'detail.noAccess')))
406
460
  }
407
461
  const reload = async () => {
408
462
  setBusy(true); setError('')
409
463
  try { await Promise.all([...new Set([view.root, ...watched])].filter(Boolean).map(id => refresh(id))) }
410
- catch { setError('执行记录刷新失败') }
464
+ catch { setError(text(t, 'tree.loadError')) }
411
465
  finally { setBusy(false) }
412
466
  }
413
467
  const keyDown = (event, row) => {
@@ -438,23 +492,23 @@ function AgentTreeBody({ state, view, saved, openDetail, refresh, watchCatalog }
438
492
  watched.map(id => React.createElement(AgentCatalogWatch, { key: id, id, watchCatalog })),
439
493
  React.createElement('header', { className: 'dsh-super-code-tree-header' },
440
494
  React.createElement('div', null,
441
- React.createElement('strong', null, '协作执行'),
442
- React.createElement('span', { className: 'dsh-super-code-tree-count' }, `显示 ${rows.length} / ${view.nodes.size} · 运行中 ${running}`)),
443
- React.createElement('button', { type: 'button', className: 'dsh-super-code-icon-button', disabled: busy || !view.root, onClick: reload, title: '刷新执行记录', 'aria-label': '刷新执行记录' }, React.createElement(IconRefreshOutline16))),
444
- (error || catalogError) && React.createElement('p', { role: 'alert', className: 'dsh-super-code-tree-error' }, error || '部分执行记录读取失败,请刷新重试'),
445
- React.createElement('div', { className: 'dsh-super-code-graph-legend', 'aria-label': '状态颜色' },
446
- ['运行中', '待命', '已停止', '记录不可用'].map((label, index) => React.createElement('span', { key: label },
447
- React.createElement('i', { className: 'dsh-super-code-circle ' + ['active', 'idle', 'stopped', 'unavailable'][index], 'aria-hidden': true }), label))),
448
- !view.root && React.createElement('p', { className: 'dsh-super-code-tree-empty' }, '暂无会话'),
449
- React.createElement('div', { className: 'dsh-super-code-graph-controls', 'aria-label': '画布操作' },
450
- React.createElement('button', { type: 'button', 'aria-label': '缩小节点图', disabled: zoom <= .4, onClick: () => zoomButton(1 / 1.2) }, '−'),
495
+ React.createElement('strong', null, text(t, 'tree.title')),
496
+ React.createElement('span', { className: 'dsh-super-code-tree-count' }, text(t, 'tree.count', { shown: rows.length, total: view.nodes.size, running }))),
497
+ React.createElement('button', { type: 'button', className: 'dsh-super-code-icon-button', disabled: busy || !view.root, onClick: reload, title: text(t, 'tree.refresh'), 'aria-label': text(t, 'tree.refresh') }, React.createElement(IconRefreshOutline16))),
498
+ (error || catalogError) && React.createElement('p', { role: 'alert', className: 'dsh-super-code-tree-error' }, error || text(t, 'tree.loadError')),
499
+ React.createElement('div', { className: 'dsh-super-code-graph-legend', 'aria-label': text(t, 'tree.status') },
500
+ ['tree.legend.active', 'tree.legend.idle', 'tree.legend.stopped', 'tree.legend.unavailable'].map((key, index) => React.createElement('span', { key },
501
+ React.createElement('i', { className: 'dsh-super-code-circle ' + ['active', 'idle', 'stopped', 'unavailable'][index], 'aria-hidden': true }), text(t, key)))),
502
+ !view.root && React.createElement('p', { className: 'dsh-super-code-tree-empty' }, text(t, 'tree.empty')),
503
+ React.createElement('div', { className: 'dsh-super-code-graph-controls', 'aria-label': text(t, 'tree.canvas') },
504
+ React.createElement('button', { type: 'button', 'aria-label': text(t, 'tree.zoom.out'), disabled: zoom <= .4, onClick: () => zoomButton(1 / 1.2) }, '−'),
451
505
  React.createElement('span', { 'aria-live': 'polite' }, `${Math.round(zoom * 100)}%`),
452
- React.createElement('button', { type: 'button', 'aria-label': '放大节点图', disabled: zoom >= 2, onClick: () => zoomButton(1.2) }, '+'),
453
- React.createElement('button', { type: 'button', onClick: fit }, '适应窗口')),
506
+ React.createElement('button', { type: 'button', 'aria-label': text(t, 'tree.zoom.in'), disabled: zoom >= 2, onClick: () => zoomButton(1.2) }, '+'),
507
+ React.createElement('button', { type: 'button', onClick: fit }, text(t, 'tree.fit'))),
454
508
  (display.hidden.length > 0 || expanded.size > 0 || collapsed.size > 0) && React.createElement('div', { className: 'dsh-super-code-history-controls' },
455
- React.createElement('button', { type: 'button', 'aria-expanded': historyOpen, onClick: () => setHistoryOpen(!historyOpen) }, `已收起 ${display.hidden.length}`),
456
- (expanded.size > 0 || collapsed.size > 0) && React.createElement('button', { type: 'button', onClick: () => { autoFit.current = true; setExpanded(new Set()); setCollapsed(new Set()); setFocused(state.current) } }, '自动整理')),
457
- historyOpen && React.createElement(AgentHistory, { view, hidden: display.hidden, totals, open }),
509
+ React.createElement('button', { type: 'button', 'aria-expanded': historyOpen, onClick: () => setHistoryOpen(!historyOpen) }, text(t, 'tree.collapsed', { count: display.hidden.length })),
510
+ (expanded.size > 0 || collapsed.size > 0) && React.createElement('button', { type: 'button', onClick: () => { autoFit.current = true; setExpanded(new Set()); setCollapsed(new Set()); setFocused(state.current) } }, text(t, 'tree.organize'))),
511
+ historyOpen && React.createElement(AgentHistory, { view, hidden: display.hidden, totals, open, t }),
458
512
  React.createElement('div', { ref: viewport, className: 'dsh-super-code-graph-scroll',
459
513
  onPointerDown: event => {
460
514
  if (!event.isPrimary || event.button !== 0) return
@@ -474,20 +528,20 @@ function AgentTreeBody({ state, view, saved, openDetail, refresh, watchCatalog }
474
528
  onPointerUp: finishDrag, onPointerCancel: finishDrag, onLostPointerCapture: finishDrag,
475
529
  onClickCapture: event => { if (gesture.current.moved && event.detail !== 0) { event.preventDefault(); event.stopPropagation() } },
476
530
  onDoubleClick: event => { if (!event.target.closest('[role="treeitem"]')) fit() },
477
- title: '拖动平移,滚轮缩放,双击空白适应窗口',
531
+ title: text(t, 'tree.canvas'),
478
532
  },
479
- React.createElement('div', { ref: graph, role: 'tree', 'aria-label': 'Agent 执行树', className: 'dsh-super-code-graph', style: { width, height, transform: `translate(${pan.x}px, ${pan.y}px) scale(${zoom})` } },
533
+ React.createElement('div', { ref: graph, role: 'tree', 'aria-label': text(t, 'tree.aria'), className: 'dsh-super-code-graph', style: { width, height, transform: `translate(${pan.x}px, ${pan.y}px) scale(${zoom})` } },
480
534
  React.createElement('svg', { width, height, className: 'dsh-super-code-graph-edges', 'aria-hidden': true }, edges.map(edge => {
481
535
  const parent = positions.get(edge.parent), child = positions.get(edge.child)
482
536
  return React.createElement('path', { key: edge.child, d: `M ${parent.x} ${parent.y + 105} V ${child.y - 12} H ${child.x} V ${child.y}`, fill: 'none' })
483
537
  })),
484
538
  rows.map(row => {
485
539
  const node = view.nodes.get(row.id)
486
- const title = node.title || node.displayTitle || '主 Agent'
487
- const { label: status, tone } = agentExecutionStatus(node, row.id === view.root)
540
+ const title = node.title || node.displayTitle || text(t, 'main.agent')
541
+ const { label: status, tone } = agentExecutionStatus(node, row.id === view.root, t)
488
542
  const usage = totals.get(row.id)
489
543
  const partial = usage.partial || usage.known < usage.agents
490
- const number = value => value.toLocaleString('zh-CN', { notation: 'compact', maximumFractionDigits: 1 })
544
+ const number = value => value.toLocaleString(localeCode(t), { notation: 'compact', maximumFractionDigits: 1 })
491
545
  const hasChildren = (view.children.get(row.id) || []).length > 0
492
546
  const isExpanded = (display.view.children.get(row.id) || []).length > 0
493
547
  return React.createElement(React.Fragment, { key: row.id }, React.createElement('button', {
@@ -497,23 +551,23 @@ function AgentTreeBody({ state, view, saved, openDetail, refresh, watchCatalog }
497
551
  'aria-expanded': hasChildren ? isExpanded : undefined,
498
552
  'aria-disabled': node.unavailable || undefined, 'aria-label': `${title},${status}`,
499
553
  className: `dsh-super-code-graph-node${row.id === state.current ? ' selected' : ''}${node.running ? ' running' : ''}`,
500
- style: { left: row.x - 64, top: row.y }, title: `${title} · ${status} · 点击查看详情`,
554
+ style: { left: row.x - 64, top: row.y }, title: `${title} · ${status} · ${text(t, 'tree.open')}`,
501
555
  onFocus: () => setFocused(row.id), onClick: () => { if (!node.unavailable) open(node) },
502
556
  onKeyDown: event => { if (event.target === event.currentTarget) keyDown(event, row) },
503
557
  },
504
- React.createElement('span', { className: 'dsh-super-code-circle ' + tone, 'aria-hidden': true }, row.id === view.root ? '主' : String(rows.findIndex(item => item.id === row.id))),
558
+ React.createElement('span', { className: 'dsh-super-code-circle ' + tone, 'aria-hidden': true }, row.id === view.root ? text(t, 'main.agent').slice(0, 1) : String(rows.findIndex(item => item.id === row.id))),
505
559
  React.createElement('span', { className: 'dsh-super-code-tree-content' },
506
560
  React.createElement('span', { className: 'dsh-super-code-tree-title' }, title),
507
- React.createElement('span', { className: 'dsh-super-code-tree-usage', title: `本节点及所有已知下级合计,包含本节点。${partial ? '部分记录或用量尚不可用;仅显示已知值。' : ''}Token ${usage.tokens.toLocaleString('zh-CN')};缓存读取 ${usage.cache.toLocaleString('zh-CN')};${usage.agents} 个 Agent` },
508
- React.createElement('span', null, usage.known ? `${number(usage.tokens)} token` : '用量 —'),
509
- React.createElement('span', null, usage.known ? `缓存 ${usage.input ? Math.round(usage.cache / usage.input * 100) : 0}%${partial ? ' · 部分' : ''}` : '缓存 —')))),
561
+ React.createElement('span', { className: 'dsh-super-code-tree-usage', title: text(t, 'tree.usage', { partial: partial ? text(t, 'usage.incomplete') : '', tokens: usage.tokens.toLocaleString(), cache: usage.cache.toLocaleString(), agents: usage.agents }) },
562
+ React.createElement('span', null, usage.known ? `${number(usage.tokens)} token` : text(t, 'usage.unknown')),
563
+ React.createElement('span', null, usage.known ? text(t, 'usage.cache', { value: usage.input ? Math.round(usage.cache / usage.input * 100) : 0 }) + (partial ? ` · ${text(t, 'usage.partial')}` : '') : 'Cache —')))),
510
564
  hasChildren && React.createElement('button', { type: 'button', className: 'dsh-super-code-branch-toggle',
511
- style: { left: row.x + 21, top: row.y + 25 }, 'aria-label': `${collapsed.has(row.id) || !isExpanded ? '展开' : '收起'} ${title} 的下级`,
512
- title: '整理下级节点;运行中的分支始终保留', onClick: () => toggleBranch(row.id) },
565
+ style: { left: row.x + 21, top: row.y + 25 }, 'aria-label': `${collapsed.has(row.id) || !isExpanded ? text(t, 'tree.expand') : text(t, 'tree.collapse')} ${title}`,
566
+ title: text(t, 'tree.organize'), onClick: () => toggleBranch(row.id) },
513
567
  React.createElement('svg', { width: 12, height: 12, viewBox: '0 0 12 12', 'aria-hidden': true, style: { transform: collapsed.has(row.id) || !isExpanded ? 'rotate(-90deg)' : undefined } },
514
568
  React.createElement('path', { d: 'M3 4.5 6 7.5 9 4.5', fill: 'none', stroke: 'currentColor', strokeWidth: 1.5 }))))
515
569
  }))),
516
- more && React.createElement('button', { type: 'button', className: 'dsh-super-code-tree-more', onClick: () => setLimit(limit + 200) }, '显示更多'))
570
+ more && React.createElement('button', { type: 'button', className: 'dsh-super-code-tree-more', onClick: () => setLimit(limit + 200) }, text(t, 'tree.more')))
517
571
  }
518
572
 
519
573
  function agentDetailAddress(node) {
@@ -554,22 +608,22 @@ function contentText(blocks) {
554
608
  }).filter(Boolean).join('\n\n')
555
609
  }
556
610
 
557
- function detailRecord(entry) {
611
+ function detailRecord(entry, t = key => zh[key] || key) {
558
612
  const event = entry.event, data = event?.data
559
613
  if (!data) return { kind: 'skip' }
560
614
  const base = { seq: event.seq, time: event.time }
561
615
  if (event.type === 'user/message') {
562
616
  if (data.source?.kind !== 'user') return { kind: 'skip' }
563
- return { ...base, kind: 'message', label: '任务', text: contentText(data.content) }
617
+ return { ...base, kind: 'message', label: text(t, 'detail.task'), text: contentText(data.content) }
564
618
  }
565
- if (event.type === 'assistant/message') return { ...base, kind: 'message', label: data.interrupted ? 'Agent · 已中断' : 'Agent', text: contentText(data.message?.content) }
566
- if (event.type === 'turn/error') return { ...base, kind: 'error', label: '执行错误', text: data.message || '执行未完成,请稍后重试。' }
619
+ if (event.type === 'assistant/message') return { ...base, kind: 'message', label: data.interrupted ? text(t, 'detail.interrupted') : text(t, 'detail.agent'), text: contentText(data.message?.content) }
620
+ if (event.type === 'turn/error') return { ...base, kind: 'error', label: text(t, 'detail.error'), text: data.message || text(t, 'detail.incomplete') }
567
621
  return { kind: 'skip' }
568
622
  }
569
623
 
570
624
  // The host journal owns decoding, cursor validation and reconnects. This view
571
625
  // keeps a bounded display window and never stages or sends to another session.
572
- function createAgentInspector(createStream, address, readPage, initial) {
626
+ function createAgentInspector(createStream, address, readPage, initial, t = key => zh[key] || key) {
573
627
  let snapshot = initial || { status: 'loading', items: [], hasMore: false, earlier: false, truncated: false, projections: {}, error: '', firstSeq: 0, cursor: -1 }
574
628
  let cursor = snapshot.cursor
575
629
  let disposed = false, stream
@@ -580,7 +634,7 @@ function createAgentInspector(createStream, address, readPage, initial) {
580
634
  snapshot = { ...snapshot, cursor, ...patch }
581
635
  listeners.forEach(listener => listener())
582
636
  }
583
- const itemsOf = entries => entries.map(detailRecord).filter(item => item.kind !== 'skip' && item.text)
637
+ const itemsOf = entries => entries.map(entry => detailRecord(entry, t)).filter(item => item.kind !== 'skip' && item.text)
584
638
  stream = createStream(agentDetailTarget(address), {
585
639
  publish: change => {
586
640
  if (disposed) return
@@ -609,13 +663,13 @@ function createAgentInspector(createStream, address, readPage, initial) {
609
663
  }
610
664
  }
611
665
  },
612
- carrierFailed: () => publish({ error: '连接中断,正在重连' }),
613
- failed: () => publish({ status: 'error', error: '对话详情读取失败' }),
666
+ carrierFailed: () => publish({ error: text(t, 'detail.connection') }),
667
+ failed: () => publish({ status: 'error', error: text(t, 'detail.loadError') }),
614
668
  })
615
669
  return {
616
670
  getSnapshot: () => snapshot,
617
671
  subscribe: listener => { listeners.add(listener); return () => listeners.delete(listener) },
618
- start: () => { stream.open({ maxMessages: 40 }).catch(() => publish({ status: 'error', error: '对话详情读取失败' })) },
672
+ start: () => { stream.open({ maxMessages: 40 }).catch(() => publish({ status: 'error', error: text(t, 'detail.loadError') })) },
619
673
  older: async () => {
620
674
  if (snapshot.status === 'paging' || !snapshot.hasMore) return
621
675
  publish({ status: 'paging', error: '' })
@@ -628,20 +682,20 @@ function createAgentInspector(createStream, address, readPage, initial) {
628
682
  firstSeq: items.length > 200 ? visible[0].seq : result.value.records[0]?.event.seq ?? 0,
629
683
  hasMore: result.value.hasMore || items.length > 200 })
630
684
  }
631
- catch { publish({ status: 'error', error: '历史记录读取失败' }) }
685
+ catch { publish({ status: 'error', error: text(t, 'detail.historyError') }) }
632
686
  },
633
687
  dispose: () => { disposed = true; listeners.clear(); void stream.dispose() },
634
688
  }
635
689
  }
636
690
 
637
- function DetailText({ text }) {
691
+ function DetailText({ text: value, t = key => zh[key] || key }) {
638
692
  const [full, setFull] = useState(false)
639
693
  return React.createElement(React.Fragment, null,
640
- React.createElement('pre', null, full ? text : text.slice(0, 12000)),
641
- !full && text.length > 12000 && React.createElement('button', { type: 'button', onClick: () => setFull(true) }, '展开全文'))
694
+ React.createElement('pre', null, full ? value : value.slice(0, 12000)),
695
+ !full && value.length > 12000 && React.createElement('button', { type: 'button', onClick: () => setFull(true) }, text(t, 'detail.open')))
642
696
  }
643
697
 
644
- function AgentDetail({ useTabInfo, useSessions, createInspector, detailStates }) {
698
+ function AgentDetail({ useTabInfo, useSessions, createInspector, detailStates, t = key => zh[key] || key }) {
645
699
  const { tab } = useTabInfo()
646
700
  const state = useSessions(s => s)
647
701
  const saved = useMemo(() => {
@@ -687,52 +741,109 @@ function AgentDetail({ useTabInfo, useSessions, createInspector, detailStates })
687
741
  const projections = state.byId[id]?.projectionValues || snapshot.projections
688
742
  const totals = projections.superAgentUsage?.totals || projections.tokenUsage
689
743
  const input = totals ? (totals.uncachedInputTokens || 0) + (totals.cacheReadTokens || 0) + (totals.cacheWriteTokens || 0) : 0
690
- const format = value => value === undefined ? '未提供' : value.toLocaleString('zh-CN')
744
+ const format = value => value === undefined ? text(t, 'detail.unavailable') : value.toLocaleString(localeCode(t))
691
745
  const tasks = projections.superCodeTasks
692
746
  const task = tasks?.current?.kind === 'task' ? tasks.current : undefined
693
- const title = node?.title || node?.displayTitle || 'Agent 详情'
747
+ const title = node?.title || node?.displayTitle || text(t, 'detail.title')
694
748
  const metrics = [
695
- ['输入', totals ? input : undefined], ['输出', totals?.outputTokens],
696
- ['缓存读取', totals?.cacheReadTokens], ['缓存写入', totals?.cacheWriteTokens],
749
+ [text(t, 'detail.input'), totals ? input : undefined], [text(t, 'detail.output'), totals?.outputTokens],
750
+ [text(t, 'detail.cacheRead'), totals?.cacheReadTokens], [text(t, 'detail.cacheWrite'), totals?.cacheWriteTokens],
697
751
  ]
698
752
  return React.createElement('section', { className: 'dsh-super-code-detail', 'data-agent-detail': id },
699
753
  React.createElement('header', { className: 'dsh-super-code-detail-header' },
700
- React.createElement('nav', { 'aria-label': 'Agent 路径' }, path.map((part, index) => React.createElement('span', { key: part.id }, index > 0 ? ' / ' : '', part.title))),
754
+ React.createElement('nav', { 'aria-label': text(t, 'detail.path') }, path.map((part, index) => React.createElement('span', { key: part.id }, index > 0 ? ' / ' : '', part.title))),
701
755
  React.createElement('div', { className: 'dsh-super-code-detail-heading' },
702
756
  React.createElement('div', null,
703
757
  React.createElement('h2', null, title),
704
- React.createElement('span', { className: 'dsh-super-code-detail-status' }, agentExecutionStatus(node, id === view.root).label)),
705
- React.createElement('button', { type: 'button', className: 'dsh-super-code-icon-button', title: '刷新对话详情', 'aria-label': '刷新对话详情', onClick: latest }, React.createElement(IconRefreshOutline16))),
758
+ React.createElement('span', { className: 'dsh-super-code-detail-status' }, agentExecutionStatus(node, id === view.root, t).label)),
759
+ React.createElement('button', { type: 'button', className: 'dsh-super-code-icon-button', title: text(t, 'detail.refresh'), 'aria-label': text(t, 'detail.refresh'), onClick: latest }, React.createElement(IconRefreshOutline16))),
706
760
  id !== view.root && task?.goal && task.goal !== title && React.createElement('p', { className: 'dsh-super-code-detail-task' }, task.goal),
707
- id === view.root && React.createElement(TaskMemoryPanel, { state: tasks }),
761
+ id === view.root && React.createElement(TaskMemoryPanel, { state: tasks, t }),
708
762
  React.createElement('dl', { className: 'dsh-super-code-detail-metrics' },
709
- React.createElement('div', null, React.createElement('dt', null, 'Token 总量'), React.createElement('dd', null, format(totals ? input + (totals.outputTokens || 0) : undefined))),
710
- React.createElement('div', null, React.createElement('dt', null, '缓存命中率'), React.createElement('dd', null, totals ? (input ? Math.round((totals.cacheReadTokens || 0) / input * 100) : 0) + '%' : '未提供'))),
763
+ React.createElement('div', null, React.createElement('dt', null, text(t, 'detail.total')), React.createElement('dd', null, format(totals ? input + (totals.outputTokens || 0) : undefined))),
764
+ React.createElement('div', null, React.createElement('dt', null, text(t, 'detail.hitRate')), React.createElement('dd', null, totals ? (input ? Math.round((totals.cacheReadTokens || 0) / input * 100) : 0) + '%' : text(t, 'detail.unavailable')))),
711
765
  React.createElement('details', { className: 'dsh-super-code-usage-details', open: saved.open.has('usage'), onToggle: event => { if (event.currentTarget.open) saved.open.add('usage'); else saved.open.delete('usage') } },
712
- React.createElement('summary', null, '用量详情'),
766
+ React.createElement('summary', null, text(t, 'detail.moreUsage')),
713
767
  React.createElement('dl', { className: 'dsh-super-code-detail-metrics' }, metrics.map(([label, value]) => React.createElement('div', { key: label }, React.createElement('dt', null, label), React.createElement('dd', null, format(value))))))),
714
- React.createElement('div', { ref: log, className: 'dsh-super-code-detail-log', 'aria-label': 'Agent 执行内容', onScroll: event => {
768
+ React.createElement('div', { ref: log, className: 'dsh-super-code-detail-log', 'aria-label': text(t, 'tree.title'), onScroll: event => {
715
769
  const element = event.currentTarget
716
770
  saved.top = element.scrollTop
717
771
  saved.following = !snapshot.earlier && element.scrollHeight - element.clientHeight - element.scrollTop < 32
718
772
  if (saved.following) markUnread(false)
719
773
  } },
720
774
  snapshot.error && React.createElement('p', { role: 'alert' }, snapshot.error),
721
- snapshot.status === 'loading' && React.createElement('p', { role: 'status' }, '正在读取对话…'),
722
- snapshot.hasMore && React.createElement('button', { type: 'button', disabled: snapshot.status === 'paging', onClick: inspector.older }, snapshot.status === 'paging' ? '读取中…' : '加载更早记录'),
723
- (snapshot.earlier || snapshot.truncated) && React.createElement('button', { type: 'button', onClick: latest }, '回到最新'),
724
- snapshot.status === 'ready' && !snapshot.items.length && React.createElement('p', null, '暂无执行内容'),
775
+ snapshot.status === 'loading' && React.createElement('p', { role: 'status' }, text(t, 'detail.reading')),
776
+ snapshot.hasMore && React.createElement('button', { type: 'button', disabled: snapshot.status === 'paging', onClick: inspector.older }, snapshot.status === 'paging' ? text(t, 'detail.read') : text(t, 'detail.older')),
777
+ (snapshot.earlier || snapshot.truncated) && React.createElement('button', { type: 'button', onClick: latest }, text(t, 'detail.latest')),
778
+ snapshot.status === 'ready' && !snapshot.items.length && React.createElement('p', null, text(t, 'detail.empty')),
725
779
  snapshot.items.map(item => React.createElement('article', { key: item.seq, className: 'dsh-super-code-detail-entry ' + item.kind },
726
780
  item.collapsed ? React.createElement('details', { open: saved.open.has(item.seq), onToggle: event => { if (event.currentTarget.open) saved.open.add(item.seq); else saved.open.delete(item.seq) } },
727
781
  React.createElement('summary', null, item.label),
728
- React.createElement(DetailText, { text: item.text }))
782
+ React.createElement(DetailText, { text: item.text, t }))
729
783
  : React.createElement(React.Fragment, null,
730
784
  React.createElement('div', { className: 'dsh-super-code-detail-entry-label' }, item.label,
731
- React.createElement('time', null, new Date(item.time).toLocaleTimeString('zh-CN', { hour12: false }))),
732
- React.createElement(DetailText, { text: item.text })))) ),
785
+ React.createElement('time', null, new Date(item.time).toLocaleTimeString(localeCode(t), { hour12: false }))),
786
+ React.createElement(DetailText, { text: item.text, t })))) ),
733
787
  unread && React.createElement('button', { type: 'button', className: 'dsh-super-code-new-content', onClick: () => {
734
788
  saved.following = true; log.current.scrollTop = log.current.scrollHeight; markUnread(false)
735
- } }, '有新内容 · 查看最新'))
789
+ } }, text(t, 'detail.new')))
790
+ }
791
+
792
+ // Explicit public Remote contribution; no model tools or private transport hooks.
793
+ const presetRemoteContribution = {
794
+ package: 'dsh-super-code',
795
+ descriptors: ['status', 'installPreset'].map(method => ({
796
+ id: `dsh-super-code:superCodePresets/${method}`, service: 'superCodePresets',
797
+ namespace: 'superCodePresets', method, invocation: { kind: 'direct' },
798
+ parameters: method === 'installPreset' ? [{ name: 'id', wire: 'id', source: 'json', codec: { mode: 'strict', typeSymbol: 'dsh-super-code#PresetId', schema: { parse(value) {
799
+ if (typeof value !== 'string' || !/^[a-z0-9][a-z0-9-]{0,63}$/.test(value)) throw new Error('Invalid preset name')
800
+ return value
801
+ } } } }] : [],
802
+ result: { mode: 'src-json' },
803
+ })),
804
+ }
805
+
806
+ function PresetSettings({ api, t }) {
807
+ const [status, setStatus] = useState(null), [name, setName] = useState('super-code')
808
+ const [busy, setBusy] = useState(true), [error, setError] = useState('')
809
+ const alive = useRef(false), pending = useRef(false)
810
+ const run = async install => {
811
+ if (pending.current) return
812
+ pending.current = true; setBusy(true); setError('')
813
+ try {
814
+ const response = install ? await api.installPreset(name.trim()) : await api.status()
815
+ if (!response.ok) throw new Error('Remote unavailable')
816
+ if (!alive.current) return
817
+ const value = response.value
818
+ setStatus(install ? value.status : value)
819
+ if (install && !value.ok) setError(t(`preset.error.${value.error}`))
820
+ } catch {
821
+ if (alive.current) setError(t('preset.error.connection'))
822
+ } finally {
823
+ pending.current = false
824
+ if (alive.current) setBusy(false)
825
+ }
826
+ }
827
+ useEffect(() => { alive.current = true; run(false); return () => { alive.current = false } }, [api])
828
+ const available = status && ['available', 'installed'].includes(status.state)
829
+ return React.createElement('section', { className: 'dsh-super-code-settings', 'aria-label': 'Super Code' },
830
+ React.createElement('header', null,
831
+ React.createElement('div', null, React.createElement('h3', null, 'Super Code'), React.createElement('p', null, t('preset.description'))),
832
+ React.createElement('button', { type: 'button', disabled: busy, onClick: () => run(false), 'aria-label': t('preset.refresh') }, t('preset.refresh'))),
833
+ status && React.createElement('p', { className: 'dsh-super-code-preset-status', role: 'status', 'data-available': available },
834
+ React.createElement('span', { 'aria-hidden': true }, '●'), ' ', t(`preset.state.${status.state}`), ' · ', status.id),
835
+ status && React.createElement('p', null, t(available ? 'preset.availableHint' : 'preset.installHint')),
836
+ status?.userConflict && status.state !== 'conflict' && React.createElement('p', null, t('preset.userConflict')),
837
+ status && !status.authorable && React.createElement('p', { role: 'note' }, t('preset.readonly')),
838
+ status?.authorable && React.createElement('form', { onSubmit: event => { event.preventDefault(); run(true) } },
839
+ React.createElement('label', { htmlFor: 'super-code-preset-name' }, t('preset.name')),
840
+ React.createElement('div', { className: 'dsh-super-code-install-row' },
841
+ React.createElement('input', { id: 'super-code-preset-name', value: name, maxLength: 64, disabled: busy,
842
+ pattern: '[a-z0-9][a-z0-9\\-]{0,63}', required: true, autoComplete: 'off', spellCheck: false,
843
+ 'aria-describedby': 'super-code-preset-name-hint', onChange: event => setName(event.target.value) }),
844
+ React.createElement('button', { type: 'submit', disabled: busy || !/^[a-z0-9][a-z0-9-]{0,63}$/.test(name.trim()) }, t(busy ? 'preset.working' : 'preset.install'))),
845
+ React.createElement('small', { id: 'super-code-preset-name-hint' }, t('preset.nameHint'))),
846
+ error && React.createElement('p', { role: 'alert' }, error))
736
847
  }
737
848
 
738
849
  const inject = [
@@ -741,10 +852,32 @@ const inject = [
741
852
  ]
742
853
 
743
854
  function apply(ctx) {
855
+ const locale = ctx.locale || { register: () => () => {}, bind: () => key => zh[key] || key }
856
+ ctx.effect(() => locale.register(LOCALE_NS, { zh, en }), 'dsh-super-code: dictionaries')
857
+ const t = locale.bind(LOCALE_NS)
858
+ ctx.effect(async () => {
859
+ const owner = ctx
860
+ await owner.remote.$mount(presetRemoteContribution)
861
+ owner.inject(['remote.superCodePresets'], ready => ready.slots.inject('settings.plugin.item', () => ready.slots.register({
862
+ name: 'settings.plugin.item', key: 'super-code', locale: LOCALE_NS,
863
+ inject: () => ({ api: ready.remote.superCodePresets, t }),
864
+ }, PresetSettings)))
865
+ })
744
866
  ctx.effect(() => {
745
867
  const style = document.createElement('style')
746
868
  style.dataset.dshSuperCode = 'true'
747
869
  style.textContent = `
870
+ .dsh-super-code-settings{padding:20px;border:1px solid var(--dsw-alias-border-l1,#dedfe3);border-radius:12px;line-height:1.6;font-size:13px}
871
+ .dsh-super-code-settings header{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}
872
+ .dsh-super-code-settings h3{margin:0;font-size:15px;font-weight:600}.dsh-super-code-settings p{margin:8px 0;color:var(--dsw-alias-label-secondary,#70757d)}
873
+ .dsh-super-code-settings button,.dsh-super-code-settings input{font:inherit;color:inherit;border:1px solid var(--dsw-alias-border-l1,#dedfe3);border-radius:7px;background:transparent;padding:7px 10px;min-height:34px}
874
+ .dsh-super-code-settings button{cursor:pointer;white-space:nowrap}.dsh-super-code-settings button:disabled{opacity:.45;cursor:default}
875
+ .dsh-super-code-settings form{margin-top:16px}.dsh-super-code-settings label{display:block;margin-bottom:6px}
876
+ .dsh-super-code-install-row{display:flex;gap:8px;flex-wrap:wrap}.dsh-super-code-install-row input{flex:1;min-width:140px}
877
+ .dsh-super-code-settings small{display:block;margin-top:6px;color:var(--dsw-alias-label-secondary,#70757d)}
878
+ .dsh-super-code-settings .dsh-super-code-preset-status{color:inherit}.dsh-super-code-preset-status span{color:#b87716}.dsh-super-code-preset-status[data-available=true] span{color:#008b78}
879
+ .dsh-super-code-settings [role=alert]{color:var(--dsw-color-text-error,#bd423b)}.dsh-super-code-settings :focus-visible{outline:2px solid var(--dsw-color-primary,#3276dc);outline-offset:2px}
880
+
748
881
  .dsh-super-code-tree{--agent-muted:var(--dsw-alias-label-secondary,#70757d);--agent-line:var(--dsw-alias-border-l1,#dedfe3);height:100%;min-width:0;min-height:0;display:flex;flex-direction:column;overflow:auto;font-size:13px;letter-spacing:0;color:inherit}
749
882
  .dsh-super-code-tree>header,.dsh-super-code-tree>.dsh-super-code-graph-controls,.dsh-super-code-tree>.dsh-super-code-graph-legend,.dsh-super-code-tree>.dsh-super-code-history-controls{flex-shrink:0}
750
883
  .dsh-super-code-tree-header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:20px 18px 16px}
@@ -882,8 +1015,8 @@ function apply(ctx) {
882
1015
  const treeKind = 'super-agent-agents'
883
1016
  const disposeTreeType = ctx.sidebarRightTabs.register({
884
1017
  id: treeId, kind: treeKind,
885
- title: () => 'Agent 执行树',
886
- guide: [{ order: 40, title: () => 'Agent 执行树', description: () => '查看 Agent 层级和执行对话' }],
1018
+ title: () => t('tree.aria'),
1019
+ guide: [{ order: 40, title: () => t('tree.aria'), description: () => t('tree.canvas') }],
887
1020
  })
888
1021
  const treeActions = {
889
1022
  // The host unmounts hidden bodies; tab signals live until close/unload.
@@ -897,20 +1030,20 @@ function apply(ctx) {
897
1030
  title: address => {
898
1031
  const target = agentDetailTarget(address), state = ctx.sessions.list.getSnapshot()
899
1032
  const node = buildAgentView(state).nodes.get(target.childSessionId || target.sessionId)
900
- return node?.title || node?.displayTitle || 'Agent 详情'
1033
+ return node?.title || node?.displayTitle || t('detail.title')
901
1034
  },
902
1035
  })
903
1036
  const detailActions = { detailStates: new WeakMap(), createInspector: (address, initial) => createAgentInspector(
904
1037
  (target, options) => new SessionEventStream(ctx.remote, target, options), address,
905
- (request, signal) => ctx.remote.session.page(request, signal), initial) }
1038
+ (request, signal) => ctx.remote.session.page(request, signal), initial, t) }
906
1039
  ctx.slots.inject('sidebar.right.pane.tab', () => ctx.slots.register({
907
- name: 'sidebar.right.pane.tab', key: 'dsh-super-code-detail', inject: () => detailActions,
1040
+ name: 'sidebar.right.pane.tab', key: 'dsh-super-code-detail', inject: () => ({ ...detailActions, t }),
908
1041
  }, AgentDetail))
909
1042
  ctx.slots.inject('sidebar.right.pane.tab', () => ctx.slots.register({
910
1043
  // Keyed tab bodies dispatch by the definition id, while `kind` is the
911
1044
  // page/content discriminator used by the Sidebar controller.
912
1045
  name: 'sidebar.right.pane.tab', key: treeId,
913
- inject: () => treeActions,
1046
+ inject: () => ({ ...treeActions, t }),
914
1047
  }, AgentTree))
915
1048
  ctx.effect(() => disposeTreeType, 'super-code: Agent tree tab')
916
1049
  ctx.effect(() => disposeDetailType, 'super-code: Agent detail tab')