dsh-super-code 0.3.6 → 0.3.8
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 +95 -63
- package/client/index.js +1049 -47
- package/cordis.patch.yml +1 -1
- package/docs/Changelog.md +19 -0
- package/docs/Compatibility.md +44 -0
- package/docs/EvaluationResults.md +134 -58
- package/docs/UsageGuide.md +112 -0
- package/lib/connection/directories.d.ts +30 -0
- package/lib/connection/directories.d.ts.map +1 -0
- package/lib/connection/directories.js +82 -0
- package/lib/connection/directories.js.map +1 -0
- package/lib/connection/git.d.ts +5 -0
- package/lib/connection/git.d.ts.map +1 -0
- package/lib/connection/git.js +86 -0
- package/lib/connection/git.js.map +1 -0
- package/lib/connection/http.d.ts +38 -0
- package/lib/connection/http.d.ts.map +1 -0
- package/lib/connection/http.js +275 -0
- package/lib/connection/http.js.map +1 -0
- package/lib/connection/service.d.ts +57 -0
- package/lib/connection/service.d.ts.map +1 -0
- package/lib/connection/service.js +168 -0
- package/lib/connection/service.js.map +1 -0
- package/lib/connection/store.d.ts +21 -0
- package/lib/connection/store.d.ts.map +1 -0
- package/lib/connection/store.js +139 -0
- package/lib/connection/store.js.map +1 -0
- package/lib/connection/tasks.d.ts +84 -0
- package/lib/connection/tasks.d.ts.map +1 -0
- package/lib/connection/tasks.js +270 -0
- package/lib/connection/tasks.js.map +1 -0
- package/lib/connection/types.d.ts +90 -0
- package/lib/connection/types.d.ts.map +1 -0
- package/lib/connection/types.js +21 -0
- package/lib/connection/types.js.map +1 -0
- package/lib/core/deletion-command.d.ts +3 -0
- package/lib/core/deletion-command.d.ts.map +1 -0
- package/lib/core/deletion-command.js +169 -0
- package/lib/core/deletion-command.js.map +1 -0
- package/lib/dsh/budgets.d.ts +5 -0
- package/lib/dsh/budgets.d.ts.map +1 -1
- package/lib/dsh/budgets.js +11 -0
- package/lib/dsh/budgets.js.map +1 -1
- package/lib/dsh/connection-host.d.ts +81 -0
- package/lib/dsh/connection-host.d.ts.map +1 -0
- package/lib/dsh/connection-host.js +130 -0
- package/lib/dsh/connection-host.js.map +1 -0
- package/lib/dsh/connection.d.ts +72 -0
- package/lib/dsh/connection.d.ts.map +1 -0
- package/lib/dsh/connection.js +183 -0
- package/lib/dsh/connection.js.map +1 -0
- package/lib/dsh/deletion-protection.d.ts +13 -0
- package/lib/dsh/deletion-protection.d.ts.map +1 -0
- package/lib/dsh/deletion-protection.js +68 -0
- package/lib/dsh/deletion-protection.js.map +1 -0
- package/lib/dsh/index.d.ts.map +1 -1
- package/lib/dsh/index.js +6 -0
- package/lib/dsh/index.js.map +1 -1
- package/lib/task-feedback-projection.d.ts +68 -0
- package/lib/task-feedback-projection.d.ts.map +1 -0
- package/lib/task-feedback-projection.js +176 -0
- package/lib/task-feedback-projection.js.map +1 -0
- package/package.json +7 -6
- package/presets/super-code/agent.cordis.yml +1 -1
package/client/index.js
CHANGED
|
@@ -256,12 +256,93 @@ const ActionButton = React.forwardRef(function ActionButton({ children, title, .
|
|
|
256
256
|
|
|
257
257
|
const LOCALE_NS = 'dshSuperCode'
|
|
258
258
|
const zh = {
|
|
259
|
-
'
|
|
259
|
+
'feedback.title': '任务状态',
|
|
260
|
+
'feedback.refresh': '刷新任务状态',
|
|
261
|
+
'feedback.noGoal': '尚无目标记录',
|
|
262
|
+
'feedback.source.record': '任务记录',
|
|
263
|
+
'feedback.source.request': '用户原文',
|
|
264
|
+
'feedback.latest': '本轮要求',
|
|
265
|
+
'feedback.progress': '步骤 {done} / {total}',
|
|
266
|
+
'feedback.progressPartial': '已记录步骤 {done} / {total}',
|
|
267
|
+
'feedback.steps': '步骤进度',
|
|
268
|
+
'feedback.todo.pending': '待处理',
|
|
269
|
+
'feedback.todo.in_progress': '进行中',
|
|
270
|
+
'feedback.todo.completed': '已完成',
|
|
271
|
+
'feedback.activity.running': '执行中',
|
|
272
|
+
'feedback.activity.succeeded': '执行成功',
|
|
273
|
+
'feedback.activity.failed': '执行失败',
|
|
274
|
+
'feedback.activity.interrupted': '已中断',
|
|
275
|
+
'feedback.waiting': '等待确认',
|
|
276
|
+
'feedback.error': '执行出错',
|
|
277
|
+
'feedback.interrupted': '已中断',
|
|
278
|
+
'feedback.noSteps': '未记录步骤或工具动作',
|
|
279
|
+
'feedback.loading': '正在读取记录…',
|
|
280
|
+
'feedback.recentOnly': '仅展示最近动作,完整记录见详情',
|
|
281
|
+
'feedback.open': '查看执行记录',
|
|
282
|
+
'feedback.openFileRecord': '查看文件操作记录',
|
|
283
|
+
'feedback.files': '文件变更记录',
|
|
284
|
+
'feedback.filesHint': '仅含已记录的成功操作,终端修改可能缺失',
|
|
285
|
+
'feedback.filesEmpty': '暂无已确认的文件操作',
|
|
286
|
+
'feedback.filesUnknown': '文件记录尚不完整',
|
|
287
|
+
'feedback.file.write': '写入',
|
|
288
|
+
'feedback.file.edit': '编辑',
|
|
289
|
+
'feedback.file.create': '新增',
|
|
290
|
+
'feedback.file.patch': '补丁',
|
|
291
|
+
'feedback.partial': '部分 Agent 或历史记录尚未加载,当前显示已知信息。',
|
|
292
|
+
'feedback.agentsPartial': '任务条目超过显示上限,可在动作执行中查看其他 Agent。',
|
|
293
|
+
'status.blocked': '受阻',
|
|
294
|
+
|
|
295
|
+
'settings.homeTab': '首页', 'settings.presetTab': '预设配置', 'settings.protectionTab': '删除保护',
|
|
296
|
+
"connection.requestTitle": "请求访问工作目录",
|
|
297
|
+
"connection.requestHint": "外部 MCP 客户端申请访问以下目录及其子目录。同意后会加入允许列表,后续委派仍使用 DSH 的权限设置。",
|
|
298
|
+
"connection.requestClient": "请求客户端",
|
|
299
|
+
"connection.requestPath": "目录",
|
|
300
|
+
"connection.requestReason": "申请理由",
|
|
301
|
+
"connection.requestError": "操作失败,目录可能已变化或设置无法保存。请重试或拒绝。",
|
|
302
|
+
"connection.approveDirectory": "允许访问",
|
|
303
|
+
"connection.rejectDirectory": "拒绝",
|
|
304
|
+
"connection.error.directory-limit": "最多允许选择 32 个目录。",
|
|
305
|
+
"connection.error.picker-unavailable": "无法打开目录选择器,请重新读取后重试。",
|
|
306
|
+
'home.protection': '保护', 'home.extensions': '扩展', 'home.configure': '配置', 'home.managePreset': '配置预设',
|
|
307
|
+
'home.connectedSessions': '正在连接的对话', 'home.historySessions': '历史对话',
|
|
308
|
+
'home.connections': '已连接客户端', 'home.noConnections': '暂无客户端连接', 'home.sessions': '关联会话', 'home.noSessions': '暂无关联会话',
|
|
309
|
+
'home.disconnect': '断开', 'home.openSession': '打开会话', 'home.connected': '已连接', 'home.enabled': '已开启', 'home.disabled': '已关闭',
|
|
310
|
+
'home.mcp': 'MCP 服务', 'home.connectionCount': '{count} / {max} 个连接', 'home.setupConnection': '配置并启用',
|
|
311
|
+
'home.lastSeen': '最近活动', 'home.refresh': '刷新概览', 'home.error': '状态读取或操作失败,请刷新后重试。',
|
|
312
|
+
'connection.maxConnections': '最大连接数', 'connection.connectionsHint': '按 MCP 客户端会话计数。断开或空闲 10 分钟后释放连接;已接收任务继续执行。',
|
|
313
|
+
'connection.addDirectory': '添加目录', 'connection.noDirectories': '暂无目录,请通过文件管理器添加。',
|
|
314
|
+
'connection.workspaceDirectory': 'DSH 工作区', 'connection.customDirectory': '添加的目录', 'connection.directoryUnavailable': '目录不可用',
|
|
315
|
+
'connection.removeDirectory': '移除目录', 'connection.directoryRefresh': '刷新目录',
|
|
316
|
+
'connection.directoryBrowser': '选择目录', 'connection.selectDirectory': '选择此目录', 'connection.parentDirectory': '上一级',
|
|
317
|
+
'connection.emptyDirectory': '没有子目录', 'connection.browserError': '无法读取此目录,请重试或返回上一级。',
|
|
318
|
+
'connection.directoryLimit': '最多允许选择 32 个目录。',
|
|
319
|
+
'taskStatus.starting': '正在启动', 'taskStatus.running': '执行中', 'taskStatus.waiting_confirmation': '等待确认',
|
|
320
|
+
'taskStatus.cancelling': '正在取消', 'taskStatus.finished': '已结束', 'taskStatus.failed': '失败',
|
|
321
|
+
'taskStatus.cancelled': '已取消', 'taskStatus.interrupted': '已中断', 'taskStatus.unavailable': '状态不可用',
|
|
322
|
+
'settings.connectionTab': '连接', 'connection.title': '连接', 'connection.enabled': '启用 MCP 连接',
|
|
323
|
+
'connection.state.disabled': '连接已关闭', 'connection.state.listening': '正在监听', 'connection.state.error': '连接不可用', 'connection.state.loading': '正在读取…',
|
|
324
|
+
'connection.scope': '允许外部客户端委派任务到当前 DSH。仅监听本机,使用当前模型、Super Code 预设和宿主权限。关闭连接不会停止已接收的任务。',
|
|
325
|
+
'connection.autosave': '修改后自动保存', 'connection.saving': '正在保存…',
|
|
326
|
+
'connection.port': '本机端口', 'connection.directories': '允许的工作目录',
|
|
327
|
+
'connection.directoriesHint': '勾选允许的目录,包含其子目录。每次打开设置会同步 DSH 工作区,保留已保存的选择。',
|
|
328
|
+
'connection.save': '保存连接', 'connection.cancel': '取消修改', 'connection.reload': '重新读取', 'connection.saved': '已保存连接设置。',
|
|
329
|
+
'connection.client': '连接 Prompt', 'connection.copyCommand': '复制连接 Prompt', 'connection.showToken': '显示凭证', 'connection.hideToken': '隐藏凭证', 'connection.copyToken': '复制凭证', 'connection.token': '连接凭证', 'connection.rotate': '更换凭证',
|
|
330
|
+
'connection.prompt': '请配置并连接 DSH Super Code MCP 服务:\n地址:{url}\n传输:Streamable HTTP\n认证:Authorization: Bearer <连接凭证>,凭证由我单独提供,请妥善保存在客户端。\n连接后先使用 dsh_sessions 按工作目录查看最近对话、状态和连接额度,由调用方选择传 taskId 复用历史,或传 newConversation: true 新建;不要默认复用最新对话。然后使用 dsh_run 委派任务、dsh_wait 查询状态或找回任务、dsh_send 补充要求、dsh_cancel 取消任务。使用稳定的 requestId 避免重复提交,只使用允许的绝对工作目录;新增目录请用 dsh_request_directory 提交申请,并等待用户在 DSH 中同意。权限确认需在 DSH 中处理;finished 仅表示执行结束,仍需验证结果。',
|
|
331
|
+
'connection.tokenHint': '请将凭证安全保存在客户端。更换凭证后,旧凭证立即失效。',
|
|
332
|
+
'connection.rotated': '已更换凭证,请更新客户端。', 'connection.copied': '已复制。',
|
|
333
|
+
'connection.error.unavailable': '连接服务尚未就绪或当前配置目录不可写,请重新读取。', 'connection.error.invalid': '请检查端口、最大连接数和目录。启用连接至少需要一个有效目录。',
|
|
334
|
+
'connection.error.conflict': '连接设置已在别处修改。草稿已保留,请重新读取。', 'connection.error.save-failed': '保存失败,草稿已保留。', 'connection.error.port-unavailable': '端口被占用或无法监听。请更换端口并保存。', 'connection.error.storage-unavailable': '任务状态保存失败,请检查配置目录。', 'connection.error.clipboard': '复制失败,请手动复制。',
|
|
335
|
+
'settings.contextTab': '上下文配置', 'settings.tabs': 'Super Code 配置',
|
|
260
336
|
'budget.common': '通用预算', 'budget.superCode': 'Super Code 专属预算',
|
|
261
337
|
'budget.applyToOfficialPresets': '同时应用到官方预设', 'budget.officialHint': '开启后,通用预算也用于标准、PTC、极简和创造模式;不包含下方的 Super Code 专属预算。',
|
|
262
338
|
'budget.superCodeHint': '仅用于 Super Code 的任务记录和任务记忆,始终不影响官方预设。',
|
|
263
339
|
'budget.defaultValue': '(默认:{value})', 'budget.off': '关闭', 'budget.managed': '宿主管理',
|
|
264
|
-
'
|
|
340
|
+
'delete.title': '删除保护', 'delete.mode': '保护模式', 'delete.off': '关闭', 'delete.relaxed': '宽松', 'delete.strict': '严格', 'delete.official': '同时应用到官方预设',
|
|
341
|
+
'delete.hint.off': '不添加删除保护提示,不额外拦截命令。宿主原有权限检查仍然有效。',
|
|
342
|
+
'delete.hint.relaxed': '默认使用 trash,脚本中也建议使用;用户明确要求时可以使用 rm,无需插件额外确认。',
|
|
343
|
+
'delete.hint.strict': '默认使用 trash,脚本也禁止直接使用 rm。识别到永久删除命令时,仅清理临时文件或构建产物可经用户逐次二次确认放行。',
|
|
344
|
+
'delete.officialHint': '默认仅保护 Super Code。此开关独立于预算的官方预设开关;保存后立即作用于后续命令。',
|
|
345
|
+
'budget.title': '上下文配置', 'budget.scope': '预算保存后对新建 Agent 生效,运行中的任务保留原配置。',
|
|
265
346
|
'budget.defaults': '留空沿用默认值,括号内显示默认值;通用预算随宿主版本变化。恢复默认会清除这里的覆盖,回到插件加载配置;未配置时跟随宿主。',
|
|
266
347
|
'budget.context': '上下文压缩', 'budget.execution': '批量工具执行', 'budget.advanced': '更多预算',
|
|
267
348
|
'budget.pressureBasis': '压缩预算基准', 'budget.inherit': '跟随默认', 'budget.host': '宿主策略', 'budget.window': '完整上下文窗口',
|
|
@@ -272,8 +353,8 @@ const zh = {
|
|
|
272
353
|
'budget.contextHint': '保留比例需小于触发比例。较大的预算会占用更多上下文。',
|
|
273
354
|
'budget.ptcHint': '包含工具等待时间;默认时限不能超过上限,也不能超过宿主允许值。',
|
|
274
355
|
'budget.ptcUnavailable': '此宿主的 PTC 计时由宿主管理;其他预算仍可配置。', 'budget.ptcCeiling': '宿主允许的最长时限:{seconds} 秒。',
|
|
275
|
-
'budget.save': '
|
|
276
|
-
'budget.saved': '
|
|
356
|
+
'budget.save': '保存设置', 'budget.reset': '恢复默认', 'budget.reload': '重新读取', 'budget.saving': '正在保存…',
|
|
357
|
+
'budget.saved': '已保存。删除保护立即生效,预算对新建 Agent 生效。', 'budget.resetDone': '已恢复默认。删除保护立即生效,预算对新建 Agent 生效。',
|
|
277
358
|
'budget.error.connection': '暂时无法读取预算,请重试。', 'budget.error.invalid': '请检查数值范围、保留比例和执行时限。',
|
|
278
359
|
'budget.error.retain': '保留比例必须小于压缩触发比例;未填写时分别按 16% 和 80% 校验。', 'budget.error.ptc-order': '默认执行时限不能超过执行时限上限。',
|
|
279
360
|
'budget.error.conflict': '预算已在别处修改。草稿已保留,请重新读取后再修改。', 'budget.error.read-only': '当前宿主的设置只读,无法保存。',
|
|
@@ -285,7 +366,7 @@ const zh = {
|
|
|
285
366
|
'memory.deleteTitle': '删除记忆', 'memory.deleteHint': '确定删除“{summary}”吗?删除后无法恢复。',
|
|
286
367
|
'memory.writeError': '操作失败,内容已保留,请重试。', 'memory.conflict': '这条记忆已在别处更改,请取消后刷新再编辑。',
|
|
287
368
|
'memory.missingEdit': '这条记忆已被删除,输入内容已保留。', 'memory.full': '该主题或记忆库已满,请删除不再需要的条目。',
|
|
288
|
-
'workspace.title': 'Super Code', 'workspace.description': 'Agent 执行树与记忆库', 'workspace.resize': '调整执行树与记忆库高度',
|
|
369
|
+
'workspace.title': 'Super Code', 'workspace.description': '任务状态、Agent 执行树与记忆库', 'workspace.resize': '调整执行树与记忆库高度',
|
|
289
370
|
'tree.collapsedLabel': '已收起', 'tree.organizeLabel': '自动整理',
|
|
290
371
|
'detail.toolCall': '工具调用', 'detail.toolResult': '工具结果', 'detail.toolError': '工具失败',
|
|
291
372
|
'detail.cache': '缓存', 'detail.info': '详情', 'tree.countCompact': '{shown}/{total} · 运行 {running}',
|
|
@@ -349,12 +430,93 @@ const zh = {
|
|
|
349
430
|
'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': '[图片]'
|
|
350
431
|
}
|
|
351
432
|
const en = {
|
|
352
|
-
'
|
|
433
|
+
'feedback.title': 'Task status',
|
|
434
|
+
'feedback.refresh': 'Refresh task status',
|
|
435
|
+
'feedback.noGoal': 'No recorded objective',
|
|
436
|
+
'feedback.source.record': 'Recorded objective',
|
|
437
|
+
'feedback.source.request': 'User request',
|
|
438
|
+
'feedback.latest': 'Latest request',
|
|
439
|
+
'feedback.progress': 'Steps {done} / {total}',
|
|
440
|
+
'feedback.progressPartial': 'Recorded steps {done} / {total}',
|
|
441
|
+
'feedback.steps': 'Step progress',
|
|
442
|
+
'feedback.todo.pending': 'Pending',
|
|
443
|
+
'feedback.todo.in_progress': 'In progress',
|
|
444
|
+
'feedback.todo.completed': 'Completed',
|
|
445
|
+
'feedback.activity.running': 'Running',
|
|
446
|
+
'feedback.activity.succeeded': 'Succeeded',
|
|
447
|
+
'feedback.activity.failed': 'Failed',
|
|
448
|
+
'feedback.activity.interrupted': 'Interrupted',
|
|
449
|
+
'feedback.waiting': 'Awaiting approval',
|
|
450
|
+
'feedback.error': 'Execution error',
|
|
451
|
+
'feedback.interrupted': 'Interrupted',
|
|
452
|
+
'feedback.noSteps': 'No recorded steps or tool activity',
|
|
453
|
+
'feedback.loading': 'Loading records…',
|
|
454
|
+
'feedback.recentOnly': 'Recent activity only; open details for the full record.',
|
|
455
|
+
'feedback.open': 'View execution log',
|
|
456
|
+
'feedback.openFileRecord': 'View file operation log',
|
|
457
|
+
'feedback.files': 'File operations',
|
|
458
|
+
'feedback.filesHint': 'Recorded successful operations; terminal changes may be absent.',
|
|
459
|
+
'feedback.filesEmpty': 'No confirmed file operations yet',
|
|
460
|
+
'feedback.filesUnknown': 'File records are incomplete',
|
|
461
|
+
'feedback.file.write': 'Written',
|
|
462
|
+
'feedback.file.edit': 'Edited',
|
|
463
|
+
'feedback.file.create': 'Created',
|
|
464
|
+
'feedback.file.patch': 'Patched',
|
|
465
|
+
'feedback.partial': 'Some Agent or history records are not loaded; showing known facts.',
|
|
466
|
+
'feedback.agentsPartial': 'The task list reached its limit; view other Agents in the execution tree.',
|
|
467
|
+
'status.blocked': 'Blocked',
|
|
468
|
+
|
|
469
|
+
'settings.homeTab': 'Home', 'settings.presetTab': 'Preset configuration', 'settings.protectionTab': 'Deletion protection',
|
|
470
|
+
"connection.requestTitle": "Working directory access request",
|
|
471
|
+
"connection.requestHint": "An external MCP client requests access to this directory and its descendants. Approval adds it to the allowlist. DSH permissions still apply to delegated tasks.",
|
|
472
|
+
"connection.requestClient": "Requesting client",
|
|
473
|
+
"connection.requestPath": "Directory",
|
|
474
|
+
"connection.requestReason": "Reason",
|
|
475
|
+
"connection.requestError": "Could not save this decision. The directory may have changed or settings may be unwritable. Retry or reject.",
|
|
476
|
+
"connection.approveDirectory": "Allow access",
|
|
477
|
+
"connection.rejectDirectory": "Reject",
|
|
478
|
+
"connection.error.directory-limit": "Select up to 32 directories.",
|
|
479
|
+
"connection.error.picker-unavailable": "Could not open the directory picker. Reload and try again.",
|
|
480
|
+
'home.protection': 'Protection', 'home.extensions': 'Extensions', 'home.configure': 'Configure', 'home.managePreset': 'Configure preset',
|
|
481
|
+
'home.connectedSessions': 'Connected conversations', 'home.historySessions': 'Past conversations',
|
|
482
|
+
'home.connections': 'Connected clients', 'home.noConnections': 'No clients connected', 'home.sessions': 'Linked sessions', 'home.noSessions': 'No linked sessions',
|
|
483
|
+
'home.disconnect': 'Disconnect', 'home.openSession': 'Open session', 'home.connected': 'Connected', 'home.enabled': 'On', 'home.disabled': 'Off',
|
|
484
|
+
'home.mcp': 'MCP service', 'home.connectionCount': '{count} / {max} connections', 'home.setupConnection': 'Set up connection',
|
|
485
|
+
'home.lastSeen': 'Last activity', 'home.refresh': 'Refresh overview', 'home.error': 'Could not read or update the status. Refresh and try again.',
|
|
486
|
+
'connection.maxConnections': 'Maximum connections', 'connection.connectionsHint': 'Counts MCP client sessions. Disconnect or 10 minutes of inactivity releases a connection; accepted tasks continue.',
|
|
487
|
+
'connection.addDirectory': 'Add directory', 'connection.noDirectories': 'No directories. Add one with the directory picker.',
|
|
488
|
+
'connection.workspaceDirectory': 'DSH workspace', 'connection.customDirectory': 'Added directory', 'connection.directoryUnavailable': 'Directory unavailable',
|
|
489
|
+
'connection.removeDirectory': 'Remove directory', 'connection.directoryRefresh': 'Refresh directories',
|
|
490
|
+
'connection.directoryBrowser': 'Choose directory', 'connection.selectDirectory': 'Select this directory', 'connection.parentDirectory': 'Parent directory',
|
|
491
|
+
'connection.emptyDirectory': 'No subdirectories', 'connection.browserError': 'Could not read this directory. Retry or return to its parent.',
|
|
492
|
+
'connection.directoryLimit': 'Select up to 32 directories.',
|
|
493
|
+
'taskStatus.starting': 'Starting', 'taskStatus.running': 'Running', 'taskStatus.waiting_confirmation': 'Waiting for confirmation',
|
|
494
|
+
'taskStatus.cancelling': 'Cancelling', 'taskStatus.finished': 'Finished', 'taskStatus.failed': 'Failed',
|
|
495
|
+
'taskStatus.cancelled': 'Cancelled', 'taskStatus.interrupted': 'Interrupted', 'taskStatus.unavailable': 'Unavailable',
|
|
496
|
+
'settings.connectionTab': 'Connection', 'connection.title': 'Connection', 'connection.enabled': 'Enable MCP connection',
|
|
497
|
+
'connection.state.disabled': 'Connection off', 'connection.state.listening': 'Listening', 'connection.state.error': 'Connection unavailable', 'connection.state.loading': 'Loading…',
|
|
498
|
+
'connection.scope': 'Let external clients delegate tasks to this DSH host using its model, Super Code preset and permissions. Listens on localhost only. Disabling the connection does not stop accepted tasks.',
|
|
499
|
+
'connection.autosave': 'Changes save automatically', 'connection.saving': 'Saving…',
|
|
500
|
+
'connection.port': 'Local port', 'connection.directories': 'Allowed working directories',
|
|
501
|
+
'connection.directoriesHint': 'Select allowed directories, including their subdirectories. Reopening settings refreshes DSH workspaces and retains saved selections.',
|
|
502
|
+
'connection.save': 'Save connection', 'connection.cancel': 'Cancel changes', 'connection.reload': 'Reload', 'connection.saved': 'Connection settings saved.',
|
|
503
|
+
'connection.client': 'Connection Prompt', 'connection.copyCommand': 'Copy connection prompt', 'connection.showToken': 'Show token', 'connection.hideToken': 'Hide token', 'connection.copyToken': 'Copy token', 'connection.token': 'Connection token', 'connection.rotate': 'Rotate token',
|
|
504
|
+
'connection.prompt': 'Configure and connect to the DSH Super Code MCP service:\nURL: {url}\nTransport: Streamable HTTP\nAuthentication: Authorization: Bearer <connection token>. I will supply the token separately; store it securely in the client.\nAfter connecting, call dsh_sessions for the working directory to show recent conversations, statuses and connection capacity. Let the caller choose whether to pass taskId to reuse history or newConversation: true to create a new root; never default to the latest conversation. Then use dsh_run to delegate, dsh_wait to observe or recover, dsh_send for follow-ups, and dsh_cancel to cancel. Use stable requestId values to avoid duplicate submissions and only allowed absolute working directories. Request additional access with dsh_request_directory and wait for user approval in DSH. Resolve permissions in DSH. A finished task still requires result verification.',
|
|
505
|
+
'connection.tokenHint': 'Store the token securely in the client. Rotation invalidates the previous token immediately.',
|
|
506
|
+
'connection.rotated': 'Token rotated. Update your client.', 'connection.copied': 'Copied.',
|
|
507
|
+
'connection.error.unavailable': 'The service is not ready or the profile directory is not writable. Reload to retry.', 'connection.error.invalid': 'Check the port, maximum connections and directories. Enabling requires at least one valid directory.',
|
|
508
|
+
'connection.error.conflict': 'Connection settings changed elsewhere. Your draft is retained; reload to continue.', 'connection.error.save-failed': 'Save failed. Your draft is retained.', 'connection.error.port-unavailable': 'The port is occupied or cannot be opened. Save with another port.', 'connection.error.storage-unavailable': 'Task state could not be saved. Check the profile directory.', 'connection.error.clipboard': 'Copy failed. Copy the text manually.',
|
|
509
|
+
'settings.contextTab': 'Context settings', 'settings.tabs': 'Super Code settings',
|
|
353
510
|
'budget.common': 'Common budgets', 'budget.superCode': 'Super Code budgets',
|
|
354
511
|
'budget.applyToOfficialPresets': 'Also apply to official presets', 'budget.officialHint': 'Apply common budgets to Standard, PTC, Minimal and Create. Super Code task and memory limits below remain exclusive to Super Code.',
|
|
355
512
|
'budget.superCodeHint': 'Task records and task memory for Super Code. These limits never affect official presets.',
|
|
356
513
|
'budget.defaultValue': '(default: {value})', 'budget.off': 'off', 'budget.managed': 'host-managed',
|
|
357
|
-
'
|
|
514
|
+
'delete.title': 'Deletion protection', 'delete.mode': 'Protection mode', 'delete.off': 'Off', 'delete.relaxed': 'Relaxed', 'delete.strict': 'Strict', 'delete.official': 'Also apply to official presets',
|
|
515
|
+
'delete.hint.off': 'Adds no deletion instructions or command interception. Host permission checks still apply.',
|
|
516
|
+
'delete.hint.relaxed': 'Prefer trash, including in scripts. Explicit user requests may use rm without extra plugin confirmation.',
|
|
517
|
+
'delete.hint.strict': 'Prefer trash; scripts must not directly use rm. Recognized permanent deletion requires separate approval for each temporary-file or build-artifact cleanup.',
|
|
518
|
+
'delete.officialHint': 'Protects Super Code by default. This switch is independent of official-preset budgets; saved changes apply to subsequent commands immediately.',
|
|
519
|
+
'budget.title': 'Context settings', 'budget.scope': 'Budget changes apply to new Agents. Running tasks keep their current settings.',
|
|
358
520
|
'budget.defaults': 'Leave fields blank to inherit defaults. Parentheses show defaults; common budgets follow the host version. Reset clears overrides and restores the plugin configuration, or host defaults when unconfigured.',
|
|
359
521
|
'budget.context': 'Context compression', 'budget.execution': 'Batched tool execution', 'budget.advanced': 'More budgets',
|
|
360
522
|
'budget.pressureBasis': 'Compression budget basis', 'budget.inherit': 'Inherit default', 'budget.host': 'Host policy', 'budget.window': 'Full context window',
|
|
@@ -365,8 +527,8 @@ const en = {
|
|
|
365
527
|
'budget.contextHint': 'Retention must be below the compression threshold. Larger budgets use more context.',
|
|
366
528
|
'budget.ptcHint': 'Includes tool waits. The default deadline must fit within both the configured maximum and the host ceiling.',
|
|
367
529
|
'budget.ptcUnavailable': 'This host manages PTC timing. Other budgets remain configurable.', 'budget.ptcCeiling': 'Host deadline ceiling: {seconds} seconds.',
|
|
368
|
-
'budget.save': 'Save
|
|
369
|
-
'budget.saved': 'Saved.
|
|
530
|
+
'budget.save': 'Save settings', 'budget.reset': 'Reset to defaults', 'budget.reload': 'Reload settings', 'budget.saving': 'Saving…',
|
|
531
|
+
'budget.saved': 'Saved. Deletion protection applies immediately; budgets apply to new Agents.', 'budget.resetDone': 'Defaults restored. Deletion protection applies immediately; budgets apply to new Agents.',
|
|
370
532
|
'budget.error.connection': 'Budgets could not be loaded. Try again.', 'budget.error.invalid': 'Check the ranges, retention ratio, and deadlines.',
|
|
371
533
|
'budget.error.retain': 'Retention must be below the compression threshold. Blank values are checked as 16% and 80%, respectively.', 'budget.error.ptc-order': 'The default deadline must not exceed the maximum deadline.',
|
|
372
534
|
'budget.error.conflict': 'Budgets changed elsewhere. Your draft is retained; reload before editing again.', 'budget.error.read-only': 'This host has read-only settings.',
|
|
@@ -378,7 +540,7 @@ const en = {
|
|
|
378
540
|
'memory.deleteTitle': 'Delete memory', 'memory.deleteHint': 'Delete “{summary}”? This cannot be undone.',
|
|
379
541
|
'memory.writeError': 'The operation failed. Your input is retained; try again.', 'memory.conflict': 'This memory changed elsewhere. Cancel and refresh before editing again.',
|
|
380
542
|
'memory.missingEdit': 'This memory was deleted. Your input is retained.', 'memory.full': 'This topic or library is full. Remove entries you no longer need.',
|
|
381
|
-
'workspace.title': 'Super Code', 'workspace.description': 'Agent tree and memory library', 'workspace.resize': 'Resize agent tree and memory library',
|
|
543
|
+
'workspace.title': 'Super Code', 'workspace.description': 'Task status, Agent tree and memory library', 'workspace.resize': 'Resize agent tree and memory library',
|
|
382
544
|
'tree.collapsedLabel': 'Collapsed', 'tree.organizeLabel': 'Organize',
|
|
383
545
|
'detail.toolCall': 'Tool call', 'detail.toolResult': 'Tool result', 'detail.toolError': 'Tool failed',
|
|
384
546
|
'detail.cache': 'Cache', 'detail.info': 'Details', 'tree.countCompact': '{shown}/{total} · Running {running}',
|
|
@@ -763,12 +925,299 @@ function AgentTree({ useSessions, useSessionStatus, sessionId, useTabInfo, treeS
|
|
|
763
925
|
return React.createElement(AgentTreeBody, { key: view.root, state, view, saved, openDetail, refresh, watchCatalog, onToggle, t })
|
|
764
926
|
}
|
|
765
927
|
|
|
928
|
+
/** Join only the selected conversation's known descendants; never infer a plan. */
|
|
929
|
+
function taskFeedbackRows(view) {
|
|
930
|
+
const { rows, more } = visibleAgentRows(view, () => true, view.nodes.size)
|
|
931
|
+
const tasks = rows.map(row => {
|
|
932
|
+
const node = view.nodes.get(row.id), projections = node.projectionValues || {}, facts = projections.superCodeFeedback
|
|
933
|
+
const current = projections.superCodeTasks?.current
|
|
934
|
+
const recorded = current?.kind === 'task' ? current : undefined
|
|
935
|
+
const goal = recorded?.goal || (facts?.goal?.kind === 'recorded' ? facts.goal.text : '')
|
|
936
|
+
const title = recorded?.title || goal || facts?.request || ''
|
|
937
|
+
const todos = facts?.todos || projections.todos || []
|
|
938
|
+
return { ...row, node, facts, title, goal, source: recorded || facts?.goal?.kind === 'recorded' ? 'record' : facts?.request ? 'request' : 'none',
|
|
939
|
+
owner: node.title || node.displayTitle || '', todos, done: todos.filter(item => item.status === 'completed').length,
|
|
940
|
+
total: todos.length, complete: !facts?.omittedTodos, taskStatus: recorded?.status || (facts?.goal?.kind === 'recorded' ? facts.goal.status : '') }
|
|
941
|
+
})
|
|
942
|
+
const files = new Map()
|
|
943
|
+
// File identity remains independent of the virtual viewport.
|
|
944
|
+
for (const node of view.nodes.values()) for (const file of node.projectionValues?.superCodeFeedback?.files || []) {
|
|
945
|
+
const row = files.get(file.path) || { path: file.path, operations: new Set(), sources: [] }
|
|
946
|
+
row.operations.add(file.operation); row.sources.push({ node, seq: file.seq, tool: file.tool })
|
|
947
|
+
files.set(file.path, row)
|
|
948
|
+
}
|
|
949
|
+
return { tasks, more, files: [...files.values()].sort((a, b) => a.path.localeCompare(b.path)).map(file => ({ ...file, ...feedbackFileLabel(file) })),
|
|
950
|
+
partial: [...view.nodes.values()].some(node => !node.projectionValues?.superCodeFeedback || node.projectionValues.superCodeFeedback.omittedFiles) }
|
|
951
|
+
}
|
|
952
|
+
/** Presentation only: retain the full path while shortening workspace-owned labels. */
|
|
953
|
+
function feedbackFileLabel(file) {
|
|
954
|
+
const path = file.path.replace(/\\/g, '/')
|
|
955
|
+
const cwd = file.sources.map(source => source.node.cwd || source.node.projectionValues?.workingDirectory || '')
|
|
956
|
+
.find(value => value && path.startsWith(value.replace(/\\/g, '/').replace(/\/$/, '') + '/'))
|
|
957
|
+
const relative = cwd ? path.slice(cwd.replace(/\\/g, '/').replace(/\/$/, '').length + 1) : path
|
|
958
|
+
const slash = relative.lastIndexOf('/')
|
|
959
|
+
return { name: relative.slice(slash + 1) || relative, directory: slash < 0 ? '' : relative.slice(0, slash) }
|
|
960
|
+
}
|
|
961
|
+
function FeedbackMark({ tone, label }) {
|
|
962
|
+
return React.createElement('span', { className: 'dsh-super-code-feedback-mark ' + tone, role: 'img', 'aria-label': label },
|
|
963
|
+
tone === 'success' && React.createElement('svg', { viewBox: '0 0 16 16', fill: 'none', stroke: 'currentColor', strokeWidth: 1.6, 'aria-hidden': true },
|
|
964
|
+
React.createElement('path', { d: 'm3.5 8 3 3 6-6', strokeLinecap: 'round', strokeLinejoin: 'round' })),
|
|
965
|
+
tone === 'error' && React.createElement('span', { 'aria-hidden': true }, '!'))
|
|
966
|
+
}
|
|
967
|
+
function FeedbackActivity({ activity, t }) {
|
|
968
|
+
const tone = { running: 'active', failed: 'error', succeeded: 'success', interrupted: 'idle' }[activity.status] || 'idle'
|
|
969
|
+
return React.createElement('div', { className: 'dsh-super-code-feedback-activity', 'data-activity-state': activity.status },
|
|
970
|
+
React.createElement(FeedbackMark, { tone, label: text(t, 'feedback.activity.' + activity.status) }),
|
|
971
|
+
React.createElement('span', { className: 'dsh-super-code-feedback-activity-text', title: activity.description || activity.tool }, activity.description || activity.tool),
|
|
972
|
+
activity.description && React.createElement('small', { className: 'dsh-super-code-feedback-tool', title: activity.tool }, activity.tool),
|
|
973
|
+
['failed', 'interrupted'].includes(activity.status) && React.createElement('small', null, text(t, 'feedback.activity.' + activity.status)))
|
|
974
|
+
}
|
|
975
|
+
/** Flatten presentation rows only; no task, step, or loaded activity is folded. */
|
|
976
|
+
function feedbackListItems(model, root, partial) {
|
|
977
|
+
const items = []
|
|
978
|
+
const add = (kind, key, data = {}, row) => items.push({ kind, key: JSON.stringify(key), row, ...data,
|
|
979
|
+
signature: JSON.stringify([kind, data.content, data.todo, data.activity, data.label, data.count,
|
|
980
|
+
data.file && [data.file.name, data.file.directory, [...data.file.operations]], kind === 'task' &&
|
|
981
|
+
[row.title, row.owner, row.done, row.total, row.complete, row.taskStatus, row.facts?.phase, row.node.running]]) })
|
|
982
|
+
for (const row of model.tasks) {
|
|
983
|
+
const facts = row.facts
|
|
984
|
+
add('task', [row.id, 'task'], {}, row)
|
|
985
|
+
if (row.goal && row.goal !== row.title) add('text', [row.id, 'goal'], { content: row.goal }, row)
|
|
986
|
+
if (facts?.latestRequest && facts.latestRequest !== facts.request) add('request', [row.id, 'request'], { content: facts.latestRequest }, row)
|
|
987
|
+
row.todos.forEach((todo, index) => add('step', [row.id, 'step', index], { todo }, row))
|
|
988
|
+
for (const activity of facts?.activities || []) add('activity', [row.id, 'activity', activity.callId, activity.seq], { activity }, row)
|
|
989
|
+
if (!row.total && !facts?.activities?.length) add('note', [row.id, 'empty'], { label: facts ? 'feedback.noSteps' : 'feedback.loading' }, row)
|
|
990
|
+
if (facts?.omittedActivities) add('note', [row.id, 'recent'], { label: 'feedback.recentOnly' }, row)
|
|
991
|
+
add('open', [row.id, 'open'], { focusable: !row.node.unavailable }, row)
|
|
992
|
+
}
|
|
993
|
+
if (root) {
|
|
994
|
+
add('files', ['files'], { count: model.files.length })
|
|
995
|
+
add('note', ['filesHint'], { label: 'feedback.filesHint' })
|
|
996
|
+
if (!model.files.length) add('note', ['filesEmpty'], { label: partial ? 'feedback.filesUnknown' : 'feedback.filesEmpty' })
|
|
997
|
+
for (const file of model.files) add('file', ['file', file.path], { file, focusable: !file.sources[0].node.unavailable })
|
|
998
|
+
}
|
|
999
|
+
if (partial) add('note', ['partial'], { label: 'feedback.partial' })
|
|
1000
|
+
return items
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
/** Prefix heights allow binary-search windows without mounting offscreen rows. */
|
|
1004
|
+
function feedbackListLayout(items, heights = new Map()) {
|
|
1005
|
+
const estimates = { task: 64, text: 48, request: 70, step: 28, activity: 30, open: 34, files: 40, file: 30, note: 26 }
|
|
1006
|
+
const offsets = [0], byKey = new Map()
|
|
1007
|
+
items.forEach((item, index) => {
|
|
1008
|
+
const measured = heights.get(item.key)
|
|
1009
|
+
const height = measured?.signature === item.signature ? measured.height : estimates[item.kind]
|
|
1010
|
+
offsets.push(offsets[index] + Math.max(1, height)); byKey.set(item.key, index)
|
|
1011
|
+
})
|
|
1012
|
+
return { offsets, byKey, height: offsets.at(-1) }
|
|
1013
|
+
}
|
|
1014
|
+
function feedbackListIndex(layout, top) {
|
|
1015
|
+
let low = 0, high = layout.offsets.length - 1
|
|
1016
|
+
while (low < high) {
|
|
1017
|
+
const middle = Math.floor((low + high) / 2)
|
|
1018
|
+
if (layout.offsets[middle + 1] <= top) low = middle + 1; else high = middle
|
|
1019
|
+
}
|
|
1020
|
+
return Math.min(low, Math.max(0, layout.offsets.length - 2))
|
|
1021
|
+
}
|
|
1022
|
+
function feedbackListWindow(layout, scrollTop, height) {
|
|
1023
|
+
const top = Math.max(0, Math.min(scrollTop, Math.max(0, layout.height - height)))
|
|
1024
|
+
return { top, start: feedbackListIndex(layout, Math.max(0, top - 240)),
|
|
1025
|
+
end: layout.height ? Math.min(layout.offsets.length - 1, feedbackListIndex(layout, top + height + 240) + 1) : 0 }
|
|
1026
|
+
}
|
|
1027
|
+
/** Keep the same visible fact in place when earlier rows resize or arrive. */
|
|
1028
|
+
function feedbackListAnchor(previous, next, scrollTop, height) {
|
|
1029
|
+
if (!previous || !previous.layout.height) return Math.max(0, Math.min(scrollTop, Math.max(0, next.layout.height - height)))
|
|
1030
|
+
const index = feedbackListIndex(previous.layout, scrollTop), key = previous.items[index]?.key
|
|
1031
|
+
const nextIndex = next.layout.byKey.get(key)
|
|
1032
|
+
const top = nextIndex === undefined ? scrollTop : next.layout.offsets[nextIndex] + Math.min(
|
|
1033
|
+
Math.max(0, scrollTop - previous.layout.offsets[index]), next.layout.offsets[nextIndex + 1] - next.layout.offsets[nextIndex] - 1)
|
|
1034
|
+
return Math.max(0, Math.min(top, Math.max(0, next.layout.height - height)))
|
|
1035
|
+
}
|
|
1036
|
+
function FeedbackRowContent({ item, root, openDetail, t }) {
|
|
1037
|
+
const row = item.row
|
|
1038
|
+
if (item.kind === 'task') {
|
|
1039
|
+
const status = feedbackStatus({ ...row, root }, t)
|
|
1040
|
+
const progressLabel = text(t, row.complete ? 'feedback.progress' : 'feedback.progressPartial', { done: row.done, total: row.total })
|
|
1041
|
+
return React.createElement('div', { className: 'dsh-super-code-feedback-task-heading' },
|
|
1042
|
+
React.createElement('div', { className: 'dsh-super-code-feedback-heading' },
|
|
1043
|
+
React.createElement('h3', { title: row.source === 'none' ? '' : text(t, 'feedback.source.' + row.source) }, row.title || text(t, 'feedback.noGoal')),
|
|
1044
|
+
React.createElement('div', { className: 'dsh-super-code-feedback-meta' },
|
|
1045
|
+
React.createElement('small', null, row.owner || (row.id === root ? text(t, 'main.agent') : row.id)),
|
|
1046
|
+
React.createElement('span', { className: 'dsh-super-code-feedback-status', 'data-tone': status.tone }, status.label))),
|
|
1047
|
+
row.total > 0 && React.createElement('span', { className: 'dsh-super-code-feedback-count', 'aria-label': progressLabel, title: progressLabel }, `${row.done}/${row.total}${row.complete ? '' : '+'}`),
|
|
1048
|
+
row.total > 0 && row.complete && React.createElement('progress', { max: row.total, value: row.done, 'aria-label': text(t, 'feedback.steps') }))
|
|
1049
|
+
}
|
|
1050
|
+
if (item.kind === 'step') return React.createElement('div', { className: 'dsh-super-code-feedback-step', 'data-step-state': item.todo.status },
|
|
1051
|
+
React.createElement(FeedbackMark, { tone: item.todo.status === 'completed' ? 'success' : item.todo.status === 'in_progress' ? 'active' : 'pending', label: text(t, 'feedback.todo.' + item.todo.status) }),
|
|
1052
|
+
React.createElement('span', null, item.todo.content))
|
|
1053
|
+
if (item.kind === 'activity') return React.createElement(FeedbackActivity, { activity: item.activity, t })
|
|
1054
|
+
if (item.kind === 'open') return React.createElement(ActionButton, { type: 'button', className: 'dsh-super-code-feedback-open', disabled: !item.focusable,
|
|
1055
|
+
title: text(t, 'feedback.open'), onClick: () => openDetail(row.node) }, text(t, 'feedback.open'), React.createElement(Glyph, { type: 'arrow' }))
|
|
1056
|
+
if (item.kind === 'files') return React.createElement('h3', { className: 'dsh-super-code-feedback-files-heading' }, text(t, 'feedback.files'), React.createElement('span', null, item.count))
|
|
1057
|
+
if (item.kind === 'file') {
|
|
1058
|
+
const file = item.file
|
|
1059
|
+
return React.createElement(ActionButton, { type: 'button', className: 'dsh-super-code-feedback-file', title: file.path,
|
|
1060
|
+
'aria-label': text(t, 'feedback.openFileRecord') + ' ' + file.path, disabled: !item.focusable, onClick: () => openDetail(file.sources[0].node) },
|
|
1061
|
+
React.createElement(Glyph, { type: 'code' }), React.createElement('strong', null, file.name),
|
|
1062
|
+
file.directory && React.createElement('small', null, file.directory),
|
|
1063
|
+
React.createElement('span', { className: 'dsh-super-code-feedback-file-operation' }, [...file.operations].map(operation => text(t, 'feedback.file.' + operation)).join(' · ')))
|
|
1064
|
+
}
|
|
1065
|
+
if (item.kind === 'note') return React.createElement('p', { className: 'dsh-super-code-feedback-muted', role: item.label === 'tree.loadError' ? 'alert' : undefined }, text(t, item.label))
|
|
1066
|
+
return React.createElement('p', { className: 'dsh-super-code-feedback-text' },
|
|
1067
|
+
item.kind === 'request' && React.createElement('small', null, text(t, 'feedback.latest')), item.content)
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
/** One measured, variable-height viewport for tasks, steps, activity and files. */
|
|
1071
|
+
function FeedbackVirtualList({ items, root, saved, openDetail, t, watchCatalog }) {
|
|
1072
|
+
const viewport = useRef(null), list = useRef(null), previous = useRef(null), pendingFocus = useRef('')
|
|
1073
|
+
const language = localeCode(t)
|
|
1074
|
+
const [scroll, setScroll] = useState({ top: saved.feedbackTop || 0, height: 640, width: saved.feedbackWidth || 0 })
|
|
1075
|
+
const measurements = useRef(saved.feedbackHeights || new Map())
|
|
1076
|
+
const [heightRevision, setHeightRevision] = useState(0)
|
|
1077
|
+
const [focused, setFocused] = useState('')
|
|
1078
|
+
const layout = useMemo(() => feedbackListLayout(items, measurements.current), [items, heightRevision])
|
|
1079
|
+
const range = feedbackListWindow(layout, scroll.top, scroll.height)
|
|
1080
|
+
const indices = Array.from({ length: range.end - range.start }, (_, index) => range.start + index)
|
|
1081
|
+
const focusIndex = layout.byKey.get(focused)
|
|
1082
|
+
if (focusIndex !== undefined && !indices.includes(focusIndex)) indices.push(focusIndex)
|
|
1083
|
+
indices.sort((a, b) => a - b)
|
|
1084
|
+
const renderKey = indices.join(',')
|
|
1085
|
+
const watched = new Set(root ? [root] : [])
|
|
1086
|
+
for (const index of indices) if (items[index].row) watched.add(items[index].row.id)
|
|
1087
|
+
useLayoutEffect(() => {
|
|
1088
|
+
const element = viewport.current
|
|
1089
|
+
const measure = () => {
|
|
1090
|
+
const width = element.clientWidth
|
|
1091
|
+
if (width !== saved.feedbackWidth || language !== saved.feedbackLanguage) {
|
|
1092
|
+
saved.feedbackWidth = width; saved.feedbackLanguage = language
|
|
1093
|
+
saved.feedbackHeights = new Map(); measurements.current = saved.feedbackHeights; setHeightRevision(value => value + 1)
|
|
1094
|
+
}
|
|
1095
|
+
setScroll(value => value.top === element.scrollTop && value.height === element.clientHeight && value.width === width
|
|
1096
|
+
? value : { top: element.scrollTop, height: element.clientHeight, width })
|
|
1097
|
+
}
|
|
1098
|
+
element.scrollTop = saved.feedbackTop || 0; measure()
|
|
1099
|
+
const observer = new ResizeObserver(measure); observer.observe(element)
|
|
1100
|
+
return () => observer.disconnect()
|
|
1101
|
+
}, [saved, language])
|
|
1102
|
+
useLayoutEffect(() => {
|
|
1103
|
+
const element = viewport.current
|
|
1104
|
+
const top = feedbackListAnchor(previous.current, { items, layout }, previous.current?.top ?? saved.feedbackTop ?? 0, element.clientHeight)
|
|
1105
|
+
element.scrollTop = top; saved.feedbackTop = element.scrollTop
|
|
1106
|
+
previous.current = { items, layout, top: element.scrollTop }
|
|
1107
|
+
setScroll(value => value.top === element.scrollTop ? value : { ...value, top: element.scrollTop })
|
|
1108
|
+
// Retain only current identities; long-running sessions must not leak measurements.
|
|
1109
|
+
for (const key of measurements.current.keys()) if (!layout.byKey.has(key)) measurements.current.delete(key)
|
|
1110
|
+
saved.feedbackHeights = measurements.current
|
|
1111
|
+
}, [items, layout, saved])
|
|
1112
|
+
useLayoutEffect(() => {
|
|
1113
|
+
const rows = [...list.current.children]
|
|
1114
|
+
let active = true
|
|
1115
|
+
const measure = () => {
|
|
1116
|
+
if (!active) return
|
|
1117
|
+
const current = measurements.current
|
|
1118
|
+
let next = current
|
|
1119
|
+
for (const element of rows) {
|
|
1120
|
+
const key = element.dataset.feedbackKey, item = items[layout.byKey.get(key)], height = element.getBoundingClientRect().height
|
|
1121
|
+
if (!item || height <= 0 || current.get(key)?.signature === item.signature && Math.abs(current.get(key).height - height) < .5) continue
|
|
1122
|
+
if (next === current) next = new Map(current)
|
|
1123
|
+
next.set(key, { signature: item.signature, height })
|
|
1124
|
+
}
|
|
1125
|
+
if (next === current) return
|
|
1126
|
+
measurements.current = next; saved.feedbackHeights = next
|
|
1127
|
+
setHeightRevision(value => value + 1)
|
|
1128
|
+
}
|
|
1129
|
+
measure()
|
|
1130
|
+
const observer = new ResizeObserver(measure); rows.forEach(element => observer.observe(element))
|
|
1131
|
+
return () => { active = false; observer.disconnect() }
|
|
1132
|
+
}, [items, renderKey, scroll.width, language, saved])
|
|
1133
|
+
useLayoutEffect(() => {
|
|
1134
|
+
if (!pendingFocus.current) return
|
|
1135
|
+
const element = [...list.current.children].find(row => row.dataset.feedbackKey === pendingFocus.current)
|
|
1136
|
+
const button = element?.querySelector('button')
|
|
1137
|
+
if (button) { button.focus({ preventScroll: true }); pendingFocus.current = '' }
|
|
1138
|
+
}, [renderKey, items, layout, focused])
|
|
1139
|
+
const tab = event => {
|
|
1140
|
+
if (event.key !== 'Tab') return
|
|
1141
|
+
const current = layout.byKey.get(focused)
|
|
1142
|
+
if (current === undefined) return
|
|
1143
|
+
const direction = event.shiftKey ? -1 : 1
|
|
1144
|
+
for (let index = current + direction; index >= 0 && index < items.length; index += direction) {
|
|
1145
|
+
if (!items[index].focusable) continue
|
|
1146
|
+
event.preventDefault(); pendingFocus.current = items[index].key; setFocused(items[index].key)
|
|
1147
|
+
const top = Math.max(0, Math.min(layout.offsets[index], Math.max(0, layout.height - viewport.current.clientHeight)))
|
|
1148
|
+
viewport.current.scrollTop = top; saved.feedbackTop = top
|
|
1149
|
+
if (previous.current) previous.current.top = top
|
|
1150
|
+
setScroll(value => ({ ...value, top })); return
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
return React.createElement('div', { ref: viewport, className: 'dsh-super-code-feedback-scroll', tabIndex: 0, 'aria-label': text(t, 'feedback.title'), onKeyDown: tab,
|
|
1154
|
+
onScroll: event => {
|
|
1155
|
+
const top = event.currentTarget.scrollTop; saved.feedbackTop = top
|
|
1156
|
+
if (previous.current) previous.current.top = top
|
|
1157
|
+
setScroll(value => value.top === top ? value : { ...value, top })
|
|
1158
|
+
}, onFocusCapture: event => setFocused(event.target.closest('[data-feedback-key]')?.dataset.feedbackKey || ''),
|
|
1159
|
+
onBlurCapture: event => { if (!event.currentTarget.contains(event.relatedTarget)) setFocused('') } },
|
|
1160
|
+
[...watched].map(id => React.createElement(AgentCatalogWatch, { key: id, id, watchCatalog })),
|
|
1161
|
+
React.createElement('ol', { ref: list, className: 'dsh-super-code-feedback-list', 'aria-label': text(t, 'feedback.title'), style: { height: layout.height }, 'data-feedback-total': items.length },
|
|
1162
|
+
indices.map(index => {
|
|
1163
|
+
const item = items[index], row = item.row, header = item.kind === 'task'
|
|
1164
|
+
const indent = row ? Math.min(Math.max(0, row.level - 1), 4) * 16 + (header ? 0 : 14) : 0
|
|
1165
|
+
return React.createElement('li', { key: item.key, 'data-feedback-key': item.key, 'data-feedback-kind': item.kind,
|
|
1166
|
+
'data-feedback-agent': row?.id, 'data-feedback-depth': row?.level, 'data-feedback-live': row ? row.node.running === true || row.facts?.phase === 'running' : undefined,
|
|
1167
|
+
'aria-posinset': index + 1, 'aria-setsize': items.length,
|
|
1168
|
+
style: { transform: `translateY(${layout.offsets[index]}px)`, paddingLeft: indent, '--sc-feedback-indent': indent + 'px' } }, React.createElement(FeedbackRowContent, { item, root, openDetail, t }))
|
|
1169
|
+
})))
|
|
1170
|
+
}
|
|
1171
|
+
function feedbackStatus(row, t = key => zh[key] || key) {
|
|
1172
|
+
if (row.node.unavailable) return { tone: 'unavailable', label: text(t, 'tree.legend.unavailable') }
|
|
1173
|
+
const phase = row.facts?.phase
|
|
1174
|
+
if (row.node.running === true) return { tone: 'active', label: text(t, phase === 'waiting' ? 'feedback.waiting' : 'tree.legend.active') }
|
|
1175
|
+
if (phase === 'error' || phase === 'interrupted') return { tone: 'error', label: text(t, `feedback.${phase}`) }
|
|
1176
|
+
if (['completed', 'cancelled', 'paused', 'blocked'].includes(row.taskStatus)) return { tone: row.taskStatus === 'completed' ? 'success' : 'idle', label: text(t, `status.${row.taskStatus}`) }
|
|
1177
|
+
// Host inactivity or a completed turn does not establish task completion.
|
|
1178
|
+
return agentExecutionStatus(row.node, row.id === row.root, t)
|
|
1179
|
+
}
|
|
1180
|
+
function TaskFeedback({ useSessions, useSessionStatus, sessionId, useTabInfo, treeStates, openDetail, refresh, watchCatalog, onToggle, t }) {
|
|
1181
|
+
const catalog = useSessions(s => s), statuses = useSessionStatus ? useSessionStatus(s => s) : undefined
|
|
1182
|
+
const state = useMemo(() => agentSessionState(catalog, sessionId, statuses), [catalog, sessionId, statuses])
|
|
1183
|
+
const view = useMemo(() => buildAgentView(state), [state.byId, state.subagentsByParent, state.current, state.currentAddress])
|
|
1184
|
+
const { tab } = useTabInfo()
|
|
1185
|
+
const saved = useMemo(() => {
|
|
1186
|
+
let value = treeStates.get(tab.signal)
|
|
1187
|
+
if (!value || value.root !== view.root) { value = { root: view.root, limit: 200, focused: state.current, expanded: new Set(), collapsed: new Set() }; treeStates.set(tab.signal, value) }
|
|
1188
|
+
return value
|
|
1189
|
+
}, [treeStates, tab.signal, view.root])
|
|
1190
|
+
const [busy, setBusy] = useState(false), [error, setError] = useState(false)
|
|
1191
|
+
const model = useMemo(() => taskFeedbackRows(view), [view])
|
|
1192
|
+
const partial = model.partial || Object.values(state.subagentsByParent || {}).some(value => value.state === 'error')
|
|
1193
|
+
const items = useMemo(() => {
|
|
1194
|
+
const rows = feedbackListItems(model, view.root, partial)
|
|
1195
|
+
if (!view.root) rows.unshift({ kind: 'note', key: 'empty', label: 'tree.empty', signature: 'empty' })
|
|
1196
|
+
if (error) rows.unshift({ kind: 'note', key: 'error', label: 'tree.loadError', signature: 'error' })
|
|
1197
|
+
return rows
|
|
1198
|
+
}, [model, view.root, partial, error])
|
|
1199
|
+
const reload = async () => {
|
|
1200
|
+
setBusy(true); setError(false)
|
|
1201
|
+
try {
|
|
1202
|
+
const ids = [...view.nodes.keys()]
|
|
1203
|
+
for (let start = 0; start < ids.length; start += 20) await Promise.all(ids.slice(start, start + 20).map(id => refresh(id)))
|
|
1204
|
+
}
|
|
1205
|
+
catch { setError(true) }
|
|
1206
|
+
finally { setBusy(false) }
|
|
1207
|
+
}
|
|
1208
|
+
return React.createElement('section', { className: 'dsh-super-code-feedback', 'data-task-feedback': true },
|
|
1209
|
+
React.createElement('header', null,
|
|
1210
|
+
React.createElement(WorkspaceSectionToggle, { section: 'tasks', expanded: true, onToggle, t }),
|
|
1211
|
+
React.createElement(ActionButton, { type: 'button', className: 'dsh-super-code-icon-button', title: text(t, 'feedback.refresh'), 'aria-label': text(t, 'feedback.refresh'), disabled: busy || !view.root, onClick: () => void reload() }, React.createElement(IconRefreshOutline16))),
|
|
1212
|
+
React.createElement(FeedbackVirtualList, { key: view.root, items, root: view.root, saved, openDetail, t, watchCatalog }))
|
|
1213
|
+
}
|
|
1214
|
+
|
|
766
1215
|
function WorkspaceSectionToggle({ section, expanded, onToggle, children, t }) {
|
|
767
|
-
const label = text(t, section === 'tree' ? 'tree.title' : 'memory.title')
|
|
1216
|
+
const label = text(t, section === 'tasks' ? 'feedback.title' : section === 'tree' ? 'tree.title' : 'memory.title')
|
|
768
1217
|
return React.createElement(ActionButton, { type: 'button', className: 'dsh-super-code-section-toggle',
|
|
769
1218
|
'data-workspace-section': section, 'aria-label': label, 'aria-expanded': expanded, onClick: onToggle },
|
|
770
1219
|
React.createElement(Glyph, { type: 'chevron' }),
|
|
771
|
-
React.createElement(Glyph, { type: section === 'tree' ? 'branch' : 'memory' }),
|
|
1220
|
+
React.createElement(Glyph, { type: section === 'tasks' ? 'context' : section === 'tree' ? 'branch' : 'memory' }),
|
|
772
1221
|
React.createElement('strong', null, label), children)
|
|
773
1222
|
}
|
|
774
1223
|
|
|
@@ -779,7 +1228,7 @@ function SuperCodeWorkspace(props) {
|
|
|
779
1228
|
const navigationKey = `${address}/${tab.navigation?.revision || 0}`
|
|
780
1229
|
const memoryTarget = memoryLibraryTarget(address, '')
|
|
781
1230
|
const saved = useMemo(() => {
|
|
782
|
-
const value = props.workspaceStates.get(tab.signal) || { active: '
|
|
1231
|
+
const value = props.workspaceStates.get(tab.signal) || { active: 'tasks' }
|
|
783
1232
|
if (value.navigationKey !== navigationKey) {
|
|
784
1233
|
value.navigationKey = navigationKey
|
|
785
1234
|
if (memoryTarget.id) value.active = 'memory'
|
|
@@ -787,10 +1236,10 @@ function SuperCodeWorkspace(props) {
|
|
|
787
1236
|
props.workspaceStates.set(tab.signal, value)
|
|
788
1237
|
return value
|
|
789
1238
|
}, [props.workspaceStates, tab.signal])
|
|
790
|
-
const [active, setActive] = useState(
|
|
1239
|
+
const [active, setActive] = useState(['tasks', 'tree', 'memory'].includes(saved.active) ? saved.active : 'tasks')
|
|
791
1240
|
const layout = useRef(null), focusSection = useRef('')
|
|
792
|
-
const toggle =
|
|
793
|
-
const next = active === '
|
|
1241
|
+
const toggle = section => {
|
|
1242
|
+
const next = active === section ? section === 'tasks' ? 'tree' : 'tasks' : section
|
|
794
1243
|
saved.active = next; focusSection.current = next; setActive(next)
|
|
795
1244
|
}
|
|
796
1245
|
useLayoutEffect(() => {
|
|
@@ -805,14 +1254,13 @@ function SuperCodeWorkspace(props) {
|
|
|
805
1254
|
}, [active])
|
|
806
1255
|
return React.createElement('section', { className: 'dsh-super-code-workspace', 'aria-label': text(props.t, 'workspace.title') },
|
|
807
1256
|
React.createElement('div', { ref: layout, className: 'dsh-super-code-workspace-layout', 'data-active-section': active, style: {
|
|
808
|
-
gridTemplateRows:
|
|
809
|
-
} },
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
React.createElement(WorkspaceSectionToggle, { section: 'memory', expanded: false, onToggle: toggle, t: props.t }))))
|
|
1257
|
+
gridTemplateRows: ['tasks', 'tree', 'memory'].map(section => active === section ? 'minmax(280px, 1fr)' : '44px').join(' '),
|
|
1258
|
+
} }, ['tasks', 'tree', 'memory'].map(section => {
|
|
1259
|
+
const onToggle = () => toggle(section)
|
|
1260
|
+
return active === section ? React.createElement(section === 'tasks' ? TaskFeedback : section === 'tree' ? AgentTree : MemoryLibrary, { ...props, key: section, onToggle })
|
|
1261
|
+
: React.createElement('header', { key: section + '-collapsed', className: 'dsh-super-code-workspace-collapsed' },
|
|
1262
|
+
React.createElement(WorkspaceSectionToggle, { section, expanded: false, onToggle, t: props.t }))
|
|
1263
|
+
})))
|
|
816
1264
|
}
|
|
817
1265
|
|
|
818
1266
|
function AgentCatalogWatch({ id, watchCatalog }) {
|
|
@@ -1588,6 +2036,14 @@ const budgetRemoteContribution = {
|
|
|
1588
2036
|
})),
|
|
1589
2037
|
}
|
|
1590
2038
|
|
|
2039
|
+
const connectionRemoteContribution = {
|
|
2040
|
+
package: 'dsh-super-code', descriptors: ['readConfig', 'saveConfig', 'revealToken', 'rotateToken', 'overview', 'disconnectClient', 'directories', 'pickDirectory', 'browseDirectory', 'pendingDirectories', 'resolveDirectory'].map(method => ({
|
|
2041
|
+
id: `dsh-super-code:superCodeConnection/${method}`, service: 'superCodeConnection', namespace: 'superCodeConnection', method, invocation: { kind: 'direct' },
|
|
2042
|
+
parameters: (method === 'saveConfig' ? ['content', 'expected'] : method === 'rotateToken' ? ['expected'] : method === 'resolveDirectory' ? ['id', 'decision'] : method === 'disconnectClient' ? ['id'] : method === 'browseDirectory' ? ['path'] : [])
|
|
2043
|
+
.map(name => ({ name, wire: name, source: 'json', codec: remoteStringCodec })), result: { mode: 'src-json' },
|
|
2044
|
+
})),
|
|
2045
|
+
}
|
|
2046
|
+
|
|
1591
2047
|
const budgetFields = [
|
|
1592
2048
|
{ key: 'thresholdRatio', group: 'context', factor: 0.01, min: 1, max: 100, step: 'any' },
|
|
1593
2049
|
{ key: 'retainRatio', group: 'context', factor: 0.01, min: 0, max: 100, step: 'any' },
|
|
@@ -1600,7 +2056,8 @@ const budgetFields = [
|
|
|
1600
2056
|
{ key: 'maxContextBytes', group: 'superCode', root: true, factor: 1, min: 1024, max: 262144, step: 1 },
|
|
1601
2057
|
]
|
|
1602
2058
|
function budgetDraft(configuration) {
|
|
1603
|
-
|
|
2059
|
+
const deletion = configuration.deletionProtection
|
|
2060
|
+
return Object.fromEntries([['deletionProtection', deletion === true ? 'strict' : deletion === false ? 'off' : deletion ?? 'relaxed'], ['deletionProtectionForOfficialPresets', configuration.deletionProtectionForOfficialPresets ?? false], ['applyToOfficialPresets', configuration.applyToOfficialPresets ?? false], ['pressureBasis', configuration.budgets?.pressureBasis ?? ''],
|
|
1604
2061
|
...budgetFields.map(field => {
|
|
1605
2062
|
const value = field.root ? configuration[field.key] : configuration.budgets?.[field.key]
|
|
1606
2063
|
return [field.key, value === undefined ? '' : String(value / field.factor)]
|
|
@@ -1608,6 +2065,14 @@ function budgetDraft(configuration) {
|
|
|
1608
2065
|
}
|
|
1609
2066
|
function budgetConfiguration(draft) {
|
|
1610
2067
|
const config = {}, budgets = {}
|
|
2068
|
+
if (draft.deletionProtection !== undefined) {
|
|
2069
|
+
if (!['off', 'relaxed', 'strict'].includes(draft.deletionProtection)) return { ok: false, error: 'invalid' }
|
|
2070
|
+
config.deletionProtection = draft.deletionProtection
|
|
2071
|
+
}
|
|
2072
|
+
if (draft.deletionProtectionForOfficialPresets !== undefined) {
|
|
2073
|
+
if (typeof draft.deletionProtectionForOfficialPresets !== 'boolean') return { ok: false, error: 'invalid' }
|
|
2074
|
+
config.deletionProtectionForOfficialPresets = draft.deletionProtectionForOfficialPresets
|
|
2075
|
+
}
|
|
1611
2076
|
if (draft.applyToOfficialPresets !== undefined) {
|
|
1612
2077
|
if (typeof draft.applyToOfficialPresets !== 'boolean') return { ok: false, error: 'invalid' }
|
|
1613
2078
|
config.applyToOfficialPresets = draft.applyToOfficialPresets
|
|
@@ -1632,7 +2097,7 @@ function budgetConfiguration(draft) {
|
|
|
1632
2097
|
return { ok: true, configuration: config }
|
|
1633
2098
|
}
|
|
1634
2099
|
|
|
1635
|
-
function BudgetSettings({ api, t, active }) {
|
|
2100
|
+
function BudgetSettings({ api, t, active, section = 'context', onSaved = () => {} }) {
|
|
1636
2101
|
const [snapshot, setSnapshot] = useState(null)
|
|
1637
2102
|
const [draft, setDraft] = useState(() => budgetDraft({})), [busy, setBusy] = useState(false)
|
|
1638
2103
|
const [error, setError] = useState(''), [notice, setNotice] = useState('')
|
|
@@ -1649,24 +2114,33 @@ function BudgetSettings({ api, t, active }) {
|
|
|
1649
2114
|
finally { pending.current = false; if (alive.current) setBusy(false) }
|
|
1650
2115
|
}
|
|
1651
2116
|
useEffect(() => { alive.current = true; return () => { alive.current = false } }, [api])
|
|
1652
|
-
useEffect(() => { if (active && !snapshot && !error) read() }, [api, active])
|
|
2117
|
+
useEffect(() => { if (active && (!snapshot || !dirty) && !error) read() }, [api, active])
|
|
1653
2118
|
const save = async reset => {
|
|
1654
2119
|
if (pending.current || !snapshot?.writable) return
|
|
1655
|
-
const
|
|
2120
|
+
const protection = section === 'protection'
|
|
2121
|
+
const parsed = budgetConfiguration(draft)
|
|
2122
|
+
if (parsed.ok) {
|
|
2123
|
+
const selected = protection
|
|
2124
|
+
? { ...snapshot.configuration, deletionProtection: reset ? 'relaxed' : draft.deletionProtection, deletionProtectionForOfficialPresets: reset ? false : draft.deletionProtectionForOfficialPresets }
|
|
2125
|
+
: { ...(reset ? {} : parsed.configuration), deletionProtection: snapshot.configuration.deletionProtection, deletionProtectionForOfficialPresets: snapshot.configuration.deletionProtectionForOfficialPresets }
|
|
2126
|
+
parsed.configuration = selected
|
|
2127
|
+
}
|
|
1656
2128
|
if (!parsed.ok) { setError(parsed.error); setNotice(''); return }
|
|
1657
2129
|
pending.current = true; setBusy(true); setError(''); setNotice('')
|
|
1658
2130
|
try {
|
|
1659
|
-
const response =
|
|
1660
|
-
: await api.saveConfig(JSON.stringify(parsed.configuration), String(snapshot.revision))
|
|
2131
|
+
const response = await api.saveConfig(JSON.stringify(parsed.configuration), String(snapshot.revision))
|
|
1661
2132
|
if (!response.ok) throw new Error('Remote unavailable')
|
|
1662
2133
|
if (!alive.current) return
|
|
1663
|
-
if (response.value.ok) { receive(response.value.snapshot); setNotice(reset ? 'resetDone' : 'saved') }
|
|
2134
|
+
if (response.value.ok) { receive(response.value.snapshot); onSaved(); setNotice(reset ? 'resetDone' : 'saved') }
|
|
1664
2135
|
else setError(response.value.error)
|
|
1665
2136
|
} catch { if (alive.current) setError('save-failed') }
|
|
1666
2137
|
finally { pending.current = false; if (alive.current) setBusy(false) }
|
|
1667
2138
|
}
|
|
1668
2139
|
const disabled = busy || !snapshot?.writable
|
|
1669
|
-
const
|
|
2140
|
+
const sectionDraft = value => section === 'protection'
|
|
2141
|
+
? [value.deletionProtection, value.deletionProtectionForOfficialPresets]
|
|
2142
|
+
: Object.fromEntries(Object.entries(value).filter(([key]) => !key.startsWith('deletionProtection')))
|
|
2143
|
+
const dirty = snapshot && JSON.stringify(sectionDraft(draft)) !== JSON.stringify(sectionDraft(budgetDraft(snapshot.configuration)))
|
|
1670
2144
|
const edit = (key, value) => { setDraft(current => ({ ...current, [key]: value })); setError(''); setNotice('') }
|
|
1671
2145
|
const defaultLabel = (key, field) => {
|
|
1672
2146
|
const value = field?.root ? snapshot?.defaults?.[key] : snapshot?.defaults?.budgets?.[key]
|
|
@@ -1683,7 +2157,21 @@ function BudgetSettings({ api, t, active }) {
|
|
|
1683
2157
|
onChange: event => edit(field.key, event.target.value) }))
|
|
1684
2158
|
})
|
|
1685
2159
|
return React.createElement('section', { className: 'dsh-super-code-budgets' },
|
|
1686
|
-
React.createElement('form', { 'aria-label': t('budget.title'), 'aria-busy': busy, onSubmit: event => { event.preventDefault(); save(false) } },
|
|
2160
|
+
React.createElement('form', { 'aria-label': t(section === 'protection' ? 'delete.title' : 'budget.title'), 'aria-busy': busy, onSubmit: event => { event.preventDefault(); save(false) } },
|
|
2161
|
+
section === 'protection' && React.createElement('section', { className: 'dsh-super-code-budget-group', 'aria-labelledby': 'super-code-deletion-title' },
|
|
2162
|
+
React.createElement('h3', { id: 'super-code-deletion-title' }, t('delete.title')),
|
|
2163
|
+
React.createElement('div', { className: 'dsh-super-code-settings-field' },
|
|
2164
|
+
React.createElement('label', { id: 'super-code-deletion-mode-label' }, t('delete.mode')),
|
|
2165
|
+
React.createElement(SelectControl, { id: 'super-code-deletion-protection', label: t('delete.mode'), value: draft.deletionProtection,
|
|
2166
|
+
choices: ['off', 'relaxed', 'strict'].map(mode => [mode, t(`delete.${mode}`)]), disabled,
|
|
2167
|
+
onChange: value => edit('deletionProtection', value) })),
|
|
2168
|
+
React.createElement('p', { id: 'super-code-deletion-hint', className: 'dsh-super-code-budget-hint' }, t(`delete.hint.${draft.deletionProtection}`)),
|
|
2169
|
+
React.createElement('label', { className: 'dsh-super-code-budget-switch' },
|
|
2170
|
+
React.createElement('input', { id: 'super-code-deletion-official', type: 'checkbox', role: 'switch', checked: draft.deletionProtectionForOfficialPresets, disabled: disabled || draft.deletionProtection === 'off',
|
|
2171
|
+
'aria-describedby': 'super-code-deletion-official-hint', onChange: event => edit('deletionProtectionForOfficialPresets', event.target.checked) }),
|
|
2172
|
+
React.createElement('span', null, t('delete.official'))),
|
|
2173
|
+
React.createElement('p', { id: 'super-code-deletion-official-hint', className: 'dsh-super-code-budget-hint' }, t('delete.officialHint'))),
|
|
2174
|
+
section !== 'protection' && React.createElement(React.Fragment, null,
|
|
1687
2175
|
React.createElement('p', { className: 'dsh-super-code-budget-hint' }, t('budget.scope')),
|
|
1688
2176
|
React.createElement('section', { className: 'dsh-super-code-budget-group', 'aria-labelledby': 'super-code-common-title' },
|
|
1689
2177
|
React.createElement('h3', { id: 'super-code-common-title' }, t('budget.common')),
|
|
@@ -1711,7 +2199,7 @@ function BudgetSettings({ api, t, active }) {
|
|
|
1711
2199
|
React.createElement('section', { className: 'dsh-super-code-budget-group dsh-super-code-budget-exclusive', 'aria-labelledby': 'super-code-exclusive-title' },
|
|
1712
2200
|
React.createElement('h3', { id: 'super-code-exclusive-title' }, t('budget.superCode')),
|
|
1713
2201
|
React.createElement('p', { className: 'dsh-super-code-budget-hint' }, t('budget.superCodeHint')),
|
|
1714
|
-
React.createElement('div', { className: 'dsh-super-code-budget-grid' }, fields('superCode'))),
|
|
2202
|
+
React.createElement('div', { className: 'dsh-super-code-budget-grid' }, fields('superCode')))),
|
|
1715
2203
|
!busy && snapshot && !snapshot.writable && React.createElement('small', null, t('budget.error.read-only')),
|
|
1716
2204
|
error && React.createElement('p', { role: 'alert' }, t(`budget.error.${error}`)),
|
|
1717
2205
|
notice && React.createElement('p', { role: 'status', className: 'dsh-super-code-budget-hint' }, t(`budget.${notice}`)),
|
|
@@ -1761,8 +2249,364 @@ function synchronizePresetDisplay(api, locale, reload, onError, preferenceScope)
|
|
|
1761
2249
|
return () => { disposed = true; unsubscribe(); unsubscribePreference() }
|
|
1762
2250
|
}
|
|
1763
2251
|
|
|
1764
|
-
function
|
|
1765
|
-
|
|
2252
|
+
function connectionDraft(config) {
|
|
2253
|
+
return { enabled: config.enabled, port: String(config.port), allowedDirectories: [...config.allowedDirectories], knownDirectories: [...config.knownDirectories ?? []],
|
|
2254
|
+
maxConcurrent: config.maxConnections ?? 3, maxConnections: String(config.maxConnections ?? 3), directoriesConfigured: config.directoriesConfigured ?? false }
|
|
2255
|
+
}
|
|
2256
|
+
function connectionConfiguration(draft) {
|
|
2257
|
+
const port = Number(draft.port), maxConnections = Number(draft.maxConnections)
|
|
2258
|
+
const allowedDirectories = [...new Set(draft.allowedDirectories)]
|
|
2259
|
+
if (typeof draft.enabled !== 'boolean' || !Number.isInteger(port) || port < 1024 || port > 65535 || !Number.isInteger(maxConnections) || maxConnections < 1 || maxConnections > 32 || allowedDirectories.length > 32 || allowedDirectories.some(path => typeof path !== 'string' || !path) || (draft.enabled && !allowedDirectories.length)) return { ok: false }
|
|
2260
|
+
return { ok: true, configuration: { enabled: draft.enabled, port, allowedDirectories, maxConcurrent: maxConnections, maxConnections, directoriesConfigured: true, knownDirectories: draft.knownDirectories } }
|
|
2261
|
+
}
|
|
2262
|
+
/** Serialize writes and retain newer edits while an earlier revision is being saved. */
|
|
2263
|
+
function createConnectionAutosave(api) {
|
|
2264
|
+
let state = { snapshot: false, draft: connectionDraft({ enabled: false, port: 23457, allowedDirectories: [] }), saving: false, error: '', notice: '', saveCount: 0 }
|
|
2265
|
+
let version = 0, acknowledged = 0, blocked = -1, timer = false, running = false
|
|
2266
|
+
const listeners = new Set()
|
|
2267
|
+
const update = patch => { state = { ...state, ...patch }; for (const listener of listeners) listener() }
|
|
2268
|
+
const flush = () => {
|
|
2269
|
+
if (timer) { clearTimeout(timer); timer = false }
|
|
2270
|
+
if (running) return running
|
|
2271
|
+
if (version === acknowledged || version === blocked || !state.snapshot) return Promise.resolve()
|
|
2272
|
+
running = (async () => {
|
|
2273
|
+
update({ saving: true, error: '', notice: '' })
|
|
2274
|
+
try {
|
|
2275
|
+
while (acknowledged < version) {
|
|
2276
|
+
const sent = version, parsed = connectionConfiguration(state.draft)
|
|
2277
|
+
if (!parsed.ok) { blocked = version; update({ error: 'invalid' }); break }
|
|
2278
|
+
const result = await api.saveConfig(JSON.stringify(parsed.configuration), String(state.snapshot.revision))
|
|
2279
|
+
if (!result.ok) throw new Error('Remote unavailable')
|
|
2280
|
+
if (!result.value.ok) { blocked = version; update({ error: result.value.error }); break }
|
|
2281
|
+
acknowledged = sent
|
|
2282
|
+
update({ snapshot: result.value.snapshot, saveCount: state.saveCount + 1,
|
|
2283
|
+
...(sent === version ? { draft: connectionDraft(result.value.snapshot.configuration), notice: 'saved' } : {}) })
|
|
2284
|
+
}
|
|
2285
|
+
} catch { blocked = version; update({ error: 'save-failed' }) }
|
|
2286
|
+
finally { update({ saving: false }) }
|
|
2287
|
+
})().finally(() => { running = false })
|
|
2288
|
+
return running
|
|
2289
|
+
}
|
|
2290
|
+
return {
|
|
2291
|
+
getSnapshot: () => state,
|
|
2292
|
+
subscribe: listener => { listeners.add(listener); return () => listeners.delete(listener) },
|
|
2293
|
+
hasChanges: () => version !== acknowledged || state.saving,
|
|
2294
|
+
receive: (snapshot, draft = connectionDraft(snapshot.configuration)) => {
|
|
2295
|
+
acknowledged = ++version
|
|
2296
|
+
update({ snapshot, draft, error: '', notice: '' })
|
|
2297
|
+
},
|
|
2298
|
+
revision: snapshot => update({ snapshot }),
|
|
2299
|
+
change: (patch, delay = 0) => {
|
|
2300
|
+
version++
|
|
2301
|
+
update({ draft: { ...state.draft, ...patch }, error: '', notice: '' })
|
|
2302
|
+
if (timer) { clearTimeout(timer); timer = false }
|
|
2303
|
+
if (delay) timer = setTimeout(() => { timer = false; void flush() }, delay)
|
|
2304
|
+
else void flush()
|
|
2305
|
+
},
|
|
2306
|
+
flush,
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
function ConnectionSwitch({ checked, disabled, onChange, children }) {
|
|
2310
|
+
return React.createElement('label', { className: 'dsh-super-code-connection-switch' },
|
|
2311
|
+
React.createElement('input', { type: 'checkbox', role: 'switch', checked, disabled, onChange: event => onChange(event.target.checked) }),
|
|
2312
|
+
React.createElement('span', { className: 'dsh-super-code-connection-track', 'aria-hidden': true }, React.createElement('i')), children)
|
|
2313
|
+
}
|
|
2314
|
+
function DirectoryBrowser({ api, t, onSelect, onClose }) {
|
|
2315
|
+
const [listing, setListing] = useState(null), [busy, setBusy] = useState(false), [error, setError] = useState(false)
|
|
2316
|
+
const dialog = useRef(null), generation = useRef(0), lastPath = useRef('')
|
|
2317
|
+
const browse = async path => {
|
|
2318
|
+
const version = ++generation.current; lastPath.current = path; setBusy(true); setError(false)
|
|
2319
|
+
try { const result = await api.browseDirectory(path); if (!result.ok) throw new Error(); if (version === generation.current) setListing(result.value) }
|
|
2320
|
+
catch { if (version === generation.current) setError(true) }
|
|
2321
|
+
finally { if (version === generation.current) setBusy(false) }
|
|
2322
|
+
}
|
|
2323
|
+
useEffect(() => {
|
|
2324
|
+
const element = dialog.current; element.showModal(); browse('')
|
|
2325
|
+
return () => { generation.current++; if (element.open) element.close() }
|
|
2326
|
+
}, [api])
|
|
2327
|
+
return React.createElement('dialog', { ref: dialog, className: 'dsh-super-code-confirm', 'aria-labelledby': 'super-code-directory-title',
|
|
2328
|
+
onCancel: event => { event.preventDefault(); event.stopPropagation(); onClose() }, onKeyDown: event => event.stopPropagation() },
|
|
2329
|
+
React.createElement('h3', { id: 'super-code-directory-title' }, t('connection.directoryBrowser')),
|
|
2330
|
+
React.createElement('nav', { className: 'dsh-super-code-directory-crumbs', 'aria-label': t('connection.directoryBrowser') },
|
|
2331
|
+
listing?.crumbs.map((row, index) => React.createElement(ActionButton, { key: `${row.path}-${index}`, type: 'button', disabled: busy, onClick: () => browse(row.path) }, row.name || row.path))),
|
|
2332
|
+
listing && React.createElement('code', { className: 'dsh-super-code-directory-path' }, listing.path),
|
|
2333
|
+
React.createElement('div', { className: 'dsh-super-code-directory-browser', 'aria-busy': busy },
|
|
2334
|
+
listing && listing.crumbs.length > 1 && React.createElement(ActionButton, { type: 'button', disabled: busy, onClick: () => browse(listing.crumbs[listing.crumbs.length - 2].path) }, '↑ ', t('connection.parentDirectory')),
|
|
2335
|
+
listing?.entries.map(row => React.createElement(ActionButton, { key: row.path, type: 'button', disabled: busy, onClick: () => browse(row.path) }, React.createElement(Glyph, { type: 'folder' }), row.name)),
|
|
2336
|
+
listing && !listing.entries.length && React.createElement('p', null, t('connection.emptyDirectory'))),
|
|
2337
|
+
error && React.createElement('p', { role: 'alert' }, t('connection.browserError')),
|
|
2338
|
+
React.createElement('div', { className: 'dsh-super-code-confirm-actions' },
|
|
2339
|
+
error && React.createElement(ActionButton, { type: 'button', disabled: busy, onClick: () => browse(lastPath.current) }, t('connection.reload')),
|
|
2340
|
+
React.createElement(ActionButton, { type: 'button', onClick: onClose }, t('preset.cancel')),
|
|
2341
|
+
React.createElement(ActionButton, { type: 'button', className: 'dsh-super-code-primary-button', disabled: busy || error || !listing,
|
|
2342
|
+
onClick: () => onSelect(listing.path) }, t('connection.selectDirectory'))))
|
|
2343
|
+
}
|
|
2344
|
+
function connectionSessions(overview) {
|
|
2345
|
+
const online = new Set(overview.connections.map(row => row.id)), connected = [], history = []
|
|
2346
|
+
for (const row of overview.sessions) (row.connectionId && online.has(row.connectionId) ? connected : history).push(row)
|
|
2347
|
+
return { connected, history }
|
|
2348
|
+
}
|
|
2349
|
+
function ConnectionActivity({ api, t, active, openSession }) {
|
|
2350
|
+
const [overview, setOverview] = useState(null), [busy, setBusy] = useState(false), [error, setError] = useState(false)
|
|
2351
|
+
const alive = useRef(false), pending = useRef(false)
|
|
2352
|
+
const load = async () => {
|
|
2353
|
+
if (pending.current) return
|
|
2354
|
+
pending.current = true
|
|
2355
|
+
try { const result = await api.overview(); if (!result.ok) throw new Error(); if (alive.current) { setOverview(result.value); setError(false) } }
|
|
2356
|
+
catch { if (alive.current) setError(true) }
|
|
2357
|
+
finally { pending.current = false }
|
|
2358
|
+
}
|
|
2359
|
+
useEffect(() => { alive.current = true; return () => { alive.current = false } }, [api])
|
|
2360
|
+
useEffect(() => { if (!active) return; load(); const timer = setInterval(load, 2000); return () => clearInterval(timer) }, [active, api])
|
|
2361
|
+
const disconnect = async id => {
|
|
2362
|
+
if (pending.current) return
|
|
2363
|
+
pending.current = true; setBusy(true)
|
|
2364
|
+
try { const result = await api.disconnectClient(id); if (!result.ok) throw new Error() }
|
|
2365
|
+
catch { if (alive.current) setError(true); return }
|
|
2366
|
+
finally { pending.current = false; if (alive.current) setBusy(false) }
|
|
2367
|
+
if (alive.current) load()
|
|
2368
|
+
}
|
|
2369
|
+
const groups = overview ? connectionSessions(overview) : { connected: [], history: [] }
|
|
2370
|
+
return React.createElement('section', { className: 'dsh-super-code-connection-activity', 'aria-label': t('home.sessions') },
|
|
2371
|
+
React.createElement('h3', null, t('home.connections')),
|
|
2372
|
+
!overview ? React.createElement('p', { role: 'status' }, t('connection.state.loading'))
|
|
2373
|
+
: overview.connections.length ? React.createElement('ul', { className: 'dsh-super-code-connection-list' }, overview.connections.map(row => React.createElement('li', { key: row.id },
|
|
2374
|
+
React.createElement('span', null, React.createElement('strong', null, row.name), React.createElement('small', null, row.version, ' · ', t('home.lastSeen'), ' ', new Date(row.lastSeen).toLocaleTimeString())),
|
|
2375
|
+
React.createElement(ActionButton, { type: 'button', disabled: busy, onClick: () => disconnect(row.id) }, t('home.disconnect'))))) : React.createElement('p', null, t('home.noConnections')),
|
|
2376
|
+
React.createElement('h3', null, t('home.sessions')),
|
|
2377
|
+
...['connected', 'history'].map(kind => React.createElement('div', { key: kind, 'data-session-group': kind },
|
|
2378
|
+
React.createElement('h4', null, t(kind === 'connected' ? 'home.connectedSessions' : 'home.historySessions'), ' · ', overview ? groups[kind].length : '—'),
|
|
2379
|
+
groups[kind].length ? React.createElement('ul', { className: 'dsh-super-code-connection-list' }, groups[kind].map(row => React.createElement('li', { key: row.taskId },
|
|
2380
|
+
React.createElement('span', null, React.createElement('strong', null, row.client || row.sessionId), React.createElement('small', null, t(`taskStatus.${row.status}`), ' · ', row.cwd), React.createElement('code', null, row.sessionId)),
|
|
2381
|
+
React.createElement(ActionButton, { type: 'button', onClick: () => openSession(row.sessionId) }, t('home.openSession'))))) : overview && React.createElement('p', null, t('home.noSessions')))),
|
|
2382
|
+
error && React.createElement('p', { role: 'alert' }, t('home.error')))
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2385
|
+
function ConnectionSettings({ api, t, active, openSession, onSaved = () => {} }) {
|
|
2386
|
+
const editor = useMemo(() => createConnectionAutosave(api), [api])
|
|
2387
|
+
const editing = useSyncExternalStore(editor.subscribe, editor.getSnapshot, editor.getSnapshot)
|
|
2388
|
+
const { snapshot, draft, saving } = editing
|
|
2389
|
+
const [catalog, setCatalog] = useState({ items: [], picker: 'unavailable' }), [custom, setCustom] = useState([]), [browser, setBrowser] = useState(false)
|
|
2390
|
+
const [busy, setBusy] = useState(false), [error, setError] = useState(''), [notice, setNotice] = useState(''), [token, setToken] = useState('')
|
|
2391
|
+
const alive = useRef(false), pending = useRef(false), visible = useRef(active)
|
|
2392
|
+
visible.current = active
|
|
2393
|
+
const dirty = editor.hasChanges()
|
|
2394
|
+
const receive = (value, directories = catalog) => {
|
|
2395
|
+
const next = connectionDraft(value.configuration)
|
|
2396
|
+
if (!next.directoriesConfigured && !next.allowedDirectories.length) next.allowedDirectories = directories.items.filter(row => row.workspace && row.available).slice(0, 32).map(row => row.path)
|
|
2397
|
+
editor.receive(value, next)
|
|
2398
|
+
}
|
|
2399
|
+
const load = async (discard = false) => {
|
|
2400
|
+
if (pending.current || (discard && editor.getSnapshot().saving)) return
|
|
2401
|
+
pending.current = true; setBusy(true); setError('')
|
|
2402
|
+
try {
|
|
2403
|
+
const [config, directories, credential] = await Promise.all([api.readConfig(), api.directories(), api.revealToken()])
|
|
2404
|
+
if (!config.ok || !directories.ok || !credential.ok) throw new Error()
|
|
2405
|
+
if (!alive.current) return
|
|
2406
|
+
setCatalog(directories.value)
|
|
2407
|
+
if (discard || !dirty) receive(config.value, directories.value)
|
|
2408
|
+
if (visible.current) setToken(credential.value.token)
|
|
2409
|
+
} catch { if (alive.current) setError('unavailable') }
|
|
2410
|
+
finally { pending.current = false; if (alive.current) setBusy(false) }
|
|
2411
|
+
}
|
|
2412
|
+
const action = async kind => {
|
|
2413
|
+
if (pending.current) return
|
|
2414
|
+
pending.current = true; setBusy(true); setError(''); setNotice('')
|
|
2415
|
+
try {
|
|
2416
|
+
const response = kind === 'rotate' ? await api.rotateToken(String(snapshot.revision)) : await api.revealToken()
|
|
2417
|
+
if (!response.ok) throw new Error()
|
|
2418
|
+
if (!alive.current) return
|
|
2419
|
+
if (kind === 'token') { if (visible.current) setToken(response.value.token) }
|
|
2420
|
+
else if (response.value.ok) {
|
|
2421
|
+
if (kind === 'rotate') {
|
|
2422
|
+
editor.revision(response.value.snapshot)
|
|
2423
|
+
const credential = await api.revealToken()
|
|
2424
|
+
if (credential.ok && alive.current && visible.current) setToken(credential.value.token)
|
|
2425
|
+
} else receive(response.value.snapshot)
|
|
2426
|
+
onSaved(); setNotice(kind === 'rotate' ? 'rotated' : 'saved')
|
|
2427
|
+
} else setError(response.value.error)
|
|
2428
|
+
} catch { if (alive.current) setError('unavailable') }
|
|
2429
|
+
finally { pending.current = false; if (alive.current) setBusy(false) }
|
|
2430
|
+
}
|
|
2431
|
+
useEffect(() => { alive.current = true; return () => { alive.current = false; void editor.flush() } }, [editor])
|
|
2432
|
+
useEffect(() => { if (editing.saveCount) onSaved() }, [editing.saveCount])
|
|
2433
|
+
useEffect(() => { if (active) load(); else { setToken(''); setBrowser(false) } }, [api, active])
|
|
2434
|
+
useEffect(() => {
|
|
2435
|
+
if (!active) return
|
|
2436
|
+
const changed = () => { if (dirty) setError('conflict'); else load(true) }
|
|
2437
|
+
window.addEventListener('super-code-directories-changed', changed)
|
|
2438
|
+
return () => window.removeEventListener('super-code-directories-changed', changed)
|
|
2439
|
+
}, [active, dirty, api])
|
|
2440
|
+
const disabled = busy || !snapshot?.writable
|
|
2441
|
+
const change = (patch, delay = 0) => {
|
|
2442
|
+
// Revoking the last directory also stops admission instead of retaining its old grant.
|
|
2443
|
+
editor.change(patch.allowedDirectories?.length === 0 ? { ...patch, enabled: false } : patch, delay)
|
|
2444
|
+
setNotice(''); setError('')
|
|
2445
|
+
}
|
|
2446
|
+
const edit = (key, value) => change({ [key]: value }, ['port', 'maxConnections'].includes(key) ? 350 : 0)
|
|
2447
|
+
const choose = path => {
|
|
2448
|
+
if (!draft.allowedDirectories.includes(path) && draft.allowedDirectories.length >= 32) { setError('directory-limit'); return }
|
|
2449
|
+
setCustom(rows => [...new Set([...rows, path])]); change({ allowedDirectories: [...new Set([...draft.allowedDirectories, path])], knownDirectories: [...new Set([...draft.knownDirectories, path])].slice(-128) }); setNotice(''); setError(''); setBrowser(false)
|
|
2450
|
+
}
|
|
2451
|
+
const addDirectory = async () => {
|
|
2452
|
+
if (pending.current) return
|
|
2453
|
+
if (catalog.picker !== 'native' && !globalThis.__DSH_DIRECTORY_PICKER__?.pick) { setBrowser(true); return }
|
|
2454
|
+
pending.current = true; setBusy(true)
|
|
2455
|
+
try {
|
|
2456
|
+
const result = globalThis.__DSH_DIRECTORY_PICKER__?.pick ? await globalThis.__DSH_DIRECTORY_PICKER__.pick()
|
|
2457
|
+
: await api.pickDirectory().then(result => { if (!result.ok) throw new Error(); return result.value.kind === 'selected' ? result.value.path : null })
|
|
2458
|
+
if (alive.current && result) choose(result)
|
|
2459
|
+
} catch { if (alive.current) setError('picker-unavailable') }
|
|
2460
|
+
finally { pending.current = false; if (alive.current) setBusy(false) }
|
|
2461
|
+
}
|
|
2462
|
+
const rows = [...catalog.items]
|
|
2463
|
+
for (const path of [...custom, ...draft.knownDirectories, ...draft.allowedDirectories]) if (!rows.some(row => row.path === path)) rows.push({ path, title: '', available: true, workspace: false })
|
|
2464
|
+
const copy = async value => {
|
|
2465
|
+
try { await navigator.clipboard.writeText(value); if (alive.current) { setNotice('copied'); setError('') } }
|
|
2466
|
+
catch { if (alive.current) setError('clipboard') }
|
|
2467
|
+
}
|
|
2468
|
+
const command = snapshot?.url ? t('connection.prompt', { url: snapshot.url }) : ''
|
|
2469
|
+
return React.createElement('section', { className: 'dsh-super-code-connection' },
|
|
2470
|
+
React.createElement('div', { className: 'dsh-super-code-settings-status-row' },
|
|
2471
|
+
React.createElement('p', { className: 'dsh-super-code-preset-status', role: 'status', 'data-available': snapshot?.state === 'listening' },
|
|
2472
|
+
React.createElement('i', { 'aria-hidden': true }), t(`connection.state.${snapshot?.state || 'loading'}`)),
|
|
2473
|
+
React.createElement('div', { className: 'dsh-super-code-settings-actions' }, React.createElement(ActionButton, { type: 'button', disabled: busy, onClick: () => load(true) }, t('connection.reload')))),
|
|
2474
|
+
snapshot?.error && React.createElement('p', { role: 'alert' }, t(`connection.error.${snapshot.error}`)),
|
|
2475
|
+
React.createElement(ConnectionActivity, { api, t, active, openSession }),
|
|
2476
|
+
React.createElement('form', { 'aria-label': t('connection.title'), 'aria-busy': saving, onSubmit: event => { event.preventDefault(); void editor.flush() } },
|
|
2477
|
+
React.createElement(ConnectionSwitch, { checked: draft.enabled, disabled, onChange: value => edit('enabled', value) }, t('connection.enabled')),
|
|
2478
|
+
React.createElement('p', { className: 'dsh-super-code-connection-hint' }, t('connection.scope')),
|
|
2479
|
+
React.createElement('div', { className: 'dsh-super-code-connection-grid' },
|
|
2480
|
+
...[['port', 1024, 65535], ['maxConnections', 1, 32]].map(([key, min, max]) => React.createElement('div', { key, className: 'dsh-super-code-settings-field' },
|
|
2481
|
+
React.createElement('label', { htmlFor: `super-code-connection-${key}` }, t(`connection.${key}`)),
|
|
2482
|
+
React.createElement('input', { id: `super-code-connection-${key}`, type: 'number', min, max, step: 1, required: true, value: draft[key], disabled, onChange: event => edit(key, event.target.value), onBlur: () => { void editor.flush() } })))),
|
|
2483
|
+
React.createElement('small', null, t('connection.connectionsHint')),
|
|
2484
|
+
React.createElement('fieldset', { className: 'dsh-super-code-directory-fieldset' },
|
|
2485
|
+
React.createElement('legend', null, t('connection.directories')),
|
|
2486
|
+
React.createElement('div', { className: 'dsh-super-code-directory-list' },
|
|
2487
|
+
...rows.map(row => React.createElement('div', { key: row.path, className: 'dsh-super-code-directory-row' },
|
|
2488
|
+
React.createElement('label', null,
|
|
2489
|
+
React.createElement('input', { type: 'checkbox', checked: draft.allowedDirectories.includes(row.path), disabled: disabled || (!row.available && !draft.allowedDirectories.includes(row.path)),
|
|
2490
|
+
onChange: event => {
|
|
2491
|
+
if (event.target.checked && draft.allowedDirectories.length >= 32) { setError('directory-limit'); return }
|
|
2492
|
+
edit('allowedDirectories', event.target.checked ? [...draft.allowedDirectories, row.path] : draft.allowedDirectories.filter(path => path !== row.path))
|
|
2493
|
+
} }),
|
|
2494
|
+
React.createElement('span', null, row.title && React.createElement('strong', null, row.title), React.createElement('code', null, row.path),
|
|
2495
|
+
React.createElement('small', { 'data-unavailable': !row.available }, t(!row.available ? 'connection.directoryUnavailable' : row.workspace ? 'connection.workspaceDirectory' : 'connection.customDirectory')))),
|
|
2496
|
+
!row.workspace && React.createElement(ActionButton, { type: 'button', disabled, 'aria-label': `${t('connection.removeDirectory')}: ${row.path}`,
|
|
2497
|
+
onClick: () => { setCustom(values => values.filter(path => path !== row.path)); setCatalog(value => ({ ...value, items: value.items.filter(item => item.path !== row.path) })); change({ knownDirectories: draft.knownDirectories.filter(path => path !== row.path), allowedDirectories: draft.allowedDirectories.filter(path => path !== row.path) }) } }, t('preset.remove')))),
|
|
2498
|
+
!rows.length && React.createElement('p', null, t('connection.noDirectories'))),
|
|
2499
|
+
React.createElement('div', { className: 'dsh-super-code-settings-actions' },
|
|
2500
|
+
React.createElement(ActionButton, { type: 'button', disabled: disabled || catalog.picker === 'unavailable', onClick: addDirectory }, t('connection.addDirectory'))),
|
|
2501
|
+
React.createElement('small', null, t('connection.directoriesHint'))),
|
|
2502
|
+
React.createElement('p', { role: 'status', className: 'dsh-super-code-connection-hint', 'data-connection-save': saving ? 'saving' : editing.error ? 'error' : editing.notice === 'saved' ? 'saved' : 'idle' },
|
|
2503
|
+
t(saving ? 'connection.saving' : editing.notice === 'saved' ? 'connection.saved' : 'connection.autosave'))),
|
|
2504
|
+
snapshot?.url && React.createElement('div', { className: 'dsh-super-code-connection-client' },
|
|
2505
|
+
React.createElement('h3', null, t('connection.client')), React.createElement('code', null, snapshot.url), React.createElement('pre', null, command),
|
|
2506
|
+
React.createElement('div', { className: 'dsh-super-code-settings-actions' },
|
|
2507
|
+
React.createElement(ActionButton, { type: 'button', disabled: busy, onClick: () => copy(command) }, t('connection.copyCommand')),
|
|
2508
|
+
React.createElement(ActionButton, { type: 'button', disabled, onClick: () => token ? setToken('') : action('token') }, t(token ? 'connection.hideToken' : 'connection.showToken')),
|
|
2509
|
+
active && token && React.createElement(ActionButton, { type: 'button', onClick: () => copy(token) }, t('connection.copyToken')),
|
|
2510
|
+
React.createElement(ActionButton, { type: 'button', disabled: disabled || dirty, onClick: () => action('rotate') }, t('connection.rotate'))),
|
|
2511
|
+
active && token && React.createElement('input', { className: 'dsh-super-code-connection-token', 'aria-label': t('connection.token'), value: token, readOnly: true, autoComplete: 'off', spellCheck: false }),
|
|
2512
|
+
React.createElement('small', null, t('connection.tokenHint'))),
|
|
2513
|
+
notice && React.createElement('p', { role: 'status' }, t(`connection.${notice}`)),
|
|
2514
|
+
(error || editing.error) && React.createElement('p', { role: 'alert' }, t(`connection.error.${error || editing.error}`)),
|
|
2515
|
+
browser && React.createElement(DirectoryBrowser, { api, t, onSelect: choose, onClose: () => setBrowser(false) }))
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2518
|
+
function SettingsHome({ budgetApi, connectionApi, t, active, revision, navigate, children }) {
|
|
2519
|
+
const [budget, setBudget] = useState(null), [connection, setConnection] = useState(null), [overview, setOverview] = useState({ connections: [], sessions: [] })
|
|
2520
|
+
const [busy, setBusy] = useState(false), [error, setError] = useState(false), alive = useRef(false), pending = useRef(false)
|
|
2521
|
+
const load = async () => {
|
|
2522
|
+
if (pending.current) return
|
|
2523
|
+
pending.current = true
|
|
2524
|
+
try {
|
|
2525
|
+
const [b, c, o] = await Promise.all([budgetApi.readConfig(), connectionApi.readConfig(), connectionApi.overview()])
|
|
2526
|
+
if (!b.ok || !c.ok || !o.ok) throw new Error()
|
|
2527
|
+
if (alive.current) { setBudget(b.value); setConnection(c.value); setOverview(o.value); setError(false) }
|
|
2528
|
+
} catch { if (alive.current) setError(true) }
|
|
2529
|
+
finally { pending.current = false }
|
|
2530
|
+
}
|
|
2531
|
+
useEffect(() => { alive.current = true; return () => { alive.current = false } }, [budgetApi, connectionApi])
|
|
2532
|
+
useEffect(() => { if (!active) return; load(); const timer = setInterval(load, 2000); return () => clearInterval(timer) }, [active, revision, budgetApi, connectionApi])
|
|
2533
|
+
const change = async (kind, value) => {
|
|
2534
|
+
if (busy || pending.current) return
|
|
2535
|
+
pending.current = true; setBusy(true)
|
|
2536
|
+
try {
|
|
2537
|
+
let response
|
|
2538
|
+
if (kind === 'protection') response = await budgetApi.saveConfig(JSON.stringify({ ...budget.configuration, deletionProtection: value }), String(budget.revision))
|
|
2539
|
+
else response = await connectionApi.saveConfig(JSON.stringify({ ...connection.configuration, enabled: value }), String(connection.revision))
|
|
2540
|
+
if (!response.ok || !response.value.ok) throw new Error()
|
|
2541
|
+
} catch { if (alive.current) setError(true); return }
|
|
2542
|
+
finally { pending.current = false; if (alive.current) setBusy(false) }
|
|
2543
|
+
if (alive.current) load()
|
|
2544
|
+
}
|
|
2545
|
+
const mode = budget ? budgetDraft(budget.configuration).deletionProtection : ''
|
|
2546
|
+
return React.createElement('section', { className: 'dsh-super-code-home' }, children,
|
|
2547
|
+
React.createElement('div', { className: 'dsh-super-code-settings-actions' }, React.createElement(ActionButton, { type: 'button', onClick: () => navigate('preset') }, t('home.managePreset'))),
|
|
2548
|
+
React.createElement('h3', { className: 'dsh-super-code-home-category' }, t('home.protection')),
|
|
2549
|
+
React.createElement('div', { className: 'dsh-super-code-home-card' },
|
|
2550
|
+
React.createElement('div', { className: 'dsh-super-code-home-heading' }, React.createElement('strong', null, t('delete.title')),
|
|
2551
|
+
React.createElement(ActionButton, { type: 'button', onClick: () => navigate('protection') }, t('home.configure'))),
|
|
2552
|
+
budget && React.createElement(SelectControl, { label: t('delete.mode'), value: mode, choices: ['off', 'relaxed', 'strict'].map(value => [value, t(`delete.${value}`)]), disabled: busy || !budget.writable, onChange: value => change('protection', value) })),
|
|
2553
|
+
React.createElement('h3', { className: 'dsh-super-code-home-category' }, t('home.extensions')),
|
|
2554
|
+
React.createElement('div', { className: 'dsh-super-code-home-card' },
|
|
2555
|
+
React.createElement('div', { className: 'dsh-super-code-home-heading' }, React.createElement('strong', null, t('home.mcp')),
|
|
2556
|
+
React.createElement(ActionButton, { type: 'button', onClick: () => navigate('connection') }, t('home.configure'))),
|
|
2557
|
+
connection && React.createElement(ConnectionSwitch, { checked: connection.configuration.enabled, disabled: busy || !connection.writable,
|
|
2558
|
+
onChange: value => value && !connection.configuration.allowedDirectories.length ? navigate('connection') : change('connection', value) }, t(`connection.state.${connection.state}`)),
|
|
2559
|
+
connection && React.createElement('small', null, t('home.connectionCount', { count: overview.connections.length, max: connection.configuration.maxConnections })),
|
|
2560
|
+
React.createElement('dl', { className: 'dsh-super-code-session-counts' }, ...['connected', 'history'].map(kind => React.createElement('div', { key: kind, 'data-session-count': kind },
|
|
2561
|
+
React.createElement('dt', null, t(kind === 'connected' ? 'home.connectedSessions' : 'home.historySessions')),
|
|
2562
|
+
React.createElement('dd', null, connection ? connectionSessions(overview)[kind].length : '—'))))),
|
|
2563
|
+
error && React.createElement('p', { role: 'alert' }, t('home.error')))
|
|
2564
|
+
}
|
|
2565
|
+
function ConnectionConsent({ api, t }) {
|
|
2566
|
+
const [requests, setRequests] = useState([]), [busy, setBusy] = useState(false), [error, setError] = useState(false)
|
|
2567
|
+
const dialog = useRef(null), alive = useRef(false), pending = useRef(false), resolving = useRef(false), settled = useRef(new Set())
|
|
2568
|
+
const current = requests[0]
|
|
2569
|
+
const load = async () => {
|
|
2570
|
+
if (pending.current) return
|
|
2571
|
+
pending.current = true
|
|
2572
|
+
try { const result = await api.pendingDirectories(); if (alive.current && result.ok) setRequests(result.value.filter(row => !settled.current.has(row.id))) }
|
|
2573
|
+
catch { /* Transport loss never grants consent. */ }
|
|
2574
|
+
finally { pending.current = false }
|
|
2575
|
+
}
|
|
2576
|
+
useEffect(() => { alive.current = true; load(); const timer = setInterval(load, 1000); return () => { alive.current = false; clearInterval(timer) } }, [api])
|
|
2577
|
+
const resolve = async decision => {
|
|
2578
|
+
if (resolving.current || !current) return
|
|
2579
|
+
resolving.current = true; setBusy(true); setError(false)
|
|
2580
|
+
try {
|
|
2581
|
+
const result = await api.resolveDirectory(current.id, decision)
|
|
2582
|
+
if (!result.ok) throw new Error()
|
|
2583
|
+
if (alive.current) { settled.current.add(current.id); setRequests(rows => rows.filter(row => row.id !== current.id)); window.dispatchEvent(new Event('super-code-directories-changed')) }
|
|
2584
|
+
} catch { if (alive.current) setError(true) }
|
|
2585
|
+
finally { resolving.current = false; if (alive.current) setBusy(false) }
|
|
2586
|
+
}
|
|
2587
|
+
useEffect(() => {
|
|
2588
|
+
if (!current || !dialog.current) return
|
|
2589
|
+
setError(false)
|
|
2590
|
+
const element = dialog.current; element.showModal(); element.querySelector('[data-consent-reject]')?.focus()
|
|
2591
|
+
const escape = event => { if (event.key === 'Escape') { event.preventDefault(); event.stopImmediatePropagation(); if (!resolving.current) resolve('rejected') } }
|
|
2592
|
+
window.addEventListener('keydown', escape, true)
|
|
2593
|
+
return () => { window.removeEventListener('keydown', escape, true); if (element.open) element.close() }
|
|
2594
|
+
}, [current?.id])
|
|
2595
|
+
if (!current) return null
|
|
2596
|
+
return React.createElement('dialog', { ref: dialog, className: 'dsh-super-code-confirm', 'aria-labelledby': 'super-code-consent-title',
|
|
2597
|
+
onCancel: event => { event.preventDefault(); event.stopPropagation(); resolve('rejected') } },
|
|
2598
|
+
React.createElement('h3', { id: 'super-code-consent-title' }, t('connection.requestTitle')),
|
|
2599
|
+
React.createElement('p', null, t('connection.requestHint')),
|
|
2600
|
+
React.createElement('dl', { className: 'dsh-super-code-directory-request' },
|
|
2601
|
+
...[['requestClient', current.client], ['requestPath', current.path], ['requestReason', current.reason]].map(([key, value]) => React.createElement('div', { key }, React.createElement('dt', null, t(`connection.${key}`)), React.createElement('dd', null, value)))),
|
|
2602
|
+
error && React.createElement('p', { role: 'alert' }, t('connection.requestError')),
|
|
2603
|
+
React.createElement('div', { className: 'dsh-super-code-confirm-actions' },
|
|
2604
|
+
React.createElement(ActionButton, { type: 'button', disabled: busy, 'data-consent-reject': true, onClick: () => resolve('rejected') }, t('connection.rejectDirectory')),
|
|
2605
|
+
React.createElement(ActionButton, { type: 'button', disabled: busy, className: 'dsh-super-code-primary-button', onClick: () => resolve('approved') }, t('connection.approveDirectory'))))
|
|
2606
|
+
}
|
|
2607
|
+
|
|
2608
|
+
function PresetSettings({ api, budgetApi, connectionApi, openSession = () => {}, t }) {
|
|
2609
|
+
const [tab, setTab] = useState('home')
|
|
1766
2610
|
const [status, setStatus] = useState(null), [customId, setCustomId] = useState(null)
|
|
1767
2611
|
const reinstall = status?.reinstallable ?? status?.state === 'installed'
|
|
1768
2612
|
const name = customId ?? (reinstall ? status.id : 'super-code')
|
|
@@ -1827,18 +2671,22 @@ function PresetSettings({ api, budgetApi, t }) {
|
|
|
1827
2671
|
}
|
|
1828
2672
|
const available = status && ['available', 'installed'].includes(status.state)
|
|
1829
2673
|
const versionState = status?.presetVersion ? status.presetVersion === status.pluginVersion ? 'match' : 'mismatch' : 'unknown'
|
|
2674
|
+
const [overviewRevision, setOverviewRevision] = useState(0)
|
|
2675
|
+
const onSaved = () => setOverviewRevision(value => value + 1)
|
|
2676
|
+
const tabs = ['home', 'preset', 'context', 'protection', 'connection']
|
|
1830
2677
|
return React.createElement('section', { className: 'dsh-super-code-settings' },
|
|
1831
2678
|
React.createElement('div', { className: 'dsh-super-code-settings-tabs', role: 'tablist', 'aria-label': t('settings.tabs'), onKeyDown: event => {
|
|
1832
2679
|
if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) return
|
|
1833
2680
|
event.preventDefault()
|
|
1834
|
-
const selected = event.key === 'Home' ?
|
|
2681
|
+
const selected = event.key === 'Home' ? tabs[0] : event.key === 'End' ? tabs[tabs.length - 1] : tabs[(tabs.indexOf(tab) + (event.key === 'ArrowRight' ? 1 : tabs.length - 1)) % tabs.length]
|
|
1835
2682
|
setTab(selected)
|
|
1836
2683
|
event.currentTarget.querySelector(`#super-code-tab-${selected}`)?.focus()
|
|
1837
|
-
} }, ...
|
|
2684
|
+
} }, ...tabs.map(value => React.createElement('button', { key: value, type: 'button', role: 'tab',
|
|
1838
2685
|
id: `super-code-tab-${value}`, 'aria-selected': tab === value, 'aria-controls': `super-code-panel-${value}`,
|
|
1839
2686
|
tabIndex: tab === value ? 0 : -1, onClick: () => setTab(value) }, t(`settings.${value}Tab`)))),
|
|
1840
2687
|
React.createElement('div', { className: 'dsh-super-code-settings-body' },
|
|
1841
|
-
React.createElement('div', { role: 'tabpanel', id: 'super-code-panel-
|
|
2688
|
+
React.createElement('div', { role: 'tabpanel', id: 'super-code-panel-home', 'aria-labelledby': 'super-code-tab-home', hidden: tab !== 'home' },
|
|
2689
|
+
React.createElement(SettingsHome, { budgetApi, connectionApi, t, active: tab === 'home', revision: overviewRevision, navigate: setTab },
|
|
1842
2690
|
React.createElement('header', { className: 'dsh-super-code-settings-header' },
|
|
1843
2691
|
React.createElement('span', { className: 'dsh-super-code-settings-mark', 'aria-hidden': true }, React.createElement(Glyph, { type: 'code' })),
|
|
1844
2692
|
React.createElement('div', { className: 'dsh-super-code-settings-head-text' },
|
|
@@ -1859,9 +2707,9 @@ function PresetSettings({ api, budgetApi, t }) {
|
|
|
1859
2707
|
React.createElement('div', null, React.createElement('dt', null, t('preset.presetVersion')), React.createElement('dd', null, status.state === 'missing' ? t('preset.versionMissing') : status.presetVersion || t('preset.versionUnknown')))),
|
|
1860
2708
|
status && status.state !== 'missing' && React.createElement('p', { className: 'dsh-super-code-version-hint', role: 'status', 'data-version-state': versionState },
|
|
1861
2709
|
t(versionState === 'match' ? 'preset.versionMatches' : versionState === 'mismatch' ? 'preset.versionMismatch' : 'preset.versionUnknownHint'),
|
|
1862
|
-
versionState === 'mismatch' && ` ${t(reinstall && status.authorable ? 'preset.versionReinstallHint' : 'preset.versionReloadHint')}`)),
|
|
2710
|
+
versionState === 'mismatch' && ` ${t(reinstall && status.authorable ? 'preset.versionReinstallHint' : 'preset.versionReloadHint')}`)))),
|
|
2711
|
+
React.createElement('div', { role: 'tabpanel', id: 'super-code-panel-preset', 'aria-labelledby': 'super-code-tab-preset', hidden: tab !== 'preset' },
|
|
1863
2712
|
status?.authorable && React.createElement('form', { onSubmit: event => { event.preventDefault(); submit() } },
|
|
1864
|
-
React.createElement('h3', null, t('preset.configuration')),
|
|
1865
2713
|
React.createElement('div', { className: 'dsh-super-code-settings-field' },
|
|
1866
2714
|
React.createElement('label', { htmlFor: 'super-code-display-name' }, t('preset.displayName')),
|
|
1867
2715
|
React.createElement('input', { id: 'super-code-display-name', value: displayName, maxLength: 120, required: true, disabled: busy, 'aria-describedby': 'super-code-display-name-hint', onChange: event => setCustomDisplayName(event.target.value) }),
|
|
@@ -1881,7 +2729,11 @@ function PresetSettings({ api, budgetApi, t }) {
|
|
|
1881
2729
|
status?.delivery === 'directory' && !status.authorable && React.createElement('p', { role: 'note' }, t('preset.readonly'))),
|
|
1882
2730
|
error && React.createElement('p', { role: 'alert' }, t(error))),
|
|
1883
2731
|
React.createElement('div', { role: 'tabpanel', id: 'super-code-panel-context', 'aria-labelledby': 'super-code-tab-context', hidden: tab !== 'context' },
|
|
1884
|
-
React.createElement(BudgetSettings, { api: budgetApi, t, active: tab === 'context' }))
|
|
2732
|
+
React.createElement(BudgetSettings, { api: budgetApi, t, active: tab === 'context', onSaved })),
|
|
2733
|
+
React.createElement('div', { role: 'tabpanel', id: 'super-code-panel-protection', 'aria-labelledby': 'super-code-tab-protection', hidden: tab !== 'protection' },
|
|
2734
|
+
React.createElement(BudgetSettings, { api: budgetApi, t, section: 'protection', active: tab === 'protection', onSaved })),
|
|
2735
|
+
React.createElement('div', { role: 'tabpanel', id: 'super-code-panel-connection', 'aria-labelledby': 'super-code-tab-connection', hidden: tab !== 'connection' },
|
|
2736
|
+
React.createElement(ConnectionSettings, { api: connectionApi, t, active: tab === 'connection', openSession, onSaved }))),
|
|
1885
2737
|
confirmation && React.createElement('dialog', { ref: dialog, className: 'dsh-super-code-confirm',
|
|
1886
2738
|
'aria-labelledby': 'super-code-confirm-title', 'aria-describedby': 'super-code-confirm-hint',
|
|
1887
2739
|
onKeyDown: event => { if (event.key === 'Escape') event.stopPropagation() },
|
|
@@ -1919,20 +2771,34 @@ function apply(ctx) {
|
|
|
1919
2771
|
// $mount creates a lifecycle effect on this same fiber; awaiting it from
|
|
1920
2772
|
// another effect would stall the namespace and the settings contribution.
|
|
1921
2773
|
void ctx.remote.$mount({ package: 'dsh-super-code', descriptors: [
|
|
1922
|
-
...presetRemoteContribution.descriptors, ...memoryRemoteContribution.descriptors, ...budgetRemoteContribution.descriptors,
|
|
2774
|
+
...presetRemoteContribution.descriptors, ...memoryRemoteContribution.descriptors, ...budgetRemoteContribution.descriptors, ...connectionRemoteContribution.descriptors,
|
|
1923
2775
|
] }).catch(error => console.warn('Super Code Remote:', error))
|
|
1924
2776
|
const memoryStates = new WeakMap(), workspaceStates = new WeakMap()
|
|
2777
|
+
const navigation = { openSession: () => {} }
|
|
2778
|
+
ctx.inject(['uiWorkspace'], ready => {
|
|
2779
|
+
navigation.openSession = id => {
|
|
2780
|
+
if (typeof ready.uiWorkspace.openSession === 'function') ready.uiWorkspace.openSession(id)
|
|
2781
|
+
else ready.sessions.open(id)
|
|
2782
|
+
}
|
|
2783
|
+
ready.effect(() => () => { navigation.openSession = () => {} })
|
|
2784
|
+
})
|
|
2785
|
+
ctx.inject(['remote.superCodeConnection'], ready => {
|
|
2786
|
+
ready.slots.inject('shell.overlay', () => ready.slots.register({
|
|
2787
|
+
name: 'shell.overlay', id: 'super-code-directory-consent', order: 180, locale: LOCALE_NS,
|
|
2788
|
+
inject: () => ({ api: ready.remote.superCodeConnection }),
|
|
2789
|
+
}, ConnectionConsent))
|
|
2790
|
+
})
|
|
1925
2791
|
// Locale already requires its host's preference service. Declare that same
|
|
1926
2792
|
// service here: Cordis rejects even optional reads without an injection.
|
|
1927
2793
|
const preferenceService = 'configForms' in ctx ? 'configForms' : 'settingsScope'
|
|
1928
|
-
ctx.inject(['remote.superCodePresets', 'remote.superCodeBudgets', preferenceService], ready => {
|
|
2794
|
+
ctx.inject(['remote.superCodePresets', 'remote.superCodeBudgets', 'remote.superCodeConnection', preferenceService], ready => {
|
|
1929
2795
|
const preferenceScope = preferenceService === 'configForms'
|
|
1930
2796
|
? ready.configForms.get('locale') : ready.settingsScope.bind({ namespace: 'locale' })
|
|
1931
2797
|
ready.effect(() => synchronizePresetDisplay(ready.remote.superCodePresets, locale,
|
|
1932
2798
|
() => window.location.reload(), error => console.warn('Super Code preset display:', error), preferenceScope), 'super-code: preset display')
|
|
1933
2799
|
ready.slots.inject('settings.section', () => ready.slots.register({
|
|
1934
2800
|
name: 'settings.section', id: 'super-code', order: 120, label: () => t('preset.title'), locale: LOCALE_NS,
|
|
1935
|
-
inject: () => ({ api: ready.remote.superCodePresets, budgetApi: ready.remote.superCodeBudgets }),
|
|
2801
|
+
inject: () => ({ api: ready.remote.superCodePresets, budgetApi: ready.remote.superCodeBudgets, connectionApi: ready.remote.superCodeConnection, openSession: id => navigation.openSession(id) }),
|
|
1936
2802
|
}, PresetSettings))
|
|
1937
2803
|
})
|
|
1938
2804
|
ctx.effect(() => {
|
|
@@ -2023,6 +2889,28 @@ function apply(ctx) {
|
|
|
2023
2889
|
.dsh-super-code-budget-group{display:flex;flex-direction:column;gap:16px;min-width:0}
|
|
2024
2890
|
.dsh-super-code-budget-group h3{margin:0;font-size:14px;font-weight:600}
|
|
2025
2891
|
.dsh-super-code-budget-exclusive{border-top:1px solid var(--sc-line);padding-top:24px}
|
|
2892
|
+
|
|
2893
|
+
.dsh-super-code-connection{display:grid;gap:18px}
|
|
2894
|
+
.dsh-super-code-connection form{display:grid;gap:16px}
|
|
2895
|
+
.dsh-super-code-connection-hint{margin:0;color:var(--sc-muted);font-size:12px;line-height:1.6}
|
|
2896
|
+
.dsh-super-code-connection-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px}
|
|
2897
|
+
.dsh-super-code-connection textarea,.dsh-super-code-connection-token{width:100%;box-sizing:border-box;border:1px solid var(--sc-line);border-radius:8px;background:var(--sc-bg);color:var(--sc-text);padding:9px 11px;font:12px/1.6 ui-monospace,monospace;resize:vertical}
|
|
2898
|
+
.dsh-super-code-connection-client{display:grid;gap:12px;border-top:1px solid var(--sc-line);padding-top:18px;min-width:0}
|
|
2899
|
+
.dsh-super-code-connection-client h3{margin:0;font-size:13px}
|
|
2900
|
+
.dsh-super-code-connection-client pre{white-space:pre-wrap;overflow-wrap:anywhere;font:12px/1.7 ui-monospace,monospace;background:var(--sc-surface);padding:12px;border-radius:8px;margin:0}
|
|
2901
|
+
.dsh-super-code-connection small{color:var(--sc-muted);font-size:12px;line-height:1.6}
|
|
2902
|
+
.dsh-super-code-connection .dsh-super-code-settings-actions{flex-wrap:wrap;gap:8px}
|
|
2903
|
+
.dsh-super-code-connection-switch{position:relative;display:inline-flex;align-items:center;gap:10px;cursor:pointer;font-size:13px;font-weight:500;width:fit-content}
|
|
2904
|
+
.dsh-super-code-connection-switch input[role=switch]{position:absolute;opacity:0;width:34px;height:20px;margin:0;padding:0;left:0;top:50%;transform:translateY(-50%);z-index:1;cursor:pointer}
|
|
2905
|
+
.dsh-super-code-connection-track{display:inline-flex;align-items:center;flex:none;width:34px;height:20px;border:1px solid var(--sc-line);border-radius:20px;padding:2px;background:var(--sc-surface);transition:background .15s}
|
|
2906
|
+
.dsh-super-code-connection-track i{display:block;width:14px;height:14px;border-radius:50%;background:var(--sc-muted);transition:transform .15s}
|
|
2907
|
+
.dsh-super-code-connection-switch input:checked+.dsh-super-code-connection-track{background:var(--sc-accent);border-color:var(--sc-accent)}
|
|
2908
|
+
.dsh-super-code-connection-switch input:checked+.dsh-super-code-connection-track i{transform:translateX(14px);background:#fff}
|
|
2909
|
+
.dsh-super-code-connection-switch input:focus-visible+.dsh-super-code-connection-track{outline:2px solid var(--sc-accent);outline-offset:3px}
|
|
2910
|
+
.dsh-super-code-connection-switch input:disabled+.dsh-super-code-connection-track{opacity:.5}
|
|
2911
|
+
.dsh-super-code-connection input[type=number]{appearance:textfield}
|
|
2912
|
+
.dsh-super-code-connection input::-webkit-inner-spin-button{appearance:none}
|
|
2913
|
+
|
|
2026
2914
|
.dsh-super-code-budget-switch{display:flex;align-items:center;gap:10px;font-size:13px;cursor:pointer}
|
|
2027
2915
|
.dsh-super-code-budget-switch input{width:30px;height:17px;flex:none;accent-color:var(--sc-active);cursor:pointer}
|
|
2028
2916
|
.dsh-super-code-budget-default{color:var(--sc-muted);font-weight:400;font-size:12px}
|
|
@@ -2037,6 +2925,60 @@ function apply(ctx) {
|
|
|
2037
2925
|
.dsh-super-code-settings [role=alert],.dsh-super-code-detail [role=alert],.dsh-super-code-tree-error{font-size:13px;color:var(--sc-error);overflow-wrap:anywhere}
|
|
2038
2926
|
@container(max-width:420px){.dsh-super-code-settings-header{padding-bottom:16px;margin-bottom:16px}.dsh-super-code-settings-body{padding:16px}.dsh-super-code-settings-summary{padding:12px}.dsh-super-code-settings-status-row{gap:8px}.dsh-super-code-preset-status{flex-basis:100%}.dsh-super-code-settings-form-actions>small{min-width:0;flex-basis:100%}}
|
|
2039
2927
|
|
|
2928
|
+
.dsh-super-code-session-counts{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px;margin:4px 0 0;padding-top:12px;border-top:1px solid var(--sc-line)}
|
|
2929
|
+
.dsh-super-code-session-counts dt{font-size:12px;color:var(--sc-muted)}
|
|
2930
|
+
.dsh-super-code-session-counts dd{font-size:22px;font-variant-numeric:tabular-nums;font-weight:600;margin:3px 0 0}
|
|
2931
|
+
.dsh-super-code-connection-activity{display:grid;gap:10px;border:1px solid var(--sc-line);border-radius:10px;padding:14px}
|
|
2932
|
+
.dsh-super-code-connection-activity h3{margin:0;font-size:13px;font-weight:600}
|
|
2933
|
+
.dsh-super-code-connection-activity h3:not(:first-child){margin-top:6px}
|
|
2934
|
+
.dsh-super-code-connection-activity h4{margin:0 0 6px;font-size:12px;color:var(--sc-muted);font-weight:500}
|
|
2935
|
+
.dsh-super-code-connection-activity p{margin:0;font-size:12px;color:var(--sc-muted)}
|
|
2936
|
+
.dsh-super-code-connection-activity button{border:1px solid var(--sc-line);border-radius:7px;background:var(--sc-bg);padding:4px 8px;font-size:12px}
|
|
2937
|
+
.dsh-super-code-connection-activity button:hover{background:var(--sc-hover)}
|
|
2938
|
+
/* Settings overview and directory consent use the same host theme aliases. */
|
|
2939
|
+
.dsh-super-code-settings-tabs{gap:20px;overflow-x:auto;white-space:nowrap}
|
|
2940
|
+
.dsh-super-code-home{display:grid;gap:12px}
|
|
2941
|
+
.dsh-super-code-home-category{font-size:11px;letter-spacing:.06em;color:var(--sc-muted);margin:12px 0 0}
|
|
2942
|
+
.dsh-super-code-home-card{display:grid;gap:12px;border:1px solid var(--sc-line);border-radius:10px;padding:14px}
|
|
2943
|
+
.dsh-super-code-home-heading{display:flex;justify-content:space-between;align-items:center;gap:12px}
|
|
2944
|
+
.dsh-super-code-home-heading strong{font-size:13px;font-weight:600}
|
|
2945
|
+
.dsh-super-code-home-card :is(p,small){margin:0;font-size:12px;color:var(--sc-muted)}
|
|
2946
|
+
.dsh-super-code-home-card h4{font-size:12px;font-weight:500;margin:6px 0 0}
|
|
2947
|
+
.dsh-super-code-home-card button,.dsh-super-code-home>.dsh-super-code-settings-actions button{border:1px solid var(--sc-line);border-radius:7px;background:var(--sc-bg);padding:4px 8px;font-size:12px}
|
|
2948
|
+
.dsh-super-code-home-card button:hover{background:var(--sc-hover)}
|
|
2949
|
+
.dsh-super-code-connection-list{list-style:none;padding:0;margin:0;max-height:280px;overflow:auto}
|
|
2950
|
+
.dsh-super-code-connection-list li{display:flex;align-items:center;gap:10px;padding:9px 0;border-bottom:1px solid var(--sc-line)}
|
|
2951
|
+
.dsh-super-code-connection-list li:last-child{border:0}
|
|
2952
|
+
.dsh-super-code-connection-list li>span{display:grid;gap:2px;flex:1;min-width:0;overflow-wrap:anywhere}
|
|
2953
|
+
.dsh-super-code-connection-list strong{font-size:12px;font-weight:500}
|
|
2954
|
+
.dsh-super-code-connection-list code{font:10px/1.5 var(--sc-mono);color:var(--sc-muted)}
|
|
2955
|
+
.dsh-super-code-connection-list button{flex:none}
|
|
2956
|
+
.dsh-super-code-home .dsh-super-code-settings-header{padding-bottom:12px;margin-bottom:0}
|
|
2957
|
+
.dsh-super-code-directory-fieldset{border:0;padding:0;margin:0;min-width:0;display:grid;gap:10px}
|
|
2958
|
+
.dsh-super-code-directory-fieldset legend{font-size:13px;font-weight:500;margin-bottom:10px}
|
|
2959
|
+
.dsh-super-code-directory-list{border:1px solid var(--sc-line);border-radius:9px;max-height:330px;overflow:auto}
|
|
2960
|
+
.dsh-super-code-directory-list>p{margin:0;padding:18px;color:var(--sc-muted);font-size:12px}
|
|
2961
|
+
.dsh-super-code-directory-row{display:flex;align-items:center;gap:8px;padding:10px 12px}
|
|
2962
|
+
.dsh-super-code-directory-row+.dsh-super-code-directory-row{border-top:1px solid var(--sc-line)}
|
|
2963
|
+
.dsh-super-code-directory-row label{display:flex;align-items:center;flex:1;min-width:0;gap:10px;cursor:pointer}
|
|
2964
|
+
.dsh-super-code-directory-row input[type=checkbox]{appearance:none;width:16px;height:16px;flex:none;border:1px solid var(--sc-line);padding:0;border-radius:4px;background:var(--sc-bg);cursor:pointer}
|
|
2965
|
+
.dsh-super-code-directory-row input[type=checkbox]:checked{background:var(--sc-accent);border-color:var(--sc-accent);box-shadow:inset 0 0 0 3px var(--sc-bg)}
|
|
2966
|
+
.dsh-super-code-directory-row input:disabled{opacity:.5;cursor:default}
|
|
2967
|
+
.dsh-super-code-directory-row label>span{display:grid;min-width:0;gap:1px}
|
|
2968
|
+
.dsh-super-code-directory-row strong{font-size:12px;font-weight:500}
|
|
2969
|
+
.dsh-super-code-directory-row code{font:11px/1.6 var(--sc-mono);overflow-wrap:anywhere}
|
|
2970
|
+
.dsh-super-code-directory-row small[data-unavailable=true]{color:var(--sc-warning)}
|
|
2971
|
+
.dsh-super-code-directory-row>button{font-size:11px;border:0;background:none;padding:5px;color:var(--sc-muted)}
|
|
2972
|
+
.dsh-super-code-directory-crumbs{display:flex;flex-wrap:wrap;gap:4px;margin:12px 0}
|
|
2973
|
+
.dsh-super-code-directory-crumbs button{font-size:11px}
|
|
2974
|
+
.dsh-super-code-directory-path{display:block;overflow-wrap:anywhere;font-size:12px;margin-bottom:10px}
|
|
2975
|
+
.dsh-super-code-directory-browser{display:grid;max-height:320px;overflow:auto;gap:2px}
|
|
2976
|
+
.dsh-super-code-directory-browser button{display:flex;align-items:center;justify-content:flex-start;gap:8px;border:0;text-align:left;overflow-wrap:anywhere}
|
|
2977
|
+
.dsh-super-code-directory-request{display:grid;gap:12px;padding:14px;border:1px solid var(--sc-line);border-radius:9px;background:var(--sc-surface)}
|
|
2978
|
+
.dsh-super-code-directory-request dt{font-size:11px;color:var(--sc-muted);margin-bottom:3px}
|
|
2979
|
+
.dsh-super-code-directory-request dd{font-size:13px;line-height:1.6;overflow-wrap:anywhere;white-space:pre-wrap;margin:0}
|
|
2980
|
+
.dsh-super-code-directory-request>div:nth-child(2) dd{font-family:var(--sc-mono);font-size:12px}
|
|
2981
|
+
|
|
2040
2982
|
/* Confirmation. */
|
|
2041
2983
|
.dsh-super-code-confirm{width:min(520px,calc(100vw - 32px));max-height:calc(100dvh - 48px);overflow:auto;border:1px solid var(--sc-line);border-radius:16px;padding:24px;background:var(--sc-bg);box-shadow:0 12px 48px #0003}
|
|
2042
2984
|
.dsh-super-code-confirm::backdrop{background:#10182066}
|
|
@@ -2054,7 +2996,7 @@ function apply(ctx) {
|
|
|
2054
2996
|
|
|
2055
2997
|
/* Exactly one section fills the pane; the other keeps its clickable heading. */
|
|
2056
2998
|
.dsh-super-code-workspace{height:100%;min-height:0;overflow:auto;background:var(--sc-bg)}
|
|
2057
|
-
.dsh-super-code-workspace-layout{display:grid;height:100%;min-height:
|
|
2999
|
+
.dsh-super-code-workspace-layout{display:grid;height:100%;min-height:368px;min-width:0}
|
|
2058
3000
|
.dsh-super-code-workspace-collapsed{display:flex;align-items:center;border-block:1px solid var(--sc-line);background:var(--sc-bg);padding:0 12px}
|
|
2059
3001
|
.dsh-super-code-section-toggle{display:flex;align-items:center;gap:6px;flex:1;min-width:0;width:100%;min-height:36px;border:0;border-radius:6px;background:transparent;padding:6px 8px;text-align:left}
|
|
2060
3002
|
.dsh-super-code-section-toggle>.dsh-super-code-tree-count{margin-left:auto}
|
|
@@ -2065,6 +3007,66 @@ function apply(ctx) {
|
|
|
2065
3007
|
.dsh-super-code-section-toggle:hover{background:var(--sc-hover)}
|
|
2066
3008
|
.dsh-super-code-workspace-collapsed>.dsh-super-code-section-toggle{width:100%;height:100%}
|
|
2067
3009
|
|
|
3010
|
+
|
|
3011
|
+
/* Read-only journal feedback, independent of the graph camera and model context. */
|
|
3012
|
+
.dsh-super-code-feedback{height:100%;min-height:0;min-width:0;display:flex;flex-direction:column}
|
|
3013
|
+
.dsh-super-code-feedback>header{display:flex;align-items:center;gap:8px;padding:4px 12px;border-bottom:1px solid var(--sc-line)}
|
|
3014
|
+
.dsh-super-code-feedback-scroll{flex:1;min-height:0;overflow:auto;padding:8px 12px 12px;overscroll-behavior:contain;overflow-anchor:none}
|
|
3015
|
+
.dsh-super-code-feedback-list{position:relative;list-style:none;padding:0;margin:0}
|
|
3016
|
+
.dsh-super-code-feedback-list>li{position:absolute;top:0;left:0;width:100%;display:flow-root;min-width:0;font-size:12px;line-height:20px;overflow-wrap:anywhere}
|
|
3017
|
+
.dsh-super-code-feedback-list>li[data-feedback-agent]:not([data-feedback-kind=task]):before{content:"";position:absolute;left:calc(var(--sc-feedback-indent) - 10px);top:0;bottom:0;border-left:1px solid var(--sc-line)}
|
|
3018
|
+
.dsh-super-code-feedback-task-heading{display:flex;flex-wrap:wrap;align-items:flex-start;gap:2px 6px;padding:8px 4px 6px}
|
|
3019
|
+
.dsh-super-code-feedback-heading{flex:1;min-width:0}
|
|
3020
|
+
.dsh-super-code-feedback-heading h3{overflow-wrap:anywhere;font-size:13px;line-height:20px;font-weight:600;margin:0}
|
|
3021
|
+
.dsh-super-code-feedback small{color:var(--sc-muted);font-size:11px;line-height:18px}
|
|
3022
|
+
.dsh-super-code-feedback-meta{display:flex;align-items:center;gap:7px;min-width:0;margin-top:1px}
|
|
3023
|
+
.dsh-super-code-feedback-meta>small{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
3024
|
+
.dsh-super-code-feedback-status{display:flex;align-items:center;gap:5px;flex:none;font-size:11px;line-height:18px;color:var(--sc-muted)}
|
|
3025
|
+
.dsh-super-code-feedback-status:before{content:"";width:3px;height:3px;border-radius:50%;background:var(--sc-faint)}
|
|
3026
|
+
.dsh-super-code-feedback-status[data-tone=active]{color:var(--sc-accent)}
|
|
3027
|
+
.dsh-super-code-feedback-status[data-tone=error]{color:var(--sc-error)}
|
|
3028
|
+
.dsh-super-code-feedback-count{flex:none;font-size:11px;font-variant-numeric:tabular-nums;line-height:20px;color:var(--sc-muted);padding-inline:4px}
|
|
3029
|
+
.dsh-super-code-feedback-muted{font-size:11px;color:var(--sc-muted);line-height:18px;margin:3px 0;white-space:pre-wrap}
|
|
3030
|
+
.dsh-super-code-feedback-text{margin:3px 0;white-space:pre-wrap}
|
|
3031
|
+
.dsh-super-code-feedback-text>small{display:block}
|
|
3032
|
+
.dsh-super-code-feedback progress{display:block;flex-basis:100%;width:100%;height:2px;accent-color:var(--sc-accent);border:0;border-radius:8px;overflow:hidden;background:var(--sc-line);margin:4px 0 0;opacity:.8}
|
|
3033
|
+
.dsh-super-code-feedback progress::-webkit-progress-bar{background:var(--sc-line)}
|
|
3034
|
+
.dsh-super-code-feedback progress::-webkit-progress-value{background:var(--sc-accent);transition:width .3s ease}
|
|
3035
|
+
.dsh-super-code-feedback progress::-moz-progress-bar{background:var(--sc-accent)}
|
|
3036
|
+
.dsh-super-code-feedback-step{display:flex;align-items:flex-start;gap:7px;min-height:26px;padding:3px 5px;margin:1px 0;border-radius:5px;line-height:20px;transition:background .15s,color .15s}
|
|
3037
|
+
.dsh-super-code-feedback-step>span:last-child{flex:1;min-width:0;overflow-wrap:anywhere}
|
|
3038
|
+
.dsh-super-code-feedback-step[data-step-state=completed]{color:var(--sc-muted)}
|
|
3039
|
+
.dsh-super-code-feedback-step[data-step-state=completed]>span:last-child{text-decoration:line-through;text-decoration-color:color-mix(in srgb,var(--sc-muted) 40%,transparent)}
|
|
3040
|
+
.dsh-super-code-feedback-step[data-step-state=pending]{color:var(--sc-muted)}
|
|
3041
|
+
.dsh-super-code-feedback-step[data-step-state=in_progress]{background:color-mix(in srgb,var(--sc-accent) 7%,transparent);box-shadow:inset 2px 0 color-mix(in srgb,var(--sc-accent) 55%,transparent);color:var(--sc-text)}
|
|
3042
|
+
.dsh-super-code-feedback-mark{position:relative;display:inline-flex;align-items:center;justify-content:center;flex:none;width:14px;height:14px;margin-top:3px;color:var(--sc-muted);font-size:11px;line-height:14px}
|
|
3043
|
+
.dsh-super-code-feedback-mark>svg{width:14px;height:14px}
|
|
3044
|
+
.dsh-super-code-feedback-mark.pending:before,.dsh-super-code-feedback-mark.idle:before,.dsh-super-code-feedback-mark.unavailable:before{content:"";width:7px;height:7px;border:1px solid var(--sc-faint);border-radius:50%}
|
|
3045
|
+
.dsh-super-code-feedback-mark.active:before{content:"";position:absolute;inset:2px;border:1.5px solid color-mix(in srgb,var(--sc-accent) 24%,transparent);border-top-color:var(--sc-accent);border-right-color:var(--sc-accent);border-radius:50%;animation:sc-feedback-spin .9s linear infinite}
|
|
3046
|
+
.dsh-super-code-feedback-mark.success{color:var(--sc-success)}
|
|
3047
|
+
.dsh-super-code-feedback-mark.error{color:var(--sc-error);border:1px solid currentColor;border-radius:50%;transform:scale(.8)}
|
|
3048
|
+
[data-feedback-live=false] .dsh-super-code-feedback-step .dsh-super-code-feedback-mark.active:before{animation:none}
|
|
3049
|
+
@keyframes sc-feedback-spin{to{transform:rotate(360deg)}}
|
|
3050
|
+
@media(prefers-reduced-motion:reduce){.dsh-super-code-feedback-mark.active:before{animation:none!important}}
|
|
3051
|
+
.dsh-super-code-feedback-activity{display:flex;gap:7px;align-items:flex-start;min-height:28px;padding:4px 0;line-height:20px}
|
|
3052
|
+
.dsh-super-code-feedback-activity-text{min-width:0;flex:1;overflow-wrap:anywhere;white-space:pre-wrap}
|
|
3053
|
+
.dsh-super-code-feedback-activity>small{flex:none;font-size:10px;line-height:20px}
|
|
3054
|
+
.dsh-super-code-feedback-tool{max-width:84px;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}
|
|
3055
|
+
.dsh-super-code-feedback-list>li[data-feedback-kind=open]{padding-bottom:8px}
|
|
3056
|
+
.dsh-super-code-feedback-open{display:inline-flex;align-items:center;gap:4px;min-height:24px;border:0;border-radius:4px;background:transparent;color:var(--sc-muted);font:inherit;font-size:11px;padding:2px 4px}
|
|
3057
|
+
.dsh-super-code-feedback-open>.dsh-super-code-glyph{width:12px;height:12px;transition:transform .15s}
|
|
3058
|
+
.dsh-super-code-feedback-open:hover{color:var(--sc-accent);background:var(--sc-hover)}
|
|
3059
|
+
.dsh-super-code-feedback-open:hover>.dsh-super-code-glyph{transform:translateX(2px)}
|
|
3060
|
+
.dsh-super-code-feedback-files-heading{display:flex;align-items:center;gap:6px;font-size:12px;font-weight:600;margin:6px 0 0;padding:10px 0 3px;border-top:1px solid var(--sc-line)}
|
|
3061
|
+
.dsh-super-code-feedback-files-heading>span{font-size:11px;font-weight:400;color:var(--sc-muted);font-variant-numeric:tabular-nums}
|
|
3062
|
+
.dsh-super-code-feedback-file{display:flex;align-items:center;gap:7px;width:100%;min-height:30px;padding:4px;border:0;border-radius:5px;background:transparent;color:var(--sc-text);text-align:left;min-width:0;font:inherit;cursor:pointer}
|
|
3063
|
+
.dsh-super-code-feedback-file:hover{background:var(--sc-hover)}
|
|
3064
|
+
.dsh-super-code-feedback-file>.dsh-super-code-glyph{flex:none;width:14px;height:14px;color:var(--sc-muted)}
|
|
3065
|
+
.dsh-super-code-feedback-file strong{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;font-weight:400}
|
|
3066
|
+
.dsh-super-code-feedback-file small{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
3067
|
+
.dsh-super-code-feedback-file-operation{flex:none;margin-left:auto;font-size:10px;color:var(--sc-success)}
|
|
3068
|
+
.dsh-super-code-feedback button:focus-visible,.dsh-super-code-feedback-scroll:focus-visible{outline:2px solid var(--sc-accent);outline-offset:-2px}
|
|
3069
|
+
|
|
2068
3070
|
/* Memory library: geometry stays at 76px per row. */
|
|
2069
3071
|
.dsh-super-code-memory{height:100%;min-height:0;display:flex;flex-direction:column;container-type:inline-size}
|
|
2070
3072
|
.dsh-super-code-memory>header{padding:16px;border-bottom:1px solid var(--sc-line);flex:none}
|
|
@@ -2404,5 +3406,5 @@ function apply(ctx) {
|
|
|
2404
3406
|
ctx.effect(() => disposeMemoryType, 'super-code: memory library tab')
|
|
2405
3407
|
}
|
|
2406
3408
|
|
|
2407
|
-
return { inject, apply, budgetDraft, budgetConfiguration, synchronizePresetDisplay, agentSessionState, buildAgentView, visibleAgentRows, agentTreeDisplay, agentTreeTotals, agentTreeLayout, agentExecutionStatus, agentDetailAddress, agentDetailTarget, agentPath, detailRecord, createAgentInspector, agentHistoryRange, agentHistoryRows, agentHistoryWindow, memoryLibraryAddress, memoryLibraryTarget, memoryLibraryNavigation, memoryLibraryWindow, createMemoryEditor }
|
|
3409
|
+
return { inject, apply, taskFeedbackRows, feedbackStatus, feedbackListItems, feedbackListLayout, feedbackListIndex, feedbackListWindow, feedbackListAnchor, connectionSessions, createConnectionAutosave, connectionDraft, connectionConfiguration, budgetDraft, budgetConfiguration, synchronizePresetDisplay, agentSessionState, buildAgentView, visibleAgentRows, agentTreeDisplay, agentTreeTotals, agentTreeLayout, agentExecutionStatus, agentDetailAddress, agentDetailTarget, agentPath, detailRecord, createAgentInspector, agentHistoryRange, agentHistoryRows, agentHistoryWindow, memoryLibraryAddress, memoryLibraryTarget, memoryLibraryNavigation, memoryLibraryWindow, createMemoryEditor }
|
|
2408
3410
|
} })
|