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/README.md +65 -117
- package/client/index.js +233 -100
- package/lib/dsh/index.d.ts +0 -1
- package/lib/dsh/index.d.ts.map +1 -1
- package/lib/dsh/index.js +3 -0
- package/lib/dsh/index.js.map +1 -1
- package/lib/dsh/preset-installation.d.ts +43 -0
- package/lib/dsh/preset-installation.d.ts.map +1 -0
- package/lib/dsh/preset-installation.js +193 -0
- package/lib/dsh/preset-installation.js.map +1 -0
- package/package.json +13 -15
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
|
-
|
|
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: '
|
|
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, '
|
|
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 ||
|
|
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':
|
|
253
|
-
React.createElement('input', { type: 'search', value: filters.query, 'aria-label':
|
|
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', '
|
|
256
|
-
select('sort', '
|
|
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 ? '
|
|
260
|
-
React.createElement('p', { className: 'dsh-super-code-history-summary', role: 'status' }, range.valid ?
|
|
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 ? '
|
|
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(
|
|
270
|
-
'aria-label':
|
|
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(
|
|
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: '
|
|
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' },
|
|
443
|
-
React.createElement('button', { type: 'button', className: 'dsh-super-code-icon-button', disabled: busy || !view.root, onClick: reload, title: '
|
|
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
|
-
['
|
|
447
|
-
React.createElement('i', { className: 'dsh-super-code-circle ' + ['active', 'idle', 'stopped', 'unavailable'][index], 'aria-hidden': true }),
|
|
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': '
|
|
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': '
|
|
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) },
|
|
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':
|
|
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 ||
|
|
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(
|
|
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 ? '
|
|
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:
|
|
508
|
-
React.createElement('span', null, usage.known ? `${number(usage.tokens)} token` :
|
|
509
|
-
React.createElement('span', null, usage.known ?
|
|
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 ? '
|
|
512
|
-
title: '
|
|
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: '
|
|
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 ?
|
|
566
|
-
if (event.type === 'turn/error') return { ...base, kind: 'error', label: '
|
|
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 ?
|
|
641
|
-
!full &&
|
|
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 ? '
|
|
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 ||
|
|
747
|
+
const title = node?.title || node?.displayTitle || text(t, 'detail.title')
|
|
694
748
|
const metrics = [
|
|
695
|
-
['
|
|
696
|
-
['
|
|
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':
|
|
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: '
|
|
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,
|
|
710
|
-
React.createElement('div', null, React.createElement('dt', null, '
|
|
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':
|
|
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
|
-
|
|
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(
|
|
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: () => '
|
|
886
|
-
guide: [{ order: 40, title: () => '
|
|
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 || '
|
|
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')
|