dsh-super-code 0.3.7 → 0.3.9
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.en.md +144 -0
- package/README.md +89 -180
- package/client/index.js +1022 -181
- package/cordis.patch.yml +1 -1
- package/docs/Acknowledgements.md +38 -67
- package/docs/Changelog.md +160 -79
- package/docs/Compatibility.md +98 -248
- package/docs/EvaluationResults.md +165 -155
- package/docs/UsageGuide.md +210 -172
- package/docs/en/Acknowledgements.md +76 -0
- package/docs/en/Changelog.md +160 -0
- package/docs/en/Compatibility.md +146 -0
- package/docs/en/EvaluationResults.md +224 -0
- package/docs/en/UsageGuide.md +304 -0
- package/docs/images/agent-tree-preview.png +0 -0
- package/docs/images/agent-tree.png +0 -0
- package/docs/images/install-tgz-preview.png +0 -0
- package/docs/images/install-tgz.png +0 -0
- package/docs/images/preset-settings-preview.png +0 -0
- package/docs/images/preset-settings.png +0 -0
- package/docs/images/task-status-preview.png +0 -0
- package/docs/images/task-status.png +0 -0
- package/lib/connection/approvals.d.ts +21 -0
- package/lib/connection/approvals.d.ts.map +1 -0
- package/lib/connection/approvals.js +83 -0
- package/lib/connection/approvals.js.map +1 -0
- package/lib/connection/client.d.ts +8 -0
- package/lib/connection/client.d.ts.map +1 -0
- package/lib/connection/client.js +34 -0
- package/lib/connection/client.js.map +1 -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/guidance.d.ts +10 -0
- package/lib/connection/guidance.d.ts.map +1 -0
- package/lib/connection/guidance.js +27 -0
- package/lib/connection/guidance.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 +342 -0
- package/lib/connection/http.js.map +1 -0
- package/lib/connection/service.d.ts +59 -0
- package/lib/connection/service.d.ts.map +1 -0
- package/lib/connection/service.js +176 -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 +141 -0
- package/lib/connection/store.js.map +1 -0
- package/lib/connection/tasks.d.ts +124 -0
- package/lib/connection/tasks.d.ts.map +1 -0
- package/lib/connection/tasks.js +393 -0
- package/lib/connection/tasks.js.map +1 -0
- package/lib/connection/types.d.ts +159 -0
- package/lib/connection/types.d.ts.map +1 -0
- package/lib/connection/types.js +23 -0
- package/lib/connection/types.js.map +1 -0
- package/lib/connection-cli.d.ts +3 -0
- package/lib/connection-cli.d.ts.map +1 -0
- package/lib/connection-cli.js +40 -0
- package/lib/connection-cli.js.map +1 -0
- package/lib/dsh/connection-approval.d.ts +5 -0
- package/lib/dsh/connection-approval.d.ts.map +1 -0
- package/lib/dsh/connection-approval.js +72 -0
- package/lib/dsh/connection-approval.js.map +1 -0
- package/lib/dsh/connection-history.d.ts +6 -0
- package/lib/dsh/connection-history.d.ts.map +1 -0
- package/lib/dsh/connection-history.js +52 -0
- package/lib/dsh/connection-history.js.map +1 -0
- package/lib/dsh/connection-host.d.ts +114 -0
- package/lib/dsh/connection-host.d.ts.map +1 -0
- package/lib/dsh/connection-host.js +208 -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 +186 -0
- package/lib/dsh/connection.js.map +1 -0
- package/lib/dsh/index.d.ts.map +1 -1
- package/lib/dsh/index.js +2 -0
- package/lib/dsh/index.js.map +1 -1
- package/package.json +14 -6
- package/presets/super-code/agent.cordis.yml +1 -1
package/client/index.js
CHANGED
|
@@ -149,7 +149,11 @@ function Glyph({ type }) {
|
|
|
149
149
|
arrow: 'M3 10h13m-5-5 5 5-5 5',
|
|
150
150
|
fit: 'M3 7V3h4m6 0h4v4m0 6v4h-4m-6 0H3v-4',
|
|
151
151
|
history: 'M3 4v5h5M3 9a7 7 0 1 1 2 6m5-10v5l3 2',
|
|
152
|
-
organize: 'M8 2h4v4H8zM2 14h4v4H2zm12 0h4v4h-4zM10 6v4m-6 4v-4h12v4'
|
|
152
|
+
organize: 'M8 2h4v4H8zM2 14h4v4H2zm12 0h4v4h-4zM10 6v4m-6 4v-4h12v4',
|
|
153
|
+
info: 'M10 9v5m0-8h0M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16Z',
|
|
154
|
+
shield: 'M10 2 3 5v5c0 4 3 7 7 8 4-1 7-4 7-8V5l-7-3Z',
|
|
155
|
+
plug: 'M7 2v4m6-4v4M5 6h10v3a5 5 0 0 1-10 0V6Zm5 8v4',
|
|
156
|
+
check: 'm4 10 4 4 8-8'
|
|
153
157
|
}
|
|
154
158
|
return React.createElement('svg', { className: 'dsh-super-code-glyph', viewBox: type === 'code' ? '0 0 24 20' : '0 0 20 20', fill: 'none', stroke: 'currentColor', strokeWidth: 1.5, strokeLinecap: 'round', strokeLinejoin: 'round', 'aria-hidden': true }, React.createElement('path', { d: paths[type] || paths.code }))
|
|
155
159
|
}
|
|
@@ -256,7 +260,9 @@ const ActionButton = React.forwardRef(function ActionButton({ children, title, .
|
|
|
256
260
|
|
|
257
261
|
const LOCALE_NS = 'dshSuperCode'
|
|
258
262
|
const zh = {
|
|
259
|
-
'feedback.title': '
|
|
263
|
+
'feedback.title': '任务',
|
|
264
|
+
'feedback.tools': '工具调用汇总',
|
|
265
|
+
'feedback.toolCount': '{tool} 成功 {ok} 次,失败 {failed} 次',
|
|
260
266
|
'feedback.refresh': '刷新任务状态',
|
|
261
267
|
'feedback.noGoal': '尚无目标记录',
|
|
262
268
|
'feedback.source.record': '任务记录',
|
|
@@ -292,18 +298,61 @@ const zh = {
|
|
|
292
298
|
'feedback.agentsPartial': '任务条目超过显示上限,可在动作执行中查看其他 Agent。',
|
|
293
299
|
'status.blocked': '受阻',
|
|
294
300
|
|
|
295
|
-
'settings.
|
|
301
|
+
'settings.homeTab': '首页', 'settings.presetTab': '预设配置', 'settings.protectionTab': '删除保护',
|
|
302
|
+
"connection.requestTitle": "请求访问工作目录",
|
|
303
|
+
"connection.requestHint": "外部 MCP 客户端申请访问以下目录及其子目录。同意后会加入允许列表,后续委派仍使用 DSH 的权限设置。",
|
|
304
|
+
"connection.requestClient": "请求客户端",
|
|
305
|
+
"connection.requestPath": "目录",
|
|
306
|
+
"connection.requestReason": "申请理由",
|
|
307
|
+
"connection.requestError": "操作失败,目录可能已变化或设置无法保存。请重试或拒绝。",
|
|
308
|
+
"connection.approveDirectory": "允许访问",
|
|
309
|
+
"connection.rejectDirectory": "拒绝",
|
|
310
|
+
"connection.error.directory-limit": "最多允许选择 32 个目录。",
|
|
311
|
+
"connection.error.picker-unavailable": "无法打开目录选择器,请重新读取后重试。",
|
|
312
|
+
'home.configure': '配置', 'home.managePreset': '配置预设', 'home.details': '打开{title}详情', 'home.activeSessions': '{count} 个活跃对话',
|
|
313
|
+
'preset.versionBadge': '版本一致',
|
|
314
|
+
'home.connectedSessions': '正在连接的对话', 'home.historySessions': '历史对话',
|
|
315
|
+
'home.connections': '已连接客户端', 'home.noConnections': '暂无客户端连接', 'home.sessions': '关联会话', 'home.noSessions': '暂无关联会话',
|
|
316
|
+
'home.disconnect': '断开', 'home.openSession': '打开会话', 'home.connected': '已连接', 'home.enabled': '已开启', 'home.disabled': '已关闭',
|
|
317
|
+
'home.mcp': 'MCP 服务', 'home.connectionCount': '{count} / {max} 个连接', 'home.setupConnection': '配置并启用',
|
|
318
|
+
'home.lastSeen': '最近活动', 'home.refresh': '刷新概览', 'home.error': '状态读取或操作失败,请刷新后重试。',
|
|
319
|
+
'connection.maxConnections': '最大连接数', 'connection.connectionsHint': '按 MCP 客户端会话计数。断开或空闲 10 分钟后释放连接;已接收任务继续执行。',
|
|
320
|
+
'connection.allowNewConversations': '允许 MCP 新增对话', 'connection.allowNewConversationsHint': '关闭后不会创建新的 DSH 对话;MCP 仍可查看并继续已有对话。',
|
|
321
|
+
'connection.allowCallerApproval': '允许调用方审批', 'connection.allowCallerApprovalHint': '默认关闭。开启后,由 MCP 调用方批准或拒绝委派任务的单次操作;无法处理或超时后回到 DSH。新增目录仍需在 DSH 同意。',
|
|
322
|
+
'connection.addDirectory': '添加目录', 'connection.noDirectories': '暂无目录,请通过文件管理器添加。',
|
|
323
|
+
'connection.workspaceDirectory': 'DSH 工作区', 'connection.customDirectory': '添加的目录', 'connection.directoryUnavailable': '目录不可用',
|
|
324
|
+
'connection.removeDirectory': '移除目录', 'connection.directoryRefresh': '刷新目录',
|
|
325
|
+
'connection.directoryBrowser': '选择目录', 'connection.selectDirectory': '选择此目录', 'connection.parentDirectory': '上一级',
|
|
326
|
+
'connection.emptyDirectory': '没有子目录', 'connection.browserError': '无法读取此目录,请重试或返回上一级。',
|
|
327
|
+
'connection.directoryLimit': '最多允许选择 32 个目录。',
|
|
328
|
+
'taskStatus.starting': '正在启动', 'taskStatus.running': '执行中', 'taskStatus.waiting_confirmation': '等待确认',
|
|
329
|
+
'taskStatus.cancelling': '正在取消', 'taskStatus.finished': '已结束', 'taskStatus.failed': '失败',
|
|
330
|
+
'taskStatus.cancelled': '已取消', 'taskStatus.interrupted': '已中断', 'taskStatus.unavailable': '状态不可用',
|
|
331
|
+
'settings.connectionTab': '连接', 'connection.title': '连接', 'connection.enabled': '启用 MCP 连接',
|
|
332
|
+
'connection.state.disabled': '连接已关闭', 'connection.state.listening': '正在监听', 'connection.state.error': '连接不可用', 'connection.state.loading': '正在读取…',
|
|
333
|
+
'connection.scope': '允许外部客户端委派任务到当前 DSH。仅监听本机,使用当前模型、Super Code 预设和宿主权限。关闭连接不会停止已接收的任务。',
|
|
334
|
+
'connection.autosave': '修改后自动保存', 'connection.saving': '正在保存…',
|
|
335
|
+
'connection.port': '本机端口', 'connection.directories': '允许的工作目录',
|
|
336
|
+
'connection.directoriesHint': '勾选允许的目录,包含其子目录。每次打开设置会同步 DSH 工作区,保留已保存的选择。',
|
|
337
|
+
'connection.save': '保存连接', 'connection.cancel': '取消修改', 'connection.reload': '重新读取', 'connection.saved': '已保存连接设置。',
|
|
338
|
+
'connection.client': '连接 Prompt', 'connection.copyCommand': '复制连接 Prompt', 'connection.showToken': '显示凭证', 'connection.hideToken': '隐藏凭证', 'connection.copyToken': '复制凭证', 'connection.token': '连接凭证', 'connection.rotate': '更换凭证',
|
|
339
|
+
'connection.prompt': '请配置并连接 DSH Super Code MCP 服务:\n地址:{url}\n传输:Streamable HTTP\n认证:Authorization: Bearer <连接凭证>,凭证由我单独提供,请妥善保存在客户端。\nDSH 仅作为任务执行器,待开发或验收的对象是指定目录中的项目/候选产物;不要为排查委派任务而操作、重装、重启或改配提供服务的 DSH/NEXT。连接后先用 dsh_status 查看允许目录、连接额度和阻塞任务,再使用 dsh_sessions 按工作目录查看最近对话和状态,由调用方选择传 taskId 复用历史,或传 newConversation: true 新建;不要默认复用最新对话。如果 DSH 关闭了“允许 MCP 新增对话”,必须传已有 taskId 复用,不能创建新对话。调用 dsh_models 查看可用模型及思考强度,再在 dsh_run 或 dsh_send 中按需传 provider、model、reasoningEffort;省略时沿用 DSH 当前/默认选择。然后使用 dsh_run 委派任务、dsh_wait 查询状态或找回任务、dsh_send 补充要求、dsh_cancel 取消任务。使用稳定的 requestId 避免重复提交,只使用允许的绝对工作目录;新增目录请用 dsh_request_directory 提交申请,并等待用户在 DSH 中同意。通过 dsh_wait 的返回游标长轮询(waitMs: 30000);空输出、失败或截断时使用 dsh_read 分页读取公开对话和工具结果。忙碌/额度错误时查看阻塞任务、等待并保留原 requestId 和输入,不重复新建或清理其他连接。复用一个 MCP 会话,用完以 DELETE 释放;SDK 需先 transport.terminateSession() 再 client.close()。原生工具未加载时可用随包的 super-code-mcp 命令,凭证通过私密环境变量提供,不读取 DSH profile 搜寻凭证。dsh_status 返回 allowCallerApproval;默认关闭,权限确认请用户在 DSH 中处理。开启后通过 dsh_wait 接收交互审批请求或 callerApproval.pending,检查具体工具、参数和原因后,用 dsh_approve 传 taskId、approvalId、decision(approve/reject)作单次决定;目录申请仍需用户在 DSH 同意。不自动点击界面批准。finished 仅表示执行结束,仍需验证结果。',
|
|
340
|
+
'connection.tokenHint': '请将凭证安全保存在客户端。更换凭证后,旧凭证立即失效。',
|
|
341
|
+
'connection.rotated': '已更换凭证,请更新客户端。', 'connection.copied': '已复制。',
|
|
342
|
+
'connection.error.unavailable': '连接服务尚未就绪或当前配置目录不可写,请重新读取。', 'connection.error.invalid': '请检查端口、最大连接数和目录。启用连接至少需要一个有效目录。', 'connection.error.new-conversation-disabled': 'MCP 新增对话已关闭,请选择已有对话继续。',
|
|
343
|
+
'connection.error.conflict': '连接设置已在别处修改。草稿已保留,请重新读取。', 'connection.error.save-failed': '保存失败,草稿已保留。', 'connection.error.port-unavailable': '端口被占用或无法监听。请更换端口并保存。', 'connection.error.storage-unavailable': '任务状态保存失败,请检查配置目录。', 'connection.error.clipboard': '复制失败,请手动复制。',
|
|
344
|
+
'settings.contextTab': '上下文配置', 'settings.tabs': 'Super Code 配置',
|
|
296
345
|
'budget.common': '通用预算', 'budget.superCode': 'Super Code 专属预算',
|
|
297
346
|
'budget.applyToOfficialPresets': '同时应用到官方预设', 'budget.officialHint': '开启后,通用预算也用于标准、PTC、极简和创造模式;不包含下方的 Super Code 专属预算。',
|
|
298
347
|
'budget.superCodeHint': '仅用于 Super Code 的任务记录和任务记忆,始终不影响官方预设。',
|
|
299
348
|
'budget.defaultValue': '(默认:{value})', 'budget.off': '关闭', 'budget.managed': '宿主管理',
|
|
300
349
|
'delete.title': '删除保护', 'delete.mode': '保护模式', 'delete.off': '关闭', 'delete.relaxed': '宽松', 'delete.strict': '严格', 'delete.official': '同时应用到官方预设',
|
|
301
350
|
'delete.hint.off': '不添加删除保护提示,不额外拦截命令。宿主原有权限检查仍然有效。',
|
|
302
|
-
'delete.hint.relaxed': '默认使用 trash
|
|
351
|
+
'delete.hint.relaxed': '默认使用 trash(脚本中也建议);用户明确要求时可用 rm,无需额外确认。',
|
|
303
352
|
'delete.hint.strict': '默认使用 trash,脚本也禁止直接使用 rm。识别到永久删除命令时,仅清理临时文件或构建产物可经用户逐次二次确认放行。',
|
|
304
353
|
'delete.officialHint': '默认仅保护 Super Code。此开关独立于预算的官方预设开关;保存后立即作用于后续命令。',
|
|
305
354
|
'budget.title': '上下文配置', 'budget.scope': '预算保存后对新建 Agent 生效,运行中的任务保留原配置。',
|
|
306
|
-
'budget.defaults': '
|
|
355
|
+
'budget.defaults': '留空沿用默认值,各项下方显示默认值;通用预算随宿主版本变化。恢复默认会清除这里的覆盖,回到插件加载配置;未配置时跟随宿主。',
|
|
307
356
|
'budget.context': '上下文压缩', 'budget.execution': '批量工具执行', 'budget.advanced': '更多预算',
|
|
308
357
|
'budget.pressureBasis': '压缩预算基准', 'budget.inherit': '跟随默认', 'budget.host': '宿主策略', 'budget.window': '完整上下文窗口',
|
|
309
358
|
'budget.thresholdRatio': '压缩触发比例(%)', 'budget.retainRatio': '最近原文保留比例(%)', 'budget.summaryMaxTokens': '摘要输出上限(Token)',
|
|
@@ -320,6 +369,9 @@ const zh = {
|
|
|
320
369
|
'budget.error.conflict': '预算已在别处修改。草稿已保留,请重新读取后再修改。', 'budget.error.read-only': '当前宿主的设置只读,无法保存。',
|
|
321
370
|
'budget.error.ptc-unavailable': '此宿主不支持覆盖 PTC 总执行时限。', 'budget.error.ptc-ceiling': '执行时限超过了宿主允许的上限。',
|
|
322
371
|
'budget.error.save-failed': '保存失败,草稿已保留,请重试。',
|
|
372
|
+
'budget.defaultHint': '默认:{value}', 'budget.error.range': '超出允许范围或格式不正确。',
|
|
373
|
+
'budget.resetQuestion': '清除这里的所有覆盖?', 'budget.resetConfirm': '恢复默认', 'delete.scope': '适用范围',
|
|
374
|
+
'save.idle': '修改后自动保存', 'save.saving': '正在保存…', 'save.saved': '已自动保存', 'save.error': '保存失败', 'save.invalid': '有字段需要修正,暂未保存', 'save.retry': '重试',
|
|
323
375
|
'memory.add': '新增记忆', 'memory.edit': '编辑', 'memory.cancel': '取消', 'memory.save': '保存', 'memory.delete': '删除', 'memory.deleting': '正在删除…', 'memory.saving': '正在保存…',
|
|
324
376
|
'memory.topic': '主题', 'memory.kind': '类型', 'memory.summaryLabel': '标题', 'memory.detailLabel': '内容', 'memory.manual': '手动维护',
|
|
325
377
|
'memory.topicHint': '字母、数字、点、下划线或短横线,最多 96 个字符。',
|
|
@@ -331,7 +383,7 @@ const zh = {
|
|
|
331
383
|
'detail.toolCall': '工具调用', 'detail.toolResult': '工具结果', 'detail.toolError': '工具失败',
|
|
332
384
|
'detail.cache': '缓存', 'detail.info': '详情', 'tree.countCompact': '{shown}/{total} · 运行 {running}',
|
|
333
385
|
'memory.loading': '正在读取记忆…', 'memory.topics': '知识主题', 'memory.allTopics': '全部主题', 'memory.related': '同主题知识', 'preset.configuration': '预设配置',
|
|
334
|
-
'memory.title': '
|
|
386
|
+
'memory.title': '记忆', 'memory.project': '当前项目', 'memory.global': '全局偏好', 'memory.search': '搜索主题或记忆',
|
|
335
387
|
'memory.description': '知识主题与条目',
|
|
336
388
|
'memory.empty': '还没有保存的记忆。需要长期保留时,直接告诉 Agent 即可。', 'memory.noMatches': '没有匹配的记忆',
|
|
337
389
|
'memory.selectSession': '先打开一个对话,查看它所在项目的记忆。', 'memory.error': '暂时无法读取记忆,请刷新重试。',
|
|
@@ -390,7 +442,9 @@ const zh = {
|
|
|
390
442
|
'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': '[图片]'
|
|
391
443
|
}
|
|
392
444
|
const en = {
|
|
393
|
-
'feedback.title': '
|
|
445
|
+
'feedback.title': 'Tasks',
|
|
446
|
+
'feedback.tools': 'Tool call summary',
|
|
447
|
+
'feedback.toolCount': '{tool}: {ok} succeeded, {failed} failed',
|
|
394
448
|
'feedback.refresh': 'Refresh task status',
|
|
395
449
|
'feedback.noGoal': 'No recorded objective',
|
|
396
450
|
'feedback.source.record': 'Recorded objective',
|
|
@@ -426,7 +480,50 @@ const en = {
|
|
|
426
480
|
'feedback.agentsPartial': 'The task list reached its limit; view other Agents in the execution tree.',
|
|
427
481
|
'status.blocked': 'Blocked',
|
|
428
482
|
|
|
429
|
-
'settings.
|
|
483
|
+
'settings.homeTab': 'Home', 'settings.presetTab': 'Preset configuration', 'settings.protectionTab': 'Deletion protection',
|
|
484
|
+
"connection.requestTitle": "Working directory access request",
|
|
485
|
+
"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.",
|
|
486
|
+
"connection.requestClient": "Requesting client",
|
|
487
|
+
"connection.requestPath": "Directory",
|
|
488
|
+
"connection.requestReason": "Reason",
|
|
489
|
+
"connection.requestError": "Could not save this decision. The directory may have changed or settings may be unwritable. Retry or reject.",
|
|
490
|
+
"connection.approveDirectory": "Allow access",
|
|
491
|
+
"connection.rejectDirectory": "Reject",
|
|
492
|
+
"connection.error.directory-limit": "Select up to 32 directories.",
|
|
493
|
+
"connection.error.picker-unavailable": "Could not open the directory picker. Reload and try again.",
|
|
494
|
+
'home.configure': 'Configure', 'home.managePreset': 'Configure preset', 'home.details': 'Open {title} details', 'home.activeSessions': '{count} active conversations',
|
|
495
|
+
'preset.versionBadge': 'Up to date',
|
|
496
|
+
'home.connectedSessions': 'Connected conversations', 'home.historySessions': 'Past conversations',
|
|
497
|
+
'home.connections': 'Connected clients', 'home.noConnections': 'No clients connected', 'home.sessions': 'Linked sessions', 'home.noSessions': 'No linked sessions',
|
|
498
|
+
'home.disconnect': 'Disconnect', 'home.openSession': 'Open session', 'home.connected': 'Connected', 'home.enabled': 'On', 'home.disabled': 'Off',
|
|
499
|
+
'home.mcp': 'MCP service', 'home.connectionCount': '{count} / {max} connections', 'home.setupConnection': 'Set up connection',
|
|
500
|
+
'home.lastSeen': 'Last activity', 'home.refresh': 'Refresh overview', 'home.error': 'Could not read or update the status. Refresh and try again.',
|
|
501
|
+
'connection.maxConnections': 'Maximum connections', 'connection.connectionsHint': 'Counts MCP client sessions. Disconnect or 10 minutes of inactivity releases a connection; accepted tasks continue.',
|
|
502
|
+
'connection.allowNewConversations': 'Allow MCP to create conversations', 'connection.allowNewConversationsHint': 'When off, MCP cannot create new DSH conversations; it can still inspect and continue existing ones.',
|
|
503
|
+
'connection.allowCallerApproval': 'Allow caller approval', 'connection.allowCallerApprovalHint': 'Off by default. MCP callers may approve or reject one-time task operations; unhandled or expired requests return to DSH. New directory access still requires consent in DSH.',
|
|
504
|
+
'connection.addDirectory': 'Add directory', 'connection.noDirectories': 'No directories. Add one with the directory picker.',
|
|
505
|
+
'connection.workspaceDirectory': 'DSH workspace', 'connection.customDirectory': 'Added directory', 'connection.directoryUnavailable': 'Directory unavailable',
|
|
506
|
+
'connection.removeDirectory': 'Remove directory', 'connection.directoryRefresh': 'Refresh directories',
|
|
507
|
+
'connection.directoryBrowser': 'Choose directory', 'connection.selectDirectory': 'Select this directory', 'connection.parentDirectory': 'Parent directory',
|
|
508
|
+
'connection.emptyDirectory': 'No subdirectories', 'connection.browserError': 'Could not read this directory. Retry or return to its parent.',
|
|
509
|
+
'connection.directoryLimit': 'Select up to 32 directories.',
|
|
510
|
+
'taskStatus.starting': 'Starting', 'taskStatus.running': 'Running', 'taskStatus.waiting_confirmation': 'Waiting for confirmation',
|
|
511
|
+
'taskStatus.cancelling': 'Cancelling', 'taskStatus.finished': 'Finished', 'taskStatus.failed': 'Failed',
|
|
512
|
+
'taskStatus.cancelled': 'Cancelled', 'taskStatus.interrupted': 'Interrupted', 'taskStatus.unavailable': 'Unavailable',
|
|
513
|
+
'settings.connectionTab': 'Connection', 'connection.title': 'Connection', 'connection.enabled': 'Enable MCP connection',
|
|
514
|
+
'connection.state.disabled': 'Connection off', 'connection.state.listening': 'Listening', 'connection.state.error': 'Connection unavailable', 'connection.state.loading': 'Loading…',
|
|
515
|
+
'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.',
|
|
516
|
+
'connection.autosave': 'Changes save automatically', 'connection.saving': 'Saving…',
|
|
517
|
+
'connection.port': 'Local port', 'connection.directories': 'Allowed working directories',
|
|
518
|
+
'connection.directoriesHint': 'Select allowed directories, including their subdirectories. Reopening settings refreshes DSH workspaces and retains saved selections.',
|
|
519
|
+
'connection.save': 'Save connection', 'connection.cancel': 'Cancel changes', 'connection.reload': 'Reload', 'connection.saved': 'Connection settings saved.',
|
|
520
|
+
'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',
|
|
521
|
+
'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.\nDSH is the task executor; the project/candidate in the chosen cwd is the target. Do not control, reinstall, restart or reconfigure the serving DSH/NEXT app to investigate delegated work. After connecting, call dsh_status for directory grants, capacity and blocking tasks, then dsh_sessions for recent conversations and statuses. 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. If DSH has “Allow MCP to create conversations” disabled, pass an existing taskId and never create a new conversation. Call dsh_models to inspect available models and reasoning efforts, then optionally pass provider, model and reasoningEffort to dsh_run or dsh_send; omit them to keep the current DSH model selection. 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. Long-poll dsh_wait with its returned cursor and waitMs: 30000. Use dsh_read to page through public messages/tool results when output is empty, failed or truncated. On busy/limit errors, inspect blockers, wait and retain the original requestId/input; do not repeatedly initialize or clear other connections. Reuse one MCP session and release it with DELETE when done; with the SDK call transport.terminateSession() before client.close(). If native tools are unavailable, use the packaged super-code-mcp command with private environment credentials rather than searching DSH profile files. Check allowCallerApproval in dsh_status (off by default). When off, ask the user to resolve permissions in DSH. When on, dsh_wait delivers interactive approvals or callerApproval.pending; inspect the tool, arguments and reason, then use dsh_approve with taskId, approvalId and decision (approve/reject) for that exact operation. Directory access still requires consent in DSH. Never click UI approval controls. A finished task still requires result verification.',
|
|
522
|
+
'connection.tokenHint': 'Store the token securely in the client. Rotation invalidates the previous token immediately.',
|
|
523
|
+
'connection.rotated': 'Token rotated. Update your client.', 'connection.copied': 'Copied.',
|
|
524
|
+
'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.', 'connection.error.new-conversation-disabled': 'MCP new conversations are disabled. Choose an existing conversation to continue.',
|
|
525
|
+
'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.',
|
|
526
|
+
'settings.contextTab': 'Context settings', 'settings.tabs': 'Super Code settings',
|
|
430
527
|
'budget.common': 'Common budgets', 'budget.superCode': 'Super Code budgets',
|
|
431
528
|
'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.',
|
|
432
529
|
'budget.superCodeHint': 'Task records and task memory for Super Code. These limits never affect official presets.',
|
|
@@ -437,7 +534,7 @@ const en = {
|
|
|
437
534
|
'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.',
|
|
438
535
|
'delete.officialHint': 'Protects Super Code by default. This switch is independent of official-preset budgets; saved changes apply to subsequent commands immediately.',
|
|
439
536
|
'budget.title': 'Context settings', 'budget.scope': 'Budget changes apply to new Agents. Running tasks keep their current settings.',
|
|
440
|
-
'budget.defaults': 'Leave fields blank to inherit defaults
|
|
537
|
+
'budget.defaults': 'Leave fields blank to inherit defaults, shown below each field; common budgets follow the host version. Reset clears overrides and restores the plugin configuration, or host defaults when unconfigured.',
|
|
441
538
|
'budget.context': 'Context compression', 'budget.execution': 'Batched tool execution', 'budget.advanced': 'More budgets',
|
|
442
539
|
'budget.pressureBasis': 'Compression budget basis', 'budget.inherit': 'Inherit default', 'budget.host': 'Host policy', 'budget.window': 'Full context window',
|
|
443
540
|
'budget.thresholdRatio': 'Compress at (%)', 'budget.retainRatio': 'Keep recent context (%)', 'budget.summaryMaxTokens': 'Summary output limit (tokens)',
|
|
@@ -454,6 +551,9 @@ const en = {
|
|
|
454
551
|
'budget.error.conflict': 'Budgets changed elsewhere. Your draft is retained; reload before editing again.', 'budget.error.read-only': 'This host has read-only settings.',
|
|
455
552
|
'budget.error.ptc-unavailable': 'This host cannot override PTC elapsed deadlines.', 'budget.error.ptc-ceiling': 'The deadline exceeds the host ceiling.',
|
|
456
553
|
'budget.error.save-failed': 'Save failed. Your draft is retained; try again.',
|
|
554
|
+
'budget.defaultHint': 'Default: {value}', 'budget.error.range': 'Out of range or not a valid number.',
|
|
555
|
+
'budget.resetQuestion': 'Clear all overrides here?', 'budget.resetConfirm': 'Reset', 'delete.scope': 'Scope',
|
|
556
|
+
'save.idle': 'Changes save automatically', 'save.saving': 'Saving…', 'save.saved': 'Saved automatically', 'save.error': 'Save failed', 'save.invalid': 'Fix the highlighted fields to save', 'save.retry': 'Retry',
|
|
457
557
|
'memory.add': 'Add memory', 'memory.edit': 'Edit', 'memory.cancel': 'Cancel', 'memory.save': 'Save', 'memory.delete': 'Delete', 'memory.deleting': 'Deleting…', 'memory.saving': 'Saving…',
|
|
458
558
|
'memory.topic': 'Topic', 'memory.kind': 'Type', 'memory.summaryLabel': 'Title', 'memory.detailLabel': 'Content', 'memory.manual': 'Manually maintained',
|
|
459
559
|
'memory.topicHint': 'Letters, digits, dots, underscores or hyphens; up to 96 characters.',
|
|
@@ -465,7 +565,7 @@ const en = {
|
|
|
465
565
|
'detail.toolCall': 'Tool call', 'detail.toolResult': 'Tool result', 'detail.toolError': 'Tool failed',
|
|
466
566
|
'detail.cache': 'Cache', 'detail.info': 'Details', 'tree.countCompact': '{shown}/{total} · Running {running}',
|
|
467
567
|
'memory.loading': 'Loading memories…', 'memory.topics': 'Knowledge topics', 'memory.allTopics': 'All topics', 'memory.related': 'More in this topic', 'preset.configuration': 'Preset configuration',
|
|
468
|
-
'memory.title': 'Memory
|
|
568
|
+
'memory.title': 'Memory', 'memory.project': 'This project', 'memory.global': 'Global preferences', 'memory.search': 'Search topics or memories',
|
|
469
569
|
'memory.description': 'Knowledge topics and entries',
|
|
470
570
|
'memory.empty': 'No saved memories yet. Tell the agent when something is worth keeping.', 'memory.noMatches': 'No matching memories',
|
|
471
571
|
'memory.selectSession': 'Open a conversation to view memory for its project.', 'memory.error': 'Memory is unavailable. Refresh to try again.',
|
|
@@ -884,19 +984,33 @@ function FeedbackMark({ tone, label }) {
|
|
|
884
984
|
React.createElement('path', { d: 'm3.5 8 3 3 6-6', strokeLinecap: 'round', strokeLinejoin: 'round' })),
|
|
885
985
|
tone === 'error' && React.createElement('span', { 'aria-hidden': true }, '!'))
|
|
886
986
|
}
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
987
|
+
/** Per-tool settled counts in first-use order; single calls stay in the execution log. */
|
|
988
|
+
function feedbackToolSummary(activities = []) {
|
|
989
|
+
const tools = new Map()
|
|
990
|
+
for (const { tool, status } of activities) {
|
|
991
|
+
const row = tools.get(tool) || { tool, succeeded: 0, failed: 0, running: false }
|
|
992
|
+
if (status === 'succeeded') row.succeeded++
|
|
993
|
+
else if (status === 'failed') row.failed++
|
|
994
|
+
else if (status === 'running') row.running = true
|
|
995
|
+
tools.set(tool, row)
|
|
996
|
+
}
|
|
997
|
+
return [...tools.values()]
|
|
998
|
+
}
|
|
999
|
+
function FeedbackTools({ tools, t }) {
|
|
1000
|
+
return React.createElement('ul', { className: 'dsh-super-code-feedback-tools', 'aria-label': text(t, 'feedback.tools') },
|
|
1001
|
+
tools.map(row => {
|
|
1002
|
+
const label = text(t, 'feedback.toolCount', { tool: row.tool, ok: row.succeeded, failed: row.failed })
|
|
1003
|
+
return React.createElement('li', { key: row.tool, title: label, 'aria-label': label, 'data-running': row.running || undefined },
|
|
1004
|
+
React.createElement('span', { 'aria-hidden': true }, row.tool),
|
|
1005
|
+
React.createElement('b', { className: 'dsh-super-code-tool-badge', 'data-tone': 'success', 'aria-hidden': true }, row.succeeded),
|
|
1006
|
+
React.createElement('b', { className: 'dsh-super-code-tool-badge', 'data-tone': 'warning', 'data-zero': row.failed === 0 || undefined, 'aria-hidden': true }, row.failed))
|
|
1007
|
+
}))
|
|
1008
|
+
}
|
|
1009
|
+
/** Flatten presentation rows only; tool calls fold into one per-task summary row. */
|
|
896
1010
|
function feedbackListItems(model, root, partial) {
|
|
897
1011
|
const items = []
|
|
898
1012
|
const add = (kind, key, data = {}, row) => items.push({ kind, key: JSON.stringify(key), row, ...data,
|
|
899
|
-
signature: JSON.stringify([kind, data.content, data.todo, data.
|
|
1013
|
+
signature: JSON.stringify([kind, data.content, data.todo, data.tools, data.label, data.count,
|
|
900
1014
|
data.file && [data.file.name, data.file.directory, [...data.file.operations]], kind === 'task' &&
|
|
901
1015
|
[row.title, row.owner, row.done, row.total, row.complete, row.taskStatus, row.facts?.phase, row.node.running]]) })
|
|
902
1016
|
for (const row of model.tasks) {
|
|
@@ -905,8 +1019,9 @@ function feedbackListItems(model, root, partial) {
|
|
|
905
1019
|
if (row.goal && row.goal !== row.title) add('text', [row.id, 'goal'], { content: row.goal }, row)
|
|
906
1020
|
if (facts?.latestRequest && facts.latestRequest !== facts.request) add('request', [row.id, 'request'], { content: facts.latestRequest }, row)
|
|
907
1021
|
row.todos.forEach((todo, index) => add('step', [row.id, 'step', index], { todo }, row))
|
|
908
|
-
|
|
909
|
-
if (
|
|
1022
|
+
const tools = feedbackToolSummary(facts?.activities)
|
|
1023
|
+
if (tools.length) add('tools', [row.id, 'tools'], { tools }, row)
|
|
1024
|
+
if (!row.total && !tools.length) add('note', [row.id, 'empty'], { label: facts ? 'feedback.noSteps' : 'feedback.loading' }, row)
|
|
910
1025
|
if (facts?.omittedActivities) add('note', [row.id, 'recent'], { label: 'feedback.recentOnly' }, row)
|
|
911
1026
|
add('open', [row.id, 'open'], { focusable: !row.node.unavailable }, row)
|
|
912
1027
|
}
|
|
@@ -922,7 +1037,7 @@ function feedbackListItems(model, root, partial) {
|
|
|
922
1037
|
|
|
923
1038
|
/** Prefix heights allow binary-search windows without mounting offscreen rows. */
|
|
924
1039
|
function feedbackListLayout(items, heights = new Map()) {
|
|
925
|
-
const estimates = { task: 64, text: 48, request: 70, step: 28,
|
|
1040
|
+
const estimates = { task: 64, text: 48, request: 70, step: 28, tools: 32, open: 34, files: 40, file: 30, note: 26 }
|
|
926
1041
|
const offsets = [0], byKey = new Map()
|
|
927
1042
|
items.forEach((item, index) => {
|
|
928
1043
|
const measured = heights.get(item.key)
|
|
@@ -970,7 +1085,7 @@ function FeedbackRowContent({ item, root, openDetail, t }) {
|
|
|
970
1085
|
if (item.kind === 'step') return React.createElement('div', { className: 'dsh-super-code-feedback-step', 'data-step-state': item.todo.status },
|
|
971
1086
|
React.createElement(FeedbackMark, { tone: item.todo.status === 'completed' ? 'success' : item.todo.status === 'in_progress' ? 'active' : 'pending', label: text(t, 'feedback.todo.' + item.todo.status) }),
|
|
972
1087
|
React.createElement('span', null, item.todo.content))
|
|
973
|
-
if (item.kind === '
|
|
1088
|
+
if (item.kind === 'tools') return React.createElement(FeedbackTools, { tools: item.tools, t })
|
|
974
1089
|
if (item.kind === 'open') return React.createElement(ActionButton, { type: 'button', className: 'dsh-super-code-feedback-open', disabled: !item.focusable,
|
|
975
1090
|
title: text(t, 'feedback.open'), onClick: () => openDetail(row.node) }, text(t, 'feedback.open'), React.createElement(Glyph, { type: 'arrow' }))
|
|
976
1091
|
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))
|
|
@@ -982,7 +1097,9 @@ function FeedbackRowContent({ item, root, openDetail, t }) {
|
|
|
982
1097
|
file.directory && React.createElement('small', null, file.directory),
|
|
983
1098
|
React.createElement('span', { className: 'dsh-super-code-feedback-file-operation' }, [...file.operations].map(operation => text(t, 'feedback.file.' + operation)).join(' · ')))
|
|
984
1099
|
}
|
|
985
|
-
if (item.
|
|
1100
|
+
if (item.label === 'tree.loadError') return React.createElement('p', { className: 'dsh-super-code-state', 'data-tone': 'error', role: 'alert' }, React.createElement(Glyph, { type: 'error' }), text(t, item.label))
|
|
1101
|
+
if (item.label === 'tree.empty') return React.createElement('p', { className: 'dsh-super-code-state', 'data-tone': 'empty' }, React.createElement(Glyph, { type: 'context' }), text(t, item.label))
|
|
1102
|
+
if (item.kind === 'note') return React.createElement('p', { className: 'dsh-super-code-feedback-muted' }, text(t, item.label))
|
|
986
1103
|
return React.createElement('p', { className: 'dsh-super-code-feedback-text' },
|
|
987
1104
|
item.kind === 'request' && React.createElement('small', null, text(t, 'feedback.latest')), item.content)
|
|
988
1105
|
}
|
|
@@ -1141,7 +1258,7 @@ function WorkspaceSectionToggle({ section, expanded, onToggle, children, t }) {
|
|
|
1141
1258
|
React.createElement('strong', null, label), children)
|
|
1142
1259
|
}
|
|
1143
1260
|
|
|
1144
|
-
/**
|
|
1261
|
+
/** Accordion: at most one open view; clicking the open heading collapses it. Tab state retains drafts. */
|
|
1145
1262
|
function SuperCodeWorkspace(props) {
|
|
1146
1263
|
const { tab } = props.useTabInfo()
|
|
1147
1264
|
const address = typeof tab.navigation?.params?.memory === 'string' ? tab.navigation.params.memory : tab.contentId
|
|
@@ -1156,11 +1273,11 @@ function SuperCodeWorkspace(props) {
|
|
|
1156
1273
|
props.workspaceStates.set(tab.signal, value)
|
|
1157
1274
|
return value
|
|
1158
1275
|
}, [props.workspaceStates, tab.signal])
|
|
1159
|
-
const [active, setActive] = useState(['tasks', 'tree', 'memory'].includes(saved.active) ? saved.active : 'tasks')
|
|
1276
|
+
const [active, setActive] = useState(['tasks', 'tree', 'memory', ''].includes(saved.active) ? saved.active : 'tasks')
|
|
1160
1277
|
const layout = useRef(null), focusSection = useRef('')
|
|
1161
1278
|
const toggle = section => {
|
|
1162
|
-
const next = active === section ?
|
|
1163
|
-
saved.active = next; focusSection.current =
|
|
1279
|
+
const next = active === section ? '' : section
|
|
1280
|
+
saved.active = next; focusSection.current = section; setActive(next)
|
|
1164
1281
|
}
|
|
1165
1282
|
useLayoutEffect(() => {
|
|
1166
1283
|
if (saved.navigationKey === navigationKey) return
|
|
@@ -1956,6 +2073,14 @@ const budgetRemoteContribution = {
|
|
|
1956
2073
|
})),
|
|
1957
2074
|
}
|
|
1958
2075
|
|
|
2076
|
+
const connectionRemoteContribution = {
|
|
2077
|
+
package: 'dsh-super-code', descriptors: ['readConfig', 'saveConfig', 'revealToken', 'rotateToken', 'overview', 'disconnectClient', 'directories', 'pickDirectory', 'browseDirectory', 'pendingDirectories', 'resolveDirectory'].map(method => ({
|
|
2078
|
+
id: `dsh-super-code:superCodeConnection/${method}`, service: 'superCodeConnection', namespace: 'superCodeConnection', method, invocation: { kind: 'direct' },
|
|
2079
|
+
parameters: (method === 'saveConfig' ? ['content', 'expected'] : method === 'rotateToken' ? ['expected'] : method === 'resolveDirectory' ? ['id', 'decision'] : method === 'disconnectClient' ? ['id'] : method === 'browseDirectory' ? ['path'] : [])
|
|
2080
|
+
.map(name => ({ name, wire: name, source: 'json', codec: remoteStringCodec })), result: { mode: 'src-json' },
|
|
2081
|
+
})),
|
|
2082
|
+
}
|
|
2083
|
+
|
|
1959
2084
|
const budgetFields = [
|
|
1960
2085
|
{ key: 'thresholdRatio', group: 'context', factor: 0.01, min: 1, max: 100, step: 'any' },
|
|
1961
2086
|
{ key: 'retainRatio', group: 'context', factor: 0.01, min: 0, max: 100, step: 'any' },
|
|
@@ -2009,106 +2134,188 @@ function budgetConfiguration(draft) {
|
|
|
2009
2134
|
return { ok: true, configuration: config }
|
|
2010
2135
|
}
|
|
2011
2136
|
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
const
|
|
2137
|
+
/* Settings primitives. Every tab composes titled sections of label/control rows,
|
|
2138
|
+
* one callout style for notices, and one autosave indicator. */
|
|
2139
|
+
function SettingsSection({ id, title, description, actions, plain = false, children }) {
|
|
2140
|
+
const lines = [].concat(description ?? []).filter(Boolean)
|
|
2141
|
+
return React.createElement('section', { className: 'dsh-super-code-section', 'aria-labelledby': `${id}-title` },
|
|
2142
|
+
React.createElement('header', { className: 'dsh-super-code-section-head' },
|
|
2143
|
+
React.createElement('div', { className: 'dsh-super-code-section-heading' },
|
|
2144
|
+
React.createElement('h3', { id: `${id}-title` }, title),
|
|
2145
|
+
...lines.map((line, index) => React.createElement('p', { key: index }, line))),
|
|
2146
|
+
actions && React.createElement('div', { className: 'dsh-super-code-section-actions' }, actions)),
|
|
2147
|
+
React.createElement('div', { className: 'dsh-super-code-section-body', 'data-plain': plain || undefined }, children))
|
|
2148
|
+
}
|
|
2149
|
+
/** Label and hint on the left, control on the right; `children` may take the describedby ids. */
|
|
2150
|
+
function SettingRow({ id, label, hint, error, children }) {
|
|
2151
|
+
const described = [hint && `${id}-hint`, error && `${id}-error`].filter(Boolean).join(' ') || undefined
|
|
2152
|
+
return React.createElement('div', { className: 'dsh-super-code-setting-row', 'data-invalid': error ? true : undefined },
|
|
2153
|
+
React.createElement('div', { className: 'dsh-super-code-setting-row-text' },
|
|
2154
|
+
React.createElement('label', { htmlFor: id }, label),
|
|
2155
|
+
hint && React.createElement('p', { id: `${id}-hint` }, hint),
|
|
2156
|
+
error && React.createElement('p', { id: `${id}-error`, className: 'dsh-super-code-setting-row-error' }, error)),
|
|
2157
|
+
React.createElement('div', { className: 'dsh-super-code-setting-row-control' }, typeof children === 'function' ? children(described) : children))
|
|
2158
|
+
}
|
|
2159
|
+
function Callout({ tone = 'info', action, children }) {
|
|
2160
|
+
return React.createElement('div', { className: 'dsh-super-code-callout', 'data-tone': tone, role: tone === 'error' ? 'alert' : tone === 'success' ? 'status' : undefined },
|
|
2161
|
+
React.createElement(Glyph, { type: tone === 'success' ? 'check' : tone === 'info' ? 'info' : 'error' }),
|
|
2162
|
+
React.createElement('div', { className: 'dsh-super-code-callout-text' }, children),
|
|
2163
|
+
action || null)
|
|
2164
|
+
}
|
|
2165
|
+
function SaveIndicator({ state, t }) {
|
|
2166
|
+
return React.createElement('p', { className: 'dsh-super-code-save-indicator', role: 'status', 'data-save-state': state },
|
|
2167
|
+
React.createElement('i', { 'aria-hidden': true }), React.createElement('span', null, t(`save.${state}`)))
|
|
2168
|
+
}
|
|
2169
|
+
/** Exclusive choices that need a sentence each; native radios keep arrow-key behavior. */
|
|
2170
|
+
function ChoiceCards({ name, label, value, choices, onChange, disabled = false }) {
|
|
2171
|
+
return React.createElement('div', { className: 'dsh-super-code-choice-cards', role: 'radiogroup', 'aria-label': label },
|
|
2172
|
+
...choices.map(([choice, title, hint]) => React.createElement('label', { key: choice, className: 'dsh-super-code-choice-card', 'data-checked': choice === value || undefined },
|
|
2173
|
+
React.createElement('input', { type: 'radio', name, value: choice, checked: choice === value, disabled, 'aria-describedby': `${name}-${choice}-hint`,
|
|
2174
|
+
onChange: () => { if (choice !== value) onChange(choice) } }),
|
|
2175
|
+
React.createElement('i', { 'aria-hidden': true }),
|
|
2176
|
+
React.createElement('span', { className: 'dsh-super-code-choice-card-text' },
|
|
2177
|
+
React.createElement('strong', null, title), React.createElement('small', { id: `${name}-${choice}-hint` }, hint)))))
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2180
|
+
/** Each budget tab writes only its own fields and carries the other tab's stored values through. */
|
|
2181
|
+
function budgetSectionConfiguration(section, draft, snapshot) {
|
|
2182
|
+
const parsed = budgetConfiguration(draft)
|
|
2183
|
+
if (!parsed.ok) return parsed
|
|
2184
|
+
const current = snapshot.configuration
|
|
2185
|
+
return { ok: true, configuration: section === 'protection'
|
|
2186
|
+
? { ...current, deletionProtection: draft.deletionProtection, deletionProtectionForOfficialPresets: draft.deletionProtectionForOfficialPresets }
|
|
2187
|
+
: { ...parsed.configuration, deletionProtection: current.deletionProtection, deletionProtectionForOfficialPresets: current.deletionProtectionForOfficialPresets } }
|
|
2188
|
+
}
|
|
2189
|
+
function createBudgetAutosave(api, section = 'context') {
|
|
2190
|
+
return createAutosave(api, { draft: budgetDraft({}), toDraft: budgetDraft,
|
|
2191
|
+
toConfiguration: (draft, snapshot) => budgetSectionConfiguration(section, draft, snapshot) })
|
|
2192
|
+
}
|
|
2193
|
+
function budgetFieldValid(field, input) {
|
|
2194
|
+
const value = String(input ?? '').trim()
|
|
2195
|
+
if (!value) return true
|
|
2196
|
+
const number = Number(value)
|
|
2197
|
+
if (!Number.isFinite(number) || number < field.min || (field.max !== undefined && number > field.max)) return false
|
|
2198
|
+
if (field.step === 'any') return true
|
|
2199
|
+
const scaled = number * field.factor, rounded = Math.round(scaled)
|
|
2200
|
+
return Math.abs(rounded - scaled) <= 1e-7 && Number.isSafeInteger(rounded)
|
|
2201
|
+
}
|
|
2202
|
+
const autosaveValidationErrors = ['invalid', 'retain', 'ptc-order']
|
|
2203
|
+
function autosaveState(editing) {
|
|
2204
|
+
return editing.saving ? 'saving' : autosaveValidationErrors.includes(editing.error) ? 'invalid'
|
|
2205
|
+
: editing.error ? 'error' : editing.notice === 'saved' ? 'saved' : 'idle'
|
|
2206
|
+
}
|
|
2207
|
+
|
|
2208
|
+
function BudgetSettings({ api, t, active, section = 'context', onSaved = () => {} }) {
|
|
2209
|
+
const protection = section === 'protection'
|
|
2210
|
+
const editor = useMemo(() => createBudgetAutosave(api, section), [api, section])
|
|
2211
|
+
const editing = useSyncExternalStore(editor.subscribe, editor.getSnapshot, editor.getSnapshot)
|
|
2212
|
+
const { snapshot, draft } = editing
|
|
2213
|
+
const [loading, setLoading] = useState(false), [failure, setFailure] = useState(''), [notice, setNotice] = useState('')
|
|
2214
|
+
const [confirmReset, setConfirmReset] = useState(false)
|
|
2016
2215
|
const alive = useRef(false), pending = useRef(false)
|
|
2017
|
-
const
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
pending.current = true; setBusy(true); setError(''); setNotice('')
|
|
2216
|
+
const load = async (discard = false) => {
|
|
2217
|
+
if (pending.current || (!discard && editor.hasChanges())) return
|
|
2218
|
+
pending.current = true; setLoading(true); setFailure('')
|
|
2021
2219
|
try {
|
|
2022
2220
|
const response = await api.readConfig()
|
|
2023
2221
|
if (!response.ok) throw new Error('Remote unavailable')
|
|
2024
|
-
if (alive.current) receive(response.value)
|
|
2025
|
-
} catch { if (alive.current)
|
|
2026
|
-
finally { pending.current = false; if (alive.current)
|
|
2222
|
+
if (alive.current) editor.receive(response.value)
|
|
2223
|
+
} catch { if (alive.current) setFailure('connection') }
|
|
2224
|
+
finally { pending.current = false; if (alive.current) setLoading(false) }
|
|
2027
2225
|
}
|
|
2028
|
-
useEffect(() => { alive.current = true; return () => { alive.current = false } }, [
|
|
2029
|
-
useEffect(() => { if (
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
pending.current
|
|
2226
|
+
useEffect(() => { alive.current = true; return () => { alive.current = false; void editor.flush() } }, [editor])
|
|
2227
|
+
useEffect(() => { if (editing.saveCount) onSaved() }, [editing.saveCount])
|
|
2228
|
+
// Other tabs write the same document, so reread on every visit; leaving flushes pending input.
|
|
2229
|
+
useEffect(() => { if (active) load(); else { setConfirmReset(false); void editor.flush() } }, [editor, active])
|
|
2230
|
+
const reset = async () => {
|
|
2231
|
+
setConfirmReset(false)
|
|
2232
|
+
if (pending.current) return
|
|
2233
|
+
await editor.flush()
|
|
2234
|
+
const current = editor.getSnapshot().snapshot
|
|
2235
|
+
if (!alive.current || !current?.writable) return
|
|
2236
|
+
const base = current.configuration
|
|
2237
|
+
const configuration = protection
|
|
2238
|
+
? { ...base, deletionProtection: 'relaxed', deletionProtectionForOfficialPresets: false }
|
|
2239
|
+
: { deletionProtection: base.deletionProtection, deletionProtectionForOfficialPresets: base.deletionProtectionForOfficialPresets }
|
|
2240
|
+
pending.current = true; setLoading(true); setFailure(''); setNotice('')
|
|
2035
2241
|
try {
|
|
2036
|
-
const response =
|
|
2037
|
-
: await api.saveConfig(JSON.stringify(parsed.configuration), String(snapshot.revision))
|
|
2242
|
+
const response = await api.saveConfig(JSON.stringify(configuration), String(current.revision))
|
|
2038
2243
|
if (!response.ok) throw new Error('Remote unavailable')
|
|
2039
2244
|
if (!alive.current) return
|
|
2040
|
-
if (response.value.ok) { receive(response.value.snapshot); setNotice(
|
|
2041
|
-
else
|
|
2042
|
-
} catch { if (alive.current)
|
|
2043
|
-
finally { pending.current = false; if (alive.current)
|
|
2245
|
+
if (response.value.ok) { editor.receive(response.value.snapshot); onSaved(); setNotice('resetDone') }
|
|
2246
|
+
else setFailure(response.value.error)
|
|
2247
|
+
} catch { if (alive.current) setFailure('save-failed') }
|
|
2248
|
+
finally { pending.current = false; if (alive.current) setLoading(false) }
|
|
2044
2249
|
}
|
|
2045
|
-
const disabled =
|
|
2046
|
-
const
|
|
2047
|
-
const
|
|
2048
|
-
const
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2250
|
+
const disabled = loading || !snapshot?.writable
|
|
2251
|
+
const edit = (key, value, delay = 0) => { setNotice(''); setFailure(''); editor.change({ [key]: value }, delay) }
|
|
2252
|
+
const parsed = budgetConfiguration(draft)
|
|
2253
|
+
const fieldError = field => !budgetFieldValid(field, draft[field.key]) ? t('budget.error.range')
|
|
2254
|
+
: field.key === 'retainRatio' && parsed.error === 'retain' ? t('budget.error.retain')
|
|
2255
|
+
: field.key === 'ptcTimeoutMs' && parsed.error === 'ptc-order' ? t('budget.error.ptc-order') : ''
|
|
2256
|
+
const defaultOf = field => {
|
|
2257
|
+
const value = field.root ? snapshot?.defaults?.[field.key] : snapshot?.defaults?.budgets?.[field.key]
|
|
2258
|
+
return value === undefined ? '' : Number(value / field.factor).toLocaleString('en-US', { maximumFractionDigits: 3, useGrouping: false })
|
|
2259
|
+
}
|
|
2260
|
+
const numberRow = field => {
|
|
2261
|
+
const id = `super-code-budget-${field.key}`, execution = field.group === 'execution', fallback = defaultOf(field)
|
|
2262
|
+
return React.createElement(SettingRow, { key: field.key, id, label: t(`budget.${field.key}`), error: fieldError(field),
|
|
2263
|
+
hint: text(t, 'budget.defaultHint', { value: fallback || t('budget.managed') }) }, described =>
|
|
2264
|
+
React.createElement('input', { id, type: 'number', inputMode: 'decimal', value: draft[field.key], min: field.min,
|
|
2265
|
+
max: execution && snapshot?.ptcMaxTimeoutMs ? snapshot.ptcMaxTimeoutMs / 1000 : field.max, step: field.step,
|
|
2266
|
+
placeholder: fallback || t('budget.inherit'), 'aria-invalid': fieldError(field) ? true : undefined, 'aria-describedby': described,
|
|
2267
|
+
disabled: disabled || (execution && snapshot?.ptcMaxTimeoutMs === false),
|
|
2268
|
+
onChange: event => edit(field.key, event.target.value, 500), onBlur: () => { void editor.flush() } }))
|
|
2269
|
+
}
|
|
2270
|
+
const rows = group => budgetFields.filter(field => field.group === group).map(numberRow)
|
|
2271
|
+
const saveError = editing.error && !autosaveValidationErrors.includes(editing.error) ? editing.error : ''
|
|
2272
|
+
const problem = failure || saveError
|
|
2273
|
+
const button = (label, onClick, extra = {}) => React.createElement(ActionButton, { type: 'button', disabled: loading, onClick, ...extra }, label)
|
|
2274
|
+
const content = protection ? React.createElement(React.Fragment, null,
|
|
2275
|
+
React.createElement(SettingsSection, { id: 'super-code-deletion-mode', title: t('delete.mode'), plain: true },
|
|
2276
|
+
React.createElement(ChoiceCards, { name: 'super-code-deletion-mode', label: t('delete.mode'), value: draft.deletionProtection, disabled,
|
|
2277
|
+
choices: ['off', 'relaxed', 'strict'].map(mode => [mode, t(`delete.${mode}`), t(`delete.hint.${mode}`)]),
|
|
2278
|
+
onChange: value => edit('deletionProtection', value) })),
|
|
2279
|
+
React.createElement(SettingsSection, { id: 'super-code-deletion-scope', title: t('delete.scope') },
|
|
2280
|
+
React.createElement(SettingRow, { id: 'super-code-deletion-official', label: t('delete.official'), hint: t('delete.officialHint') }, described =>
|
|
2281
|
+
React.createElement(ConnectionSwitch, { id: 'super-code-deletion-official', checked: draft.deletionProtectionForOfficialPresets,
|
|
2282
|
+
disabled: disabled || draft.deletionProtection === 'off', 'aria-describedby': described,
|
|
2283
|
+
onChange: value => edit('deletionProtectionForOfficialPresets', value) }))))
|
|
2284
|
+
: React.createElement(React.Fragment, null,
|
|
2285
|
+
React.createElement(Callout, { tone: 'info' }, React.createElement('p', null, t('budget.scope')), React.createElement('p', null, t('budget.defaults'))),
|
|
2286
|
+
React.createElement(SettingsSection, { id: 'super-code-budget-common', title: t('budget.common') },
|
|
2287
|
+
React.createElement(SettingRow, { id: 'super-code-budget-official', label: t('budget.applyToOfficialPresets'), hint: t('budget.officialHint') }, described =>
|
|
2288
|
+
React.createElement(ConnectionSwitch, { id: 'super-code-budget-official', checked: draft.applyToOfficialPresets, disabled, 'aria-describedby': described,
|
|
2289
|
+
onChange: value => edit('applyToOfficialPresets', value) }))),
|
|
2290
|
+
React.createElement(SettingsSection, { id: 'super-code-budget-context', title: t('budget.context'), description: t('budget.contextHint') },
|
|
2291
|
+
React.createElement(SettingRow, { id: 'super-code-budget-basis', label: t('budget.pressureBasis'), hint: text(t, 'budget.defaultHint', { value: t('budget.host') }) },
|
|
2292
|
+
React.createElement(SelectControl, { id: 'super-code-budget-basis', label: t('budget.pressureBasis'), value: draft.pressureBasis, disabled,
|
|
2293
|
+
choices: [['', t('budget.inherit')], ['host', t('budget.host')], ['window', t('budget.window')]], onChange: value => edit('pressureBasis', value) })),
|
|
2294
|
+
...rows('context'),
|
|
2295
|
+
React.createElement('details', { className: 'dsh-super-code-section-more' },
|
|
2296
|
+
React.createElement('summary', null, React.createElement(Glyph, { type: 'chevron' }), t('budget.advanced')), ...rows('advanced'))),
|
|
2297
|
+
React.createElement(SettingsSection, { id: 'super-code-budget-execution', title: t('budget.execution'),
|
|
2298
|
+
description: [t(snapshot?.ptcMaxTimeoutMs === false ? 'budget.ptcUnavailable' : 'budget.ptcHint'),
|
|
2299
|
+
snapshot?.ptcMaxTimeoutMs && text(t, 'budget.ptcCeiling', { seconds: snapshot.ptcMaxTimeoutMs / 1000 })] }, ...rows('execution')),
|
|
2300
|
+
React.createElement(SettingsSection, { id: 'super-code-budget-exclusive', title: t('budget.superCode'), description: t('budget.superCodeHint') }, ...rows('superCode')))
|
|
2062
2301
|
return React.createElement('section', { className: 'dsh-super-code-budgets' },
|
|
2063
|
-
React.createElement('form', { 'aria-label': t('budget.title'), 'aria-busy':
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
React.createElement(
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
React.createElement('label', { className: 'dsh-super-code-budget-switch' },
|
|
2081
|
-
React.createElement('input', { type: 'checkbox', role: 'switch', checked: draft.applyToOfficialPresets, disabled,
|
|
2082
|
-
'aria-describedby': 'super-code-official-hint', onChange: event => edit('applyToOfficialPresets', event.target.checked) }),
|
|
2083
|
-
React.createElement('span', null, t('budget.applyToOfficialPresets'), ' ', React.createElement('span', { className: 'dsh-super-code-budget-default' }, text(t, 'budget.defaultValue', { value: t('budget.off') })))),
|
|
2084
|
-
React.createElement('p', { id: 'super-code-official-hint', className: 'dsh-super-code-budget-hint' }, t('budget.officialHint')),
|
|
2085
|
-
React.createElement('p', { className: 'dsh-super-code-budget-hint' }, t('budget.defaults')),
|
|
2086
|
-
React.createElement('fieldset', null, React.createElement('legend', null, t('budget.context')),
|
|
2087
|
-
React.createElement('div', { className: 'dsh-super-code-budget-grid' },
|
|
2088
|
-
React.createElement('div', { className: 'dsh-super-code-settings-field' },
|
|
2089
|
-
React.createElement('label', { id: 'super-code-budget-basis-label' }, t('budget.pressureBasis'), ' ', defaultLabel('pressureBasis')),
|
|
2090
|
-
React.createElement(SelectControl, { id: 'super-code-budget-basis', label: t('budget.pressureBasis'), value: draft.pressureBasis,
|
|
2091
|
-
choices: [['', t('budget.inherit')], ['host', t('budget.host')], ['window', t('budget.window')]], disabled,
|
|
2092
|
-
onChange: value => edit('pressureBasis', value) })), fields('context')),
|
|
2093
|
-
React.createElement('small', null, t('budget.contextHint'))),
|
|
2094
|
-
React.createElement('fieldset', null, React.createElement('legend', null, t('budget.execution')),
|
|
2095
|
-
React.createElement('div', { className: 'dsh-super-code-budget-grid' }, fields('execution')),
|
|
2096
|
-
React.createElement('small', null, t(snapshot?.ptcMaxTimeoutMs === false ? 'budget.ptcUnavailable' : 'budget.ptcHint')),
|
|
2097
|
-
snapshot?.ptcMaxTimeoutMs && React.createElement('small', null, text(t, 'budget.ptcCeiling', { seconds: snapshot.ptcMaxTimeoutMs / 1000 }))),
|
|
2098
|
-
React.createElement('details', { className: 'dsh-super-code-budget-more' },
|
|
2099
|
-
React.createElement('summary', null, t('budget.advanced')),
|
|
2100
|
-
React.createElement('div', { className: 'dsh-super-code-budget-grid' }, fields('advanced')))),
|
|
2101
|
-
React.createElement('section', { className: 'dsh-super-code-budget-group dsh-super-code-budget-exclusive', 'aria-labelledby': 'super-code-exclusive-title' },
|
|
2102
|
-
React.createElement('h3', { id: 'super-code-exclusive-title' }, t('budget.superCode')),
|
|
2103
|
-
React.createElement('p', { className: 'dsh-super-code-budget-hint' }, t('budget.superCodeHint')),
|
|
2104
|
-
React.createElement('div', { className: 'dsh-super-code-budget-grid' }, fields('superCode'))),
|
|
2105
|
-
!busy && snapshot && !snapshot.writable && React.createElement('small', null, t('budget.error.read-only')),
|
|
2106
|
-
error && React.createElement('p', { role: 'alert' }, t(`budget.error.${error}`)),
|
|
2107
|
-
notice && React.createElement('p', { role: 'status', className: 'dsh-super-code-budget-hint' }, t(`budget.${notice}`)),
|
|
2108
|
-
React.createElement('div', { className: 'dsh-super-code-settings-form-actions' },
|
|
2109
|
-
error && React.createElement(ActionButton, { type: 'button', disabled: busy, onClick: read }, t('budget.reload')),
|
|
2110
|
-
React.createElement(ActionButton, { type: 'button', disabled, onClick: () => save(true) }, t('budget.reset')),
|
|
2111
|
-
React.createElement(ActionButton, { type: 'submit', className: 'dsh-super-code-primary-button', disabled: disabled || !dirty }, t(busy ? 'budget.saving' : 'budget.save')))))
|
|
2302
|
+
React.createElement('form', { className: 'dsh-super-code-stack', 'aria-label': t(protection ? 'delete.title' : 'budget.title'), 'aria-busy': loading || editing.saving,
|
|
2303
|
+
onSubmit: event => { event.preventDefault(); void editor.flush() } },
|
|
2304
|
+
problem && React.createElement(Callout, { tone: 'error',
|
|
2305
|
+
action: failure === 'connection' || problem === 'conflict' ? button(t('budget.reload'), () => load(true))
|
|
2306
|
+
: saveError ? button(t('save.retry'), () => { void editor.retry() }) : null },
|
|
2307
|
+
React.createElement('p', null, t(`budget.error.${problem}`))),
|
|
2308
|
+
snapshot && !snapshot.writable && React.createElement(Callout, { tone: 'warning' }, React.createElement('p', null, t('budget.error.read-only'))),
|
|
2309
|
+
notice && React.createElement(Callout, { tone: 'success' }, React.createElement('p', null, t(`budget.${notice}`))),
|
|
2310
|
+
content,
|
|
2311
|
+
React.createElement('div', { className: 'dsh-super-code-settings-footer' },
|
|
2312
|
+
React.createElement(SaveIndicator, { state: autosaveState(editing), t }),
|
|
2313
|
+
React.createElement('div', { className: 'dsh-super-code-settings-footer-actions' },
|
|
2314
|
+
confirmReset ? React.createElement(React.Fragment, null,
|
|
2315
|
+
React.createElement('span', null, t('budget.resetQuestion')),
|
|
2316
|
+
button(t('preset.cancel'), () => setConfirmReset(false)),
|
|
2317
|
+
button(t('budget.resetConfirm'), reset, { className: 'dsh-super-code-danger-button', disabled }))
|
|
2318
|
+
: button(t('budget.reset'), () => setConfirmReset(true), { disabled })))))
|
|
2112
2319
|
}
|
|
2113
2320
|
|
|
2114
2321
|
// Old hosts render file metadata. Synchronize through our own Remote, then
|
|
@@ -2151,8 +2358,420 @@ function synchronizePresetDisplay(api, locale, reload, onError, preferenceScope)
|
|
|
2151
2358
|
return () => { disposed = true; unsubscribe(); unsubscribePreference() }
|
|
2152
2359
|
}
|
|
2153
2360
|
|
|
2154
|
-
function
|
|
2155
|
-
|
|
2361
|
+
function connectionDraft(config) {
|
|
2362
|
+
return { enabled: config.enabled, allowNewConversations: config.allowNewConversations !== false, allowCallerApproval: config.allowCallerApproval === true, port: String(config.port), allowedDirectories: [...config.allowedDirectories], knownDirectories: [...config.knownDirectories ?? []],
|
|
2363
|
+
maxConcurrent: config.maxConnections ?? 3, maxConnections: String(config.maxConnections ?? 3), directoriesConfigured: config.directoriesConfigured ?? false }
|
|
2364
|
+
}
|
|
2365
|
+
function connectionConfiguration(draft) {
|
|
2366
|
+
const port = Number(draft.port), maxConnections = Number(draft.maxConnections)
|
|
2367
|
+
const allowedDirectories = [...new Set(draft.allowedDirectories)]
|
|
2368
|
+
if (typeof draft.enabled !== 'boolean' || typeof draft.allowNewConversations !== 'boolean' || typeof draft.allowCallerApproval !== '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 }
|
|
2369
|
+
return { ok: true, configuration: { enabled: draft.enabled, allowNewConversations: draft.allowNewConversations, allowCallerApproval: draft.allowCallerApproval, port, allowedDirectories, maxConcurrent: maxConnections, maxConnections, directoriesConfigured: true, knownDirectories: draft.knownDirectories } }
|
|
2370
|
+
}
|
|
2371
|
+
/**
|
|
2372
|
+
* Serialize writes and retain newer edits while an earlier revision is being saved.
|
|
2373
|
+
* `toConfiguration` returns `{ ok, configuration }` or `{ ok: false, error? }`; invalid drafts never write.
|
|
2374
|
+
*/
|
|
2375
|
+
function createAutosave(api, { draft: initialDraft, toDraft, toConfiguration }) {
|
|
2376
|
+
let state = { snapshot: false, draft: initialDraft, saving: false, error: '', notice: '', saveCount: 0 }
|
|
2377
|
+
let version = 0, acknowledged = 0, blocked = -1, timer = false, running = false
|
|
2378
|
+
const listeners = new Set()
|
|
2379
|
+
const update = patch => { state = { ...state, ...patch }; for (const listener of listeners) listener() }
|
|
2380
|
+
const flush = () => {
|
|
2381
|
+
if (timer) { clearTimeout(timer); timer = false }
|
|
2382
|
+
if (running) return running
|
|
2383
|
+
if (version === acknowledged || version === blocked || !state.snapshot) return Promise.resolve()
|
|
2384
|
+
running = (async () => {
|
|
2385
|
+
update({ saving: true, error: '', notice: '' })
|
|
2386
|
+
try {
|
|
2387
|
+
while (acknowledged < version) {
|
|
2388
|
+
const sent = version, parsed = toConfiguration(state.draft, state.snapshot)
|
|
2389
|
+
if (!parsed.ok) { blocked = version; update({ error: parsed.error || 'invalid' }); break }
|
|
2390
|
+
const result = await api.saveConfig(JSON.stringify(parsed.configuration), String(state.snapshot.revision))
|
|
2391
|
+
if (!result.ok) throw new Error('Remote unavailable')
|
|
2392
|
+
if (!result.value.ok) { blocked = version; update({ error: result.value.error }); break }
|
|
2393
|
+
acknowledged = sent
|
|
2394
|
+
update({ snapshot: result.value.snapshot, saveCount: state.saveCount + 1,
|
|
2395
|
+
...(sent === version ? { draft: toDraft(result.value.snapshot.configuration), notice: 'saved' } : {}) })
|
|
2396
|
+
}
|
|
2397
|
+
} catch { blocked = version; update({ error: 'save-failed' }) }
|
|
2398
|
+
finally { update({ saving: false }) }
|
|
2399
|
+
})().finally(() => { running = false })
|
|
2400
|
+
return running
|
|
2401
|
+
}
|
|
2402
|
+
return {
|
|
2403
|
+
getSnapshot: () => state,
|
|
2404
|
+
subscribe: listener => { listeners.add(listener); return () => listeners.delete(listener) },
|
|
2405
|
+
hasChanges: () => version !== acknowledged || state.saving,
|
|
2406
|
+
receive: (snapshot, draft = toDraft(snapshot.configuration)) => {
|
|
2407
|
+
acknowledged = ++version
|
|
2408
|
+
update({ snapshot, draft, error: '', notice: '' })
|
|
2409
|
+
},
|
|
2410
|
+
revision: snapshot => update({ snapshot }),
|
|
2411
|
+
change: (patch, delay = 0) => {
|
|
2412
|
+
version++
|
|
2413
|
+
update({ draft: { ...state.draft, ...patch }, error: '', notice: '' })
|
|
2414
|
+
if (timer) { clearTimeout(timer); timer = false }
|
|
2415
|
+
if (delay) timer = setTimeout(() => { timer = false; void flush() }, delay)
|
|
2416
|
+
else void flush()
|
|
2417
|
+
},
|
|
2418
|
+
/** Retry the last blocked or failed draft without requiring another edit. */
|
|
2419
|
+
retry: () => { blocked = -1; return flush() },
|
|
2420
|
+
flush,
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
function createConnectionAutosave(api) {
|
|
2424
|
+
return createAutosave(api, { draft: connectionDraft({ enabled: false, port: 23457, allowedDirectories: [] }),
|
|
2425
|
+
toDraft: connectionDraft, toConfiguration: draft => connectionConfiguration(draft) })
|
|
2426
|
+
}
|
|
2427
|
+
/** The single switch control for every settings toggle; extra props go to the input. */
|
|
2428
|
+
function ConnectionSwitch({ checked, disabled, onChange, children, ...input }) {
|
|
2429
|
+
return React.createElement('label', { className: 'dsh-super-code-connection-switch' },
|
|
2430
|
+
React.createElement('input', { ...input, type: 'checkbox', role: 'switch', checked, disabled, onChange: event => onChange(event.target.checked) }),
|
|
2431
|
+
React.createElement('span', { className: 'dsh-super-code-connection-track', 'aria-hidden': true }, React.createElement('i')), children)
|
|
2432
|
+
}
|
|
2433
|
+
function DirectoryBrowser({ api, t, onSelect, onClose }) {
|
|
2434
|
+
const [listing, setListing] = useState(null), [busy, setBusy] = useState(false), [error, setError] = useState(false)
|
|
2435
|
+
const dialog = useRef(null), generation = useRef(0), lastPath = useRef('')
|
|
2436
|
+
const browse = async path => {
|
|
2437
|
+
const version = ++generation.current; lastPath.current = path; setBusy(true); setError(false)
|
|
2438
|
+
try { const result = await api.browseDirectory(path); if (!result.ok) throw new Error(); if (version === generation.current) setListing(result.value) }
|
|
2439
|
+
catch { if (version === generation.current) setError(true) }
|
|
2440
|
+
finally { if (version === generation.current) setBusy(false) }
|
|
2441
|
+
}
|
|
2442
|
+
useEffect(() => {
|
|
2443
|
+
const element = dialog.current; element.showModal(); browse('')
|
|
2444
|
+
return () => { generation.current++; if (element.open) element.close() }
|
|
2445
|
+
}, [api])
|
|
2446
|
+
return React.createElement('dialog', { ref: dialog, className: 'dsh-super-code-confirm', 'aria-labelledby': 'super-code-directory-title',
|
|
2447
|
+
onCancel: event => { event.preventDefault(); event.stopPropagation(); onClose() }, onKeyDown: event => event.stopPropagation() },
|
|
2448
|
+
React.createElement('h3', { id: 'super-code-directory-title' }, t('connection.directoryBrowser')),
|
|
2449
|
+
React.createElement('nav', { className: 'dsh-super-code-directory-crumbs', 'aria-label': t('connection.directoryBrowser') },
|
|
2450
|
+
listing?.crumbs.map((row, index) => React.createElement(ActionButton, { key: `${row.path}-${index}`, type: 'button', disabled: busy, onClick: () => browse(row.path) }, row.name || row.path))),
|
|
2451
|
+
listing && React.createElement('code', { className: 'dsh-super-code-directory-path' }, listing.path),
|
|
2452
|
+
React.createElement('div', { className: 'dsh-super-code-directory-browser', 'aria-busy': busy },
|
|
2453
|
+
listing && listing.crumbs.length > 1 && React.createElement(ActionButton, { type: 'button', disabled: busy, onClick: () => browse(listing.crumbs[listing.crumbs.length - 2].path) }, '↑ ', t('connection.parentDirectory')),
|
|
2454
|
+
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)),
|
|
2455
|
+
listing && !listing.entries.length && React.createElement('p', null, t('connection.emptyDirectory'))),
|
|
2456
|
+
error && React.createElement('p', { role: 'alert' }, t('connection.browserError')),
|
|
2457
|
+
React.createElement('div', { className: 'dsh-super-code-confirm-actions' },
|
|
2458
|
+
error && React.createElement(ActionButton, { type: 'button', disabled: busy, onClick: () => browse(lastPath.current) }, t('connection.reload')),
|
|
2459
|
+
React.createElement(ActionButton, { type: 'button', onClick: onClose }, t('preset.cancel')),
|
|
2460
|
+
React.createElement(ActionButton, { type: 'button', className: 'dsh-super-code-primary-button', disabled: busy || error || !listing,
|
|
2461
|
+
onClick: () => onSelect(listing.path) }, t('connection.selectDirectory'))))
|
|
2462
|
+
}
|
|
2463
|
+
function connectionSessions(overview) {
|
|
2464
|
+
const online = new Set(overview.connections.map(row => row.id)), connected = [], history = []
|
|
2465
|
+
for (const row of overview.sessions) (row.connectionId && online.has(row.connectionId) ? connected : history).push(row)
|
|
2466
|
+
return { connected, history }
|
|
2467
|
+
}
|
|
2468
|
+
function ConnectionActivity({ api, t, active, openSession }) {
|
|
2469
|
+
const [overview, setOverview] = useState(null), [busy, setBusy] = useState(false), [error, setError] = useState(false)
|
|
2470
|
+
const alive = useRef(false), pending = useRef(false)
|
|
2471
|
+
const load = async () => {
|
|
2472
|
+
if (pending.current) return
|
|
2473
|
+
pending.current = true
|
|
2474
|
+
try { const result = await api.overview(); if (!result.ok) throw new Error(); if (alive.current) { setOverview(result.value); setError(false) } }
|
|
2475
|
+
catch { if (alive.current) setError(true) }
|
|
2476
|
+
finally { pending.current = false }
|
|
2477
|
+
}
|
|
2478
|
+
useEffect(() => { alive.current = true; return () => { alive.current = false } }, [api])
|
|
2479
|
+
useEffect(() => { if (!active) return; load(); const timer = setInterval(load, 2000); return () => clearInterval(timer) }, [active, api])
|
|
2480
|
+
const disconnect = async id => {
|
|
2481
|
+
if (pending.current) return
|
|
2482
|
+
pending.current = true; setBusy(true)
|
|
2483
|
+
try { const result = await api.disconnectClient(id); if (!result.ok) throw new Error() }
|
|
2484
|
+
catch { if (alive.current) setError(true); return }
|
|
2485
|
+
finally { pending.current = false; if (alive.current) setBusy(false) }
|
|
2486
|
+
if (alive.current) load()
|
|
2487
|
+
}
|
|
2488
|
+
const groups = overview ? connectionSessions(overview) : { connected: [], history: [] }
|
|
2489
|
+
return React.createElement('section', { className: 'dsh-super-code-connection-activity', 'aria-label': t('home.sessions') },
|
|
2490
|
+
React.createElement('h3', null, t('home.connections')),
|
|
2491
|
+
!overview ? React.createElement('p', { role: 'status' }, t('connection.state.loading'))
|
|
2492
|
+
: overview.connections.length ? React.createElement('ul', { className: 'dsh-super-code-connection-list' }, overview.connections.map(row => React.createElement('li', { key: row.id },
|
|
2493
|
+
React.createElement('span', null, React.createElement('strong', null, row.name), React.createElement('small', null, row.version, ' · ', t('home.lastSeen'), ' ', new Date(row.lastSeen).toLocaleTimeString())),
|
|
2494
|
+
React.createElement(ActionButton, { type: 'button', disabled: busy, onClick: () => disconnect(row.id) }, t('home.disconnect'))))) : React.createElement('p', null, t('home.noConnections')),
|
|
2495
|
+
React.createElement('h3', null, t('home.sessions')),
|
|
2496
|
+
...['connected', 'history'].map(kind => React.createElement('div', { key: kind, 'data-session-group': kind },
|
|
2497
|
+
React.createElement('h4', null, t(kind === 'connected' ? 'home.connectedSessions' : 'home.historySessions'), ' · ', overview ? groups[kind].length : '—'),
|
|
2498
|
+
groups[kind].length ? React.createElement('ul', { className: 'dsh-super-code-connection-list' }, groups[kind].map(row => React.createElement('li', { key: row.taskId },
|
|
2499
|
+
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)),
|
|
2500
|
+
React.createElement(ActionButton, { type: 'button', onClick: () => openSession(row.sessionId) }, t('home.openSession'))))) : overview && React.createElement('p', null, t('home.noSessions')))),
|
|
2501
|
+
error && React.createElement('p', { role: 'alert' }, t('home.error')))
|
|
2502
|
+
}
|
|
2503
|
+
|
|
2504
|
+
function ConnectionSettings({ api, t, active, openSession, onSaved = () => {} }) {
|
|
2505
|
+
const editor = useMemo(() => createConnectionAutosave(api), [api])
|
|
2506
|
+
const editing = useSyncExternalStore(editor.subscribe, editor.getSnapshot, editor.getSnapshot)
|
|
2507
|
+
const { snapshot, draft, saving } = editing
|
|
2508
|
+
const [catalog, setCatalog] = useState({ items: [], picker: 'unavailable' }), [custom, setCustom] = useState([]), [browser, setBrowser] = useState(false)
|
|
2509
|
+
const [busy, setBusy] = useState(false), [error, setError] = useState(''), [notice, setNotice] = useState(''), [token, setToken] = useState('')
|
|
2510
|
+
const alive = useRef(false), pending = useRef(false), visible = useRef(active)
|
|
2511
|
+
visible.current = active
|
|
2512
|
+
const dirty = editor.hasChanges()
|
|
2513
|
+
const receive = (value, directories = catalog) => {
|
|
2514
|
+
const next = connectionDraft(value.configuration)
|
|
2515
|
+
if (!next.directoriesConfigured && !next.allowedDirectories.length) next.allowedDirectories = directories.items.filter(row => row.workspace && row.available).slice(0, 32).map(row => row.path)
|
|
2516
|
+
editor.receive(value, next)
|
|
2517
|
+
}
|
|
2518
|
+
const load = async (discard = false) => {
|
|
2519
|
+
if (pending.current || (discard && editor.getSnapshot().saving)) return
|
|
2520
|
+
pending.current = true; setBusy(true); setError('')
|
|
2521
|
+
try {
|
|
2522
|
+
const [config, directories, credential] = await Promise.all([api.readConfig(), api.directories(), api.revealToken()])
|
|
2523
|
+
if (!config.ok || !directories.ok || !credential.ok) throw new Error()
|
|
2524
|
+
if (!alive.current) return
|
|
2525
|
+
setCatalog(directories.value)
|
|
2526
|
+
if (discard || !dirty) receive(config.value, directories.value)
|
|
2527
|
+
if (visible.current) setToken(credential.value.token)
|
|
2528
|
+
} catch { if (alive.current) setError('unavailable') }
|
|
2529
|
+
finally { pending.current = false; if (alive.current) setBusy(false) }
|
|
2530
|
+
}
|
|
2531
|
+
const action = async kind => {
|
|
2532
|
+
if (pending.current) return
|
|
2533
|
+
pending.current = true; setBusy(true); setError(''); setNotice('')
|
|
2534
|
+
try {
|
|
2535
|
+
const response = kind === 'rotate' ? await api.rotateToken(String(snapshot.revision)) : await api.revealToken()
|
|
2536
|
+
if (!response.ok) throw new Error()
|
|
2537
|
+
if (!alive.current) return
|
|
2538
|
+
if (kind === 'token') { if (visible.current) setToken(response.value.token) }
|
|
2539
|
+
else if (response.value.ok) {
|
|
2540
|
+
if (kind === 'rotate') {
|
|
2541
|
+
editor.revision(response.value.snapshot)
|
|
2542
|
+
const credential = await api.revealToken()
|
|
2543
|
+
if (credential.ok && alive.current && visible.current) setToken(credential.value.token)
|
|
2544
|
+
} else receive(response.value.snapshot)
|
|
2545
|
+
onSaved(); setNotice(kind === 'rotate' ? 'rotated' : 'saved')
|
|
2546
|
+
} else setError(response.value.error)
|
|
2547
|
+
} catch { if (alive.current) setError('unavailable') }
|
|
2548
|
+
finally { pending.current = false; if (alive.current) setBusy(false) }
|
|
2549
|
+
}
|
|
2550
|
+
useEffect(() => { alive.current = true; return () => { alive.current = false; void editor.flush() } }, [editor])
|
|
2551
|
+
useEffect(() => { if (editing.saveCount) onSaved() }, [editing.saveCount])
|
|
2552
|
+
useEffect(() => { if (active) load(); else { setToken(''); setBrowser(false) } }, [api, active])
|
|
2553
|
+
useEffect(() => {
|
|
2554
|
+
if (!active) return
|
|
2555
|
+
const changed = () => { if (dirty) setError('conflict'); else load(true) }
|
|
2556
|
+
window.addEventListener('super-code-directories-changed', changed)
|
|
2557
|
+
return () => window.removeEventListener('super-code-directories-changed', changed)
|
|
2558
|
+
}, [active, dirty, api])
|
|
2559
|
+
const disabled = busy || !snapshot?.writable
|
|
2560
|
+
const change = (patch, delay = 0) => {
|
|
2561
|
+
// Revoking the last directory also stops admission instead of retaining its old grant.
|
|
2562
|
+
editor.change(patch.allowedDirectories?.length === 0 ? { ...patch, enabled: false } : patch, delay)
|
|
2563
|
+
setNotice(''); setError('')
|
|
2564
|
+
}
|
|
2565
|
+
const edit = (key, value) => change({ [key]: value }, ['port', 'maxConnections'].includes(key) ? 350 : 0)
|
|
2566
|
+
const choose = path => {
|
|
2567
|
+
if (!draft.allowedDirectories.includes(path) && draft.allowedDirectories.length >= 32) { setError('directory-limit'); return }
|
|
2568
|
+
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)
|
|
2569
|
+
}
|
|
2570
|
+
const addDirectory = async () => {
|
|
2571
|
+
if (pending.current) return
|
|
2572
|
+
if (catalog.picker !== 'native' && !globalThis.__DSH_DIRECTORY_PICKER__?.pick) { setBrowser(true); return }
|
|
2573
|
+
pending.current = true; setBusy(true)
|
|
2574
|
+
try {
|
|
2575
|
+
const result = globalThis.__DSH_DIRECTORY_PICKER__?.pick ? await globalThis.__DSH_DIRECTORY_PICKER__.pick()
|
|
2576
|
+
: await api.pickDirectory().then(result => { if (!result.ok) throw new Error(); return result.value.kind === 'selected' ? result.value.path : null })
|
|
2577
|
+
if (alive.current && result) choose(result)
|
|
2578
|
+
} catch { if (alive.current) setError('picker-unavailable') }
|
|
2579
|
+
finally { pending.current = false; if (alive.current) setBusy(false) }
|
|
2580
|
+
}
|
|
2581
|
+
const rows = [...catalog.items]
|
|
2582
|
+
for (const path of [...custom, ...draft.knownDirectories, ...draft.allowedDirectories]) if (!rows.some(row => row.path === path)) rows.push({ path, title: '', available: true, workspace: false })
|
|
2583
|
+
const copy = async value => {
|
|
2584
|
+
try { await navigator.clipboard.writeText(value); if (alive.current) { setNotice('copied'); setError('') } }
|
|
2585
|
+
catch { if (alive.current) setError('clipboard') }
|
|
2586
|
+
}
|
|
2587
|
+
const command = snapshot?.url ? t('connection.prompt', { url: snapshot.url }) : ''
|
|
2588
|
+
return React.createElement('section', { className: 'dsh-super-code-connection' },
|
|
2589
|
+
React.createElement('div', { className: 'dsh-super-code-settings-status-row' },
|
|
2590
|
+
React.createElement('p', { className: 'dsh-super-code-preset-status', role: 'status', 'data-available': snapshot?.state === 'listening' },
|
|
2591
|
+
React.createElement('i', { 'aria-hidden': true }), t(`connection.state.${snapshot?.state || 'loading'}`)),
|
|
2592
|
+
React.createElement('div', { className: 'dsh-super-code-settings-actions' }, React.createElement(ActionButton, { type: 'button', disabled: busy, onClick: () => load(true) }, t('connection.reload')))),
|
|
2593
|
+
snapshot?.error && React.createElement('p', { role: 'alert' }, t(`connection.error.${snapshot.error}`)),
|
|
2594
|
+
React.createElement(ConnectionActivity, { api, t, active, openSession }),
|
|
2595
|
+
React.createElement('form', { 'aria-label': t('connection.title'), 'aria-busy': saving, onSubmit: event => { event.preventDefault(); void editor.flush() } },
|
|
2596
|
+
React.createElement(ConnectionSwitch, { checked: draft.enabled, disabled, onChange: value => edit('enabled', value) }, t('connection.enabled')),
|
|
2597
|
+
React.createElement('p', { className: 'dsh-super-code-connection-hint' }, t('connection.scope')),
|
|
2598
|
+
React.createElement(ConnectionSwitch, { checked: draft.allowNewConversations, disabled, onChange: value => edit('allowNewConversations', value) }, t('connection.allowNewConversations')),
|
|
2599
|
+
React.createElement('small', null, t('connection.allowNewConversationsHint')),
|
|
2600
|
+
React.createElement(ConnectionSwitch, { checked: draft.allowCallerApproval, disabled, onChange: value => edit('allowCallerApproval', value) }, t('connection.allowCallerApproval')),
|
|
2601
|
+
React.createElement('small', null, t('connection.allowCallerApprovalHint')),
|
|
2602
|
+
React.createElement('div', { className: 'dsh-super-code-connection-grid' },
|
|
2603
|
+
...[['port', 1024, 65535], ['maxConnections', 1, 32]].map(([key, min, max]) => React.createElement('div', { key, className: 'dsh-super-code-settings-field' },
|
|
2604
|
+
React.createElement('label', { htmlFor: `super-code-connection-${key}` }, t(`connection.${key}`)),
|
|
2605
|
+
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() } })))),
|
|
2606
|
+
React.createElement('small', null, t('connection.connectionsHint')),
|
|
2607
|
+
React.createElement('fieldset', { className: 'dsh-super-code-directory-fieldset' },
|
|
2608
|
+
React.createElement('legend', null, t('connection.directories')),
|
|
2609
|
+
React.createElement('div', { className: 'dsh-super-code-directory-list' },
|
|
2610
|
+
...rows.map(row => React.createElement('div', { key: row.path, className: 'dsh-super-code-directory-row' },
|
|
2611
|
+
React.createElement('label', null,
|
|
2612
|
+
React.createElement('input', { type: 'checkbox', checked: draft.allowedDirectories.includes(row.path), disabled: disabled || (!row.available && !draft.allowedDirectories.includes(row.path)),
|
|
2613
|
+
onChange: event => {
|
|
2614
|
+
if (event.target.checked && draft.allowedDirectories.length >= 32) { setError('directory-limit'); return }
|
|
2615
|
+
edit('allowedDirectories', event.target.checked ? [...draft.allowedDirectories, row.path] : draft.allowedDirectories.filter(path => path !== row.path))
|
|
2616
|
+
} }),
|
|
2617
|
+
React.createElement('span', null, row.title && React.createElement('strong', null, row.title), React.createElement('code', null, row.path),
|
|
2618
|
+
React.createElement('small', { 'data-unavailable': !row.available }, t(!row.available ? 'connection.directoryUnavailable' : row.workspace ? 'connection.workspaceDirectory' : 'connection.customDirectory')))),
|
|
2619
|
+
!row.workspace && React.createElement(ActionButton, { type: 'button', disabled, 'aria-label': `${t('connection.removeDirectory')}: ${row.path}`,
|
|
2620
|
+
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')))),
|
|
2621
|
+
!rows.length && React.createElement('p', null, t('connection.noDirectories'))),
|
|
2622
|
+
React.createElement('div', { className: 'dsh-super-code-settings-actions' },
|
|
2623
|
+
React.createElement(ActionButton, { type: 'button', disabled: disabled || catalog.picker === 'unavailable', onClick: addDirectory }, t('connection.addDirectory'))),
|
|
2624
|
+
React.createElement('small', null, t('connection.directoriesHint'))),
|
|
2625
|
+
React.createElement('p', { role: 'status', className: 'dsh-super-code-connection-hint', 'data-connection-save': saving ? 'saving' : editing.error ? 'error' : editing.notice === 'saved' ? 'saved' : 'idle' },
|
|
2626
|
+
t(saving ? 'connection.saving' : editing.notice === 'saved' ? 'connection.saved' : 'connection.autosave'))),
|
|
2627
|
+
snapshot?.url && React.createElement('div', { className: 'dsh-super-code-connection-client' },
|
|
2628
|
+
React.createElement('h3', null, t('connection.client')), React.createElement('code', null, snapshot.url), React.createElement('pre', null, command),
|
|
2629
|
+
React.createElement('div', { className: 'dsh-super-code-settings-actions' },
|
|
2630
|
+
React.createElement(ActionButton, { type: 'button', disabled: busy, onClick: () => copy(command) }, t('connection.copyCommand')),
|
|
2631
|
+
React.createElement(ActionButton, { type: 'button', disabled, onClick: () => token ? setToken('') : action('token') }, t(token ? 'connection.hideToken' : 'connection.showToken')),
|
|
2632
|
+
active && token && React.createElement(ActionButton, { type: 'button', onClick: () => copy(token) }, t('connection.copyToken')),
|
|
2633
|
+
React.createElement(ActionButton, { type: 'button', disabled: disabled || dirty, onClick: () => action('rotate') }, t('connection.rotate'))),
|
|
2634
|
+
active && token && React.createElement('input', { className: 'dsh-super-code-connection-token', 'aria-label': t('connection.token'), value: token, readOnly: true, autoComplete: 'off', spellCheck: false }),
|
|
2635
|
+
React.createElement('small', null, t('connection.tokenHint'))),
|
|
2636
|
+
notice && React.createElement('p', { role: 'status' }, t(`connection.${notice}`)),
|
|
2637
|
+
(error || editing.error) && React.createElement('p', { role: 'alert' }, t(`connection.error.${error || editing.error}`)),
|
|
2638
|
+
browser && React.createElement(DirectoryBrowser, { api, t, onSelect: choose, onClose: () => setBrowser(false) }))
|
|
2639
|
+
}
|
|
2640
|
+
|
|
2641
|
+
/* A few exclusive choices read faster as one visible radio group than as a menu. */
|
|
2642
|
+
function SegmentedControl({ label, value, choices, onChange, disabled = false, busy = false, describedBy }) {
|
|
2643
|
+
const selected = choices.findIndex(([choice]) => choice === value)
|
|
2644
|
+
const focusable = Math.max(0, selected)
|
|
2645
|
+
const pick = choice => { if (!disabled && !busy && choice !== value) onChange(choice) }
|
|
2646
|
+
return React.createElement('div', { className: 'dsh-super-code-segmented', role: 'radiogroup', 'aria-label': label, 'aria-describedby': describedBy,
|
|
2647
|
+
'aria-busy': busy || undefined, 'aria-disabled': disabled || undefined,
|
|
2648
|
+
onKeyDown: event => {
|
|
2649
|
+
const step = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 }[event.key]
|
|
2650
|
+
const next = event.key === 'Home' ? 0 : event.key === 'End' ? choices.length - 1
|
|
2651
|
+
: step === undefined ? -1 : (focusable + step + choices.length) % choices.length
|
|
2652
|
+
if (next < 0 || disabled || busy) return
|
|
2653
|
+
event.preventDefault()
|
|
2654
|
+
event.currentTarget.querySelectorAll('[role=radio]')[next]?.focus()
|
|
2655
|
+
pick(choices[next][0])
|
|
2656
|
+
} },
|
|
2657
|
+
...choices.map(([choice, text], index) => React.createElement('button', { key: choice, type: 'button', role: 'radio',
|
|
2658
|
+
'aria-checked': index === selected, tabIndex: index === focusable ? 0 : -1, disabled, onClick: () => pick(choice) }, text)))
|
|
2659
|
+
}
|
|
2660
|
+
function SettingsHome({ budgetApi, connectionApi, t, active, revision, navigate, children }) {
|
|
2661
|
+
const [budget, setBudget] = useState(null), [connection, setConnection] = useState(null), [overview, setOverview] = useState({ connections: [], sessions: [] })
|
|
2662
|
+
// `saving` keeps the chosen value on screen until the reread confirms it.
|
|
2663
|
+
const [saving, setSaving] = useState(null), [failure, setFailure] = useState('')
|
|
2664
|
+
const alive = useRef(false), request = useRef(0), loading = useRef(false), writing = useRef(false)
|
|
2665
|
+
const load = async (force = false) => {
|
|
2666
|
+
if (!force && (loading.current || writing.current)) return
|
|
2667
|
+
const sequence = ++request.current
|
|
2668
|
+
loading.current = true
|
|
2669
|
+
try {
|
|
2670
|
+
const [b, c, o] = await Promise.all([budgetApi.readConfig(), connectionApi.readConfig(), connectionApi.overview()])
|
|
2671
|
+
if (!b.ok || !c.ok || !o.ok) throw new Error()
|
|
2672
|
+
if (alive.current && sequence === request.current) {
|
|
2673
|
+
setBudget(b.value); setConnection(c.value); setOverview(o.value); setFailure(value => value === 'load' ? '' : value)
|
|
2674
|
+
}
|
|
2675
|
+
} catch { if (alive.current && sequence === request.current) setFailure('load') }
|
|
2676
|
+
finally { if (sequence === request.current) loading.current = false }
|
|
2677
|
+
}
|
|
2678
|
+
useEffect(() => { alive.current = true; return () => { alive.current = false } }, [budgetApi, connectionApi])
|
|
2679
|
+
useEffect(() => { if (!active) return; load(); const timer = setInterval(load, 2000); return () => clearInterval(timer) }, [active, revision, budgetApi, connectionApi])
|
|
2680
|
+
const change = async (kind, value) => {
|
|
2681
|
+
const current = kind === 'protection' ? budget : connection
|
|
2682
|
+
if (writing.current || !current?.writable) return
|
|
2683
|
+
// A save supersedes any poll in flight, so a stale read cannot undo the choice.
|
|
2684
|
+
writing.current = true; request.current++; loading.current = false
|
|
2685
|
+
setSaving({ kind, value }); setFailure('')
|
|
2686
|
+
let saved = false
|
|
2687
|
+
try {
|
|
2688
|
+
const configuration = kind === 'protection' ? { ...current.configuration, deletionProtection: value } : { ...current.configuration, enabled: value }
|
|
2689
|
+
const response = await (kind === 'protection' ? budgetApi : connectionApi).saveConfig(JSON.stringify(configuration), String(current.revision))
|
|
2690
|
+
saved = Boolean(response.ok && response.value.ok)
|
|
2691
|
+
} catch { /* Reported on the row below. */ }
|
|
2692
|
+
if (saved && alive.current) await load(true)
|
|
2693
|
+
writing.current = false
|
|
2694
|
+
if (alive.current) { setSaving(null); if (!saved) setFailure(kind) }
|
|
2695
|
+
}
|
|
2696
|
+
const mode = saving?.kind === 'protection' ? saving.value : budget ? budgetDraft(budget.configuration).deletionProtection : ''
|
|
2697
|
+
const enabled = saving?.kind === 'connection' ? saving.value : Boolean(connection?.configuration.enabled)
|
|
2698
|
+
const state = !connection || saving?.kind === 'connection' ? 'loading' : connection.state
|
|
2699
|
+
const listening = enabled && state === 'listening'
|
|
2700
|
+
const needsSetup = Boolean(connection) && !enabled && !connection.configuration.allowedDirectories.length
|
|
2701
|
+
const placeholder = kind => React.createElement('span', { className: 'dsh-super-code-skeleton', 'data-skeleton': kind, 'aria-hidden': true })
|
|
2702
|
+
const row = ({ key, icon, title, target, on, body, control }) => React.createElement('section', { className: 'dsh-super-code-home-row',
|
|
2703
|
+
'data-home-row': key, 'data-active': on || undefined, 'aria-label': title, 'aria-busy': saving?.kind === key || undefined },
|
|
2704
|
+
React.createElement('span', { className: 'dsh-super-code-home-row-icon', 'aria-hidden': true }, React.createElement(Glyph, { type: icon })),
|
|
2705
|
+
React.createElement('div', { className: 'dsh-super-code-home-row-text' },
|
|
2706
|
+
React.createElement('h3', { className: 'dsh-super-code-home-row-title' },
|
|
2707
|
+
React.createElement('button', { type: 'button', 'aria-label': t('home.details', { title }), onClick: () => navigate(target) }, title, React.createElement(Glyph, { type: 'chevron' }))),
|
|
2708
|
+
body),
|
|
2709
|
+
React.createElement('div', { className: 'dsh-super-code-home-row-control' }, control),
|
|
2710
|
+
failure === key && React.createElement('p', { role: 'alert' }, t('home.error')))
|
|
2711
|
+
return React.createElement('section', { className: 'dsh-super-code-home' }, children,
|
|
2712
|
+
failure === 'load' && React.createElement('p', { role: 'alert' }, t('home.error')),
|
|
2713
|
+
React.createElement('div', { className: 'dsh-super-code-home-list' },
|
|
2714
|
+
row({ key: 'protection', icon: 'shield', title: t('delete.title'), target: 'protection', on: Boolean(mode) && mode !== 'off',
|
|
2715
|
+
body: budget ? React.createElement('p', { id: 'super-code-home-protection-hint', className: 'dsh-super-code-home-row-hint' }, t(`delete.hint.${mode}`)) : placeholder('text'),
|
|
2716
|
+
control: budget ? React.createElement(SegmentedControl, { label: t('delete.mode'), value: mode, describedBy: 'super-code-home-protection-hint',
|
|
2717
|
+
choices: ['off', 'relaxed', 'strict'].map(value => [value, t(`delete.${value}`)]), disabled: !budget.writable, busy: saving?.kind === 'protection',
|
|
2718
|
+
onChange: value => change('protection', value) }) : placeholder('control') }),
|
|
2719
|
+
row({ key: 'connection', icon: 'plug', title: t('home.mcp'), target: 'connection', on: enabled,
|
|
2720
|
+
body: connection ? React.createElement('p', { className: 'dsh-super-code-home-row-status', 'data-tone': state === 'listening' ? 'success' : state === 'error' ? 'error' : 'idle' },
|
|
2721
|
+
React.createElement('i', { 'aria-hidden': true }),
|
|
2722
|
+
React.createElement('span', null, t(`connection.state.${state}`)),
|
|
2723
|
+
listening && React.createElement('span', null, t('home.connectionCount', { count: overview.connections.length, max: connection.configuration.maxConnections })),
|
|
2724
|
+
listening && React.createElement('span', null, t('home.activeSessions', { count: connectionSessions(overview).connected.length }))) : placeholder('text'),
|
|
2725
|
+
control: needsSetup ? React.createElement(ActionButton, { type: 'button', onClick: () => navigate('connection') }, t('home.setupConnection'))
|
|
2726
|
+
: connection ? React.createElement(ConnectionSwitch, { checked: enabled, disabled: !connection.writable, 'aria-label': t('home.mcp'),
|
|
2727
|
+
onChange: value => value && !connection.configuration.allowedDirectories.length ? navigate('connection') : change('connection', value) })
|
|
2728
|
+
: placeholder('control') })))
|
|
2729
|
+
}
|
|
2730
|
+
function ConnectionConsent({ api, t }) {
|
|
2731
|
+
const [requests, setRequests] = useState([]), [busy, setBusy] = useState(false), [error, setError] = useState(false)
|
|
2732
|
+
const dialog = useRef(null), alive = useRef(false), pending = useRef(false), resolving = useRef(false), settled = useRef(new Set())
|
|
2733
|
+
const current = requests[0]
|
|
2734
|
+
const load = async () => {
|
|
2735
|
+
if (pending.current) return
|
|
2736
|
+
pending.current = true
|
|
2737
|
+
try { const result = await api.pendingDirectories(); if (alive.current && result.ok) setRequests(result.value.filter(row => !settled.current.has(row.id))) }
|
|
2738
|
+
catch { /* Transport loss never grants consent. */ }
|
|
2739
|
+
finally { pending.current = false }
|
|
2740
|
+
}
|
|
2741
|
+
useEffect(() => { alive.current = true; load(); const timer = setInterval(load, 1000); return () => { alive.current = false; clearInterval(timer) } }, [api])
|
|
2742
|
+
const resolve = async decision => {
|
|
2743
|
+
if (resolving.current || !current) return
|
|
2744
|
+
resolving.current = true; setBusy(true); setError(false)
|
|
2745
|
+
try {
|
|
2746
|
+
const result = await api.resolveDirectory(current.id, decision)
|
|
2747
|
+
if (!result.ok) throw new Error()
|
|
2748
|
+
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')) }
|
|
2749
|
+
} catch { if (alive.current) setError(true) }
|
|
2750
|
+
finally { resolving.current = false; if (alive.current) setBusy(false) }
|
|
2751
|
+
}
|
|
2752
|
+
useEffect(() => {
|
|
2753
|
+
if (!current || !dialog.current) return
|
|
2754
|
+
setError(false)
|
|
2755
|
+
const element = dialog.current; element.showModal(); element.querySelector('[data-consent-reject]')?.focus()
|
|
2756
|
+
const escape = event => { if (event.key === 'Escape') { event.preventDefault(); event.stopImmediatePropagation(); if (!resolving.current) resolve('rejected') } }
|
|
2757
|
+
window.addEventListener('keydown', escape, true)
|
|
2758
|
+
return () => { window.removeEventListener('keydown', escape, true); if (element.open) element.close() }
|
|
2759
|
+
}, [current?.id])
|
|
2760
|
+
if (!current) return null
|
|
2761
|
+
return React.createElement('dialog', { ref: dialog, className: 'dsh-super-code-confirm', 'aria-labelledby': 'super-code-consent-title',
|
|
2762
|
+
onCancel: event => { event.preventDefault(); event.stopPropagation(); resolve('rejected') } },
|
|
2763
|
+
React.createElement('h3', { id: 'super-code-consent-title' }, t('connection.requestTitle')),
|
|
2764
|
+
React.createElement('p', null, t('connection.requestHint')),
|
|
2765
|
+
React.createElement('dl', { className: 'dsh-super-code-directory-request' },
|
|
2766
|
+
...[['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)))),
|
|
2767
|
+
error && React.createElement('p', { role: 'alert' }, t('connection.requestError')),
|
|
2768
|
+
React.createElement('div', { className: 'dsh-super-code-confirm-actions' },
|
|
2769
|
+
React.createElement(ActionButton, { type: 'button', disabled: busy, 'data-consent-reject': true, onClick: () => resolve('rejected') }, t('connection.rejectDirectory')),
|
|
2770
|
+
React.createElement(ActionButton, { type: 'button', disabled: busy, className: 'dsh-super-code-primary-button', onClick: () => resolve('approved') }, t('connection.approveDirectory'))))
|
|
2771
|
+
}
|
|
2772
|
+
|
|
2773
|
+
function PresetSettings({ api, budgetApi, connectionApi, openSession = () => {}, t }) {
|
|
2774
|
+
const [tab, setTab] = useState('home')
|
|
2156
2775
|
const [status, setStatus] = useState(null), [customId, setCustomId] = useState(null)
|
|
2157
2776
|
const reinstall = status?.reinstallable ?? status?.state === 'installed'
|
|
2158
2777
|
const name = customId ?? (reinstall ? status.id : 'super-code')
|
|
@@ -2217,41 +2836,55 @@ function PresetSettings({ api, budgetApi, t }) {
|
|
|
2217
2836
|
}
|
|
2218
2837
|
const available = status && ['available', 'installed'].includes(status.state)
|
|
2219
2838
|
const versionState = status?.presetVersion ? status.presetVersion === status.pluginVersion ? 'match' : 'mismatch' : 'unknown'
|
|
2839
|
+
const stateProblem = Boolean(status) && ['missing', 'broken', 'conflict'].includes(status.state)
|
|
2840
|
+
const versionProblem = Boolean(status) && status.state !== 'missing' && versionState !== 'match'
|
|
2841
|
+
const [overviewRevision, setOverviewRevision] = useState(0)
|
|
2842
|
+
const onSaved = () => setOverviewRevision(value => value + 1)
|
|
2843
|
+
const tabs = ['home', 'preset', 'context', 'protection', 'connection']
|
|
2220
2844
|
return React.createElement('section', { className: 'dsh-super-code-settings' },
|
|
2221
2845
|
React.createElement('div', { className: 'dsh-super-code-settings-tabs', role: 'tablist', 'aria-label': t('settings.tabs'), onKeyDown: event => {
|
|
2222
2846
|
if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) return
|
|
2223
2847
|
event.preventDefault()
|
|
2224
|
-
const selected = event.key === 'Home' ?
|
|
2848
|
+
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]
|
|
2225
2849
|
setTab(selected)
|
|
2226
2850
|
event.currentTarget.querySelector(`#super-code-tab-${selected}`)?.focus()
|
|
2227
|
-
} }, ...
|
|
2851
|
+
} }, ...tabs.map(value => React.createElement('button', { key: value, type: 'button', role: 'tab',
|
|
2228
2852
|
id: `super-code-tab-${value}`, 'aria-selected': tab === value, 'aria-controls': `super-code-panel-${value}`,
|
|
2229
2853
|
tabIndex: tab === value ? 0 : -1, onClick: () => setTab(value) }, t(`settings.${value}Tab`)))),
|
|
2230
2854
|
React.createElement('div', { className: 'dsh-super-code-settings-body' },
|
|
2231
|
-
React.createElement('div', { role: 'tabpanel', id: 'super-code-panel-
|
|
2855
|
+
React.createElement('div', { role: 'tabpanel', id: 'super-code-panel-home', 'aria-labelledby': 'super-code-tab-home', hidden: tab !== 'home' },
|
|
2856
|
+
React.createElement(SettingsHome, { budgetApi, connectionApi, t, active: tab === 'home', revision: overviewRevision, navigate: setTab },
|
|
2857
|
+
// Healthy state collapses to one status line; problems expand into a notice with its fix.
|
|
2232
2858
|
React.createElement('header', { className: 'dsh-super-code-settings-header' },
|
|
2233
2859
|
React.createElement('span', { className: 'dsh-super-code-settings-mark', 'aria-hidden': true }, React.createElement(Glyph, { type: 'code' })),
|
|
2234
2860
|
React.createElement('div', { className: 'dsh-super-code-settings-head-text' },
|
|
2235
2861
|
React.createElement('h2', { className: 'dsh-super-code-settings-name' }, t('preset.title')),
|
|
2236
|
-
React.createElement('
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
React.createElement('
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
React.createElement(
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2862
|
+
React.createElement('div', { className: 'dsh-super-code-settings-status-row' },
|
|
2863
|
+
status ? React.createElement('p', { className: 'dsh-super-code-preset-status', role: 'status', 'data-available': available },
|
|
2864
|
+
React.createElement('i', { 'aria-hidden': true }),
|
|
2865
|
+
React.createElement('span', null, t(status.delivery === 'declaration' && status.state === 'installed' ? 'preset.state.declaredInstalled' : `preset.state.${status.state}`)),
|
|
2866
|
+
React.createElement('code', null, status.id))
|
|
2867
|
+
: React.createElement('p', { className: 'dsh-super-code-preset-status', role: 'status' }, busy ? t('preset.working') : t('preset.unavailableHint')),
|
|
2868
|
+
status && React.createElement('dl', { className: 'dsh-super-code-preset-meta' },
|
|
2869
|
+
React.createElement('div', null, React.createElement('dt', null, t('preset.pluginVersion')), React.createElement('dd', null, status.pluginVersion)),
|
|
2870
|
+
React.createElement('div', null, React.createElement('dt', null, t('preset.presetVersion')), React.createElement('dd', { 'data-stale': status.state !== 'missing' && versionState !== 'match' || undefined }, status.state === 'missing' ? t('preset.versionMissing') : status.presetVersion || t('preset.versionUnknown')))),
|
|
2871
|
+
status && status.state !== 'missing' && versionState === 'match' && React.createElement('span', { className: 'dsh-super-code-version-badge', 'data-version-state': 'match', title: t('preset.versionMatches') }, t('preset.versionBadge')))),
|
|
2872
|
+
React.createElement('div', { className: 'dsh-super-code-settings-actions' },
|
|
2873
|
+
React.createElement(ActionButton, { type: 'button', className: 'dsh-super-code-icon-button', disabled: busy, onClick: () => run(false), 'aria-label': t('preset.refresh') },
|
|
2874
|
+
React.createElement(IconRefreshOutline16, { 'aria-hidden': true })))),
|
|
2875
|
+
(stateProblem || versionProblem) && React.createElement('div', { className: 'dsh-super-code-preset-notice', 'data-tone': status.state === 'broken' ? 'error' : 'warning' },
|
|
2876
|
+
React.createElement(Glyph, { type: 'error' }),
|
|
2877
|
+
React.createElement('div', { className: 'dsh-super-code-preset-notice-text' },
|
|
2878
|
+
stateProblem && React.createElement('p', null, t(status.authorable ? 'preset.installHint' : 'preset.unavailableHint')),
|
|
2879
|
+
versionProblem && React.createElement('p', { 'data-version-state': versionState },
|
|
2880
|
+
t(versionState === 'mismatch' ? 'preset.versionMismatch' : 'preset.versionUnknownHint')),
|
|
2881
|
+
// The fix goes on its own line, so no locale-specific sentence joiner is needed.
|
|
2882
|
+
versionProblem && versionState === 'mismatch' && React.createElement('p', { className: 'dsh-super-code-preset-notice-fix' },
|
|
2883
|
+
t(reinstall && status.authorable ? 'preset.versionReinstallHint' : 'preset.versionReloadHint'))),
|
|
2884
|
+
status.authorable && React.createElement(ActionButton, { type: 'button', className: 'dsh-super-code-primary-button', onClick: () => setTab('preset') },
|
|
2885
|
+
t(status.state === 'missing' ? status.delivery === 'declaration' ? 'preset.installDeclared' : 'preset.install' : 'home.managePreset'))))),
|
|
2886
|
+
React.createElement('div', { role: 'tabpanel', id: 'super-code-panel-preset', 'aria-labelledby': 'super-code-tab-preset', hidden: tab !== 'preset' },
|
|
2253
2887
|
status?.authorable && React.createElement('form', { onSubmit: event => { event.preventDefault(); submit() } },
|
|
2254
|
-
React.createElement('h3', null, t('preset.configuration')),
|
|
2255
2888
|
React.createElement('div', { className: 'dsh-super-code-settings-field' },
|
|
2256
2889
|
React.createElement('label', { htmlFor: 'super-code-display-name' }, t('preset.displayName')),
|
|
2257
2890
|
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) }),
|
|
@@ -2271,7 +2904,11 @@ function PresetSettings({ api, budgetApi, t }) {
|
|
|
2271
2904
|
status?.delivery === 'directory' && !status.authorable && React.createElement('p', { role: 'note' }, t('preset.readonly'))),
|
|
2272
2905
|
error && React.createElement('p', { role: 'alert' }, t(error))),
|
|
2273
2906
|
React.createElement('div', { role: 'tabpanel', id: 'super-code-panel-context', 'aria-labelledby': 'super-code-tab-context', hidden: tab !== 'context' },
|
|
2274
|
-
React.createElement(BudgetSettings, { api: budgetApi, t, active: tab === 'context' }))
|
|
2907
|
+
React.createElement(BudgetSettings, { api: budgetApi, t, active: tab === 'context', onSaved })),
|
|
2908
|
+
React.createElement('div', { role: 'tabpanel', id: 'super-code-panel-protection', 'aria-labelledby': 'super-code-tab-protection', hidden: tab !== 'protection' },
|
|
2909
|
+
React.createElement(BudgetSettings, { api: budgetApi, t, section: 'protection', active: tab === 'protection', onSaved })),
|
|
2910
|
+
React.createElement('div', { role: 'tabpanel', id: 'super-code-panel-connection', 'aria-labelledby': 'super-code-tab-connection', hidden: tab !== 'connection' },
|
|
2911
|
+
React.createElement(ConnectionSettings, { api: connectionApi, t, active: tab === 'connection', openSession, onSaved }))),
|
|
2275
2912
|
confirmation && React.createElement('dialog', { ref: dialog, className: 'dsh-super-code-confirm',
|
|
2276
2913
|
'aria-labelledby': 'super-code-confirm-title', 'aria-describedby': 'super-code-confirm-hint',
|
|
2277
2914
|
onKeyDown: event => { if (event.key === 'Escape') event.stopPropagation() },
|
|
@@ -2309,20 +2946,34 @@ function apply(ctx) {
|
|
|
2309
2946
|
// $mount creates a lifecycle effect on this same fiber; awaiting it from
|
|
2310
2947
|
// another effect would stall the namespace and the settings contribution.
|
|
2311
2948
|
void ctx.remote.$mount({ package: 'dsh-super-code', descriptors: [
|
|
2312
|
-
...presetRemoteContribution.descriptors, ...memoryRemoteContribution.descriptors, ...budgetRemoteContribution.descriptors,
|
|
2949
|
+
...presetRemoteContribution.descriptors, ...memoryRemoteContribution.descriptors, ...budgetRemoteContribution.descriptors, ...connectionRemoteContribution.descriptors,
|
|
2313
2950
|
] }).catch(error => console.warn('Super Code Remote:', error))
|
|
2314
2951
|
const memoryStates = new WeakMap(), workspaceStates = new WeakMap()
|
|
2952
|
+
const navigation = { openSession: () => {} }
|
|
2953
|
+
ctx.inject(['uiWorkspace'], ready => {
|
|
2954
|
+
navigation.openSession = id => {
|
|
2955
|
+
if (typeof ready.uiWorkspace.openSession === 'function') ready.uiWorkspace.openSession(id)
|
|
2956
|
+
else ready.sessions.open(id)
|
|
2957
|
+
}
|
|
2958
|
+
ready.effect(() => () => { navigation.openSession = () => {} })
|
|
2959
|
+
})
|
|
2960
|
+
ctx.inject(['remote.superCodeConnection'], ready => {
|
|
2961
|
+
ready.slots.inject('shell.overlay', () => ready.slots.register({
|
|
2962
|
+
name: 'shell.overlay', id: 'super-code-directory-consent', order: 180, locale: LOCALE_NS,
|
|
2963
|
+
inject: () => ({ api: ready.remote.superCodeConnection }),
|
|
2964
|
+
}, ConnectionConsent))
|
|
2965
|
+
})
|
|
2315
2966
|
// Locale already requires its host's preference service. Declare that same
|
|
2316
2967
|
// service here: Cordis rejects even optional reads without an injection.
|
|
2317
2968
|
const preferenceService = 'configForms' in ctx ? 'configForms' : 'settingsScope'
|
|
2318
|
-
ctx.inject(['remote.superCodePresets', 'remote.superCodeBudgets', preferenceService], ready => {
|
|
2969
|
+
ctx.inject(['remote.superCodePresets', 'remote.superCodeBudgets', 'remote.superCodeConnection', preferenceService], ready => {
|
|
2319
2970
|
const preferenceScope = preferenceService === 'configForms'
|
|
2320
2971
|
? ready.configForms.get('locale') : ready.settingsScope.bind({ namespace: 'locale' })
|
|
2321
2972
|
ready.effect(() => synchronizePresetDisplay(ready.remote.superCodePresets, locale,
|
|
2322
2973
|
() => window.location.reload(), error => console.warn('Super Code preset display:', error), preferenceScope), 'super-code: preset display')
|
|
2323
2974
|
ready.slots.inject('settings.section', () => ready.slots.register({
|
|
2324
2975
|
name: 'settings.section', id: 'super-code', order: 120, label: () => t('preset.title'), locale: LOCALE_NS,
|
|
2325
|
-
inject: () => ({ api: ready.remote.superCodePresets, budgetApi: ready.remote.superCodeBudgets }),
|
|
2976
|
+
inject: () => ({ api: ready.remote.superCodePresets, budgetApi: ready.remote.superCodeBudgets, connectionApi: ready.remote.superCodeConnection, openSession: id => navigation.openSession(id) }),
|
|
2326
2977
|
}, PresetSettings))
|
|
2327
2978
|
})
|
|
2328
2979
|
ctx.effect(() => {
|
|
@@ -2343,6 +2994,11 @@ function apply(ctx) {
|
|
|
2343
2994
|
--sc-success:var(--dsw-color-text-success,#197661);
|
|
2344
2995
|
--sc-warning:var(--dsw-color-text-warning,#9c650c);
|
|
2345
2996
|
--sc-error:var(--dsw-color-text-error,#bb4545);
|
|
2997
|
+
--sc-active:var(--sc-accent);
|
|
2998
|
+
--sc-idle:var(--dsw-alias-state-idle-primary,var(--sc-faint));
|
|
2999
|
+
--sc-on-accent:var(--dsw-alias-label-primary-foreground,var(--sc-bg));
|
|
3000
|
+
--sc-shadow:color-mix(in srgb,var(--sc-text) 14%,transparent);
|
|
3001
|
+
--sc-radius:6px;
|
|
2346
3002
|
--sc-mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
|
|
2347
3003
|
--agent-muted:var(--sc-muted);--agent-line:var(--sc-line);
|
|
2348
3004
|
color:var(--sc-text);font-size:14px;line-height:1.55;letter-spacing:0;
|
|
@@ -2371,9 +3027,7 @@ function apply(ctx) {
|
|
|
2371
3027
|
.dsh-super-code-settings-mark svg{width:22px;height:22px;color:var(--sc-text)}
|
|
2372
3028
|
.dsh-super-code-settings-head-text{display:grid;gap:4px;min-width:0}
|
|
2373
3029
|
.dsh-super-code-settings-name{font-size:16px;font-weight:600;line-height:1.4;margin:0}
|
|
2374
|
-
.dsh-super-code-settings-description{font-size:13px;color:var(--sc-muted);margin:0}
|
|
2375
3030
|
.dsh-super-code-settings-body{padding:20px 24px 24px}
|
|
2376
|
-
.dsh-super-code-settings-summary{padding:16px;border-radius:12px;background:var(--sc-surface)}
|
|
2377
3031
|
.dsh-super-code-settings-status-row{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;flex-wrap:wrap}
|
|
2378
3032
|
.dsh-super-code-preset-status{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:4px 0;font-size:13px;min-width:0;flex:1}
|
|
2379
3033
|
.dsh-super-code-preset-status>i{width:7px;height:7px;flex:none;border-radius:50%;background:var(--sc-warning)}
|
|
@@ -2381,15 +3035,12 @@ function apply(ctx) {
|
|
|
2381
3035
|
.dsh-super-code-preset-status>span{font-weight:500}
|
|
2382
3036
|
.dsh-super-code-preset-status code{color:var(--sc-muted);overflow-wrap:anywhere}
|
|
2383
3037
|
.dsh-super-code-settings-actions{display:flex;flex:none;margin-left:auto}
|
|
2384
|
-
|
|
2385
|
-
.dsh-super-code-settings-actions button
|
|
3038
|
+
/* One button family: secondary by default, primary via class; 32px in forms, 28px in cards. */
|
|
3039
|
+
.dsh-super-code-settings-actions button,.dsh-super-code-settings-form-actions button,.dsh-super-code-confirm button,.dsh-super-code-home-row-control>button,.dsh-super-code-preset-notice>button,.dsh-super-code-connection-activity button{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:32px;border:1px solid var(--sc-line);border-radius:var(--sc-radius);background:var(--sc-bg);color:var(--sc-text);padding:5px 12px;font:inherit;font-size:13px;cursor:pointer;transition:background .12s,border-color .12s}
|
|
3040
|
+
.dsh-super-code-settings-actions button:hover,.dsh-super-code-settings-form-actions button:hover,.dsh-super-code-confirm button:hover,.dsh-super-code-home-row-control>button:hover,.dsh-super-code-preset-notice>button:hover,.dsh-super-code-connection-activity button:hover{background:var(--sc-hover);border-color:color-mix(in srgb,var(--sc-text) 22%,var(--sc-line))}
|
|
3041
|
+
:is(.dsh-super-code-settings,.dsh-super-code-confirm) button:focus-visible{outline:2px solid var(--sc-accent);outline-offset:2px}
|
|
3042
|
+
:is(.dsh-super-code-settings,.dsh-super-code-confirm) button:disabled{opacity:.5;cursor:default}
|
|
2386
3043
|
.dsh-super-code-settings-actions svg{width:14px;height:14px}
|
|
2387
|
-
.dsh-super-code-preset-versions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px;margin:16px 0 0;padding-top:16px;border-top:1px solid var(--sc-line)}
|
|
2388
|
-
.dsh-super-code-preset-versions>div+div{border-left:1px solid var(--sc-line);padding-left:16px}
|
|
2389
|
-
.dsh-super-code-preset-versions dt{font-size:12px;color:var(--sc-muted)}
|
|
2390
|
-
.dsh-super-code-preset-versions dd{font-family:var(--sc-mono);font-variant-numeric:tabular-nums;font-size:15px;margin:4px 0 0;overflow-wrap:anywhere}
|
|
2391
|
-
.dsh-super-code-version-hint{font-size:12px;color:var(--sc-muted);margin:12px 0 0;overflow-wrap:anywhere}
|
|
2392
|
-
.dsh-super-code-version-hint[data-version-state=mismatch],.dsh-super-code-version-hint[data-version-state=unknown]{padding:10px 12px;background:color-mix(in srgb,var(--sc-warning) 8%,var(--sc-bg));border-left:2px solid var(--sc-warning);border-radius:0 6px 6px 0;color:var(--sc-text)}
|
|
2393
3044
|
.dsh-super-code-settings form{display:grid;gap:16px;margin-top:24px}
|
|
2394
3045
|
.dsh-super-code-settings form>h3{margin:0;font-size:14px;font-weight:600}
|
|
2395
3046
|
.dsh-super-code-settings-field{display:grid;gap:6px}
|
|
@@ -2398,8 +3049,8 @@ function apply(ctx) {
|
|
|
2398
3049
|
.dsh-super-code-settings-form-actions{display:flex;align-items:center;justify-content:flex-end;flex-wrap:wrap;gap:12px;padding-top:4px}
|
|
2399
3050
|
.dsh-super-code-settings-form-actions>small{flex:1;min-width:220px}
|
|
2400
3051
|
.dsh-super-code-settings-form-actions>button{margin-left:auto;flex:none}
|
|
2401
|
-
.dsh-super-code-settings .dsh-super-code-primary-button,.dsh-super-code-confirm .dsh-super-code-primary-button{background:var(--sc-
|
|
2402
|
-
.dsh-super-code-settings .dsh-super-code-primary-button:hover,.dsh-super-code-confirm .dsh-super-code-primary-button:hover{background:color-mix(in srgb,var(--sc-text)
|
|
3052
|
+
.dsh-super-code-settings .dsh-super-code-primary-button,.dsh-super-code-confirm .dsh-super-code-primary-button{background:var(--sc-accent);border-color:var(--sc-accent);color:var(--sc-on-accent);font-weight:500}
|
|
3053
|
+
.dsh-super-code-settings .dsh-super-code-primary-button:hover:not(:disabled),.dsh-super-code-confirm .dsh-super-code-primary-button:hover:not(:disabled){background:color-mix(in srgb,var(--sc-accent) 86%,var(--sc-text));border-color:color-mix(in srgb,var(--sc-accent) 86%,var(--sc-text))}
|
|
2403
3054
|
.dsh-super-code-settings-guidance:empty{display:none}
|
|
2404
3055
|
.dsh-super-code-settings-guidance{margin-top:20px;padding-top:12px;border-top:1px solid var(--sc-line);color:var(--sc-muted);font-size:12px}
|
|
2405
3056
|
.dsh-super-code-settings-guidance p{margin:0 0 4px}
|
|
@@ -2408,13 +3059,95 @@ function apply(ctx) {
|
|
|
2408
3059
|
.dsh-super-code-settings-tabs>[role=tab][aria-selected=true]{border-bottom-color:var(--sc-text);color:var(--sc-text);font-weight:600}
|
|
2409
3060
|
.dsh-super-code-settings-tabs>[role=tab]:focus-visible{outline:2px solid var(--sc-active);outline-offset:3px;border-radius:2px}
|
|
2410
3061
|
.dsh-super-code-settings [role=tabpanel][hidden]{display:none}
|
|
2411
|
-
.
|
|
2412
|
-
.dsh-super-code-
|
|
2413
|
-
.dsh-super-code-
|
|
2414
|
-
.dsh-super-code-
|
|
2415
|
-
.dsh-super-code-
|
|
2416
|
-
.dsh-super-code-
|
|
2417
|
-
.dsh-super-code-
|
|
3062
|
+
/* Settings primitives: stacked sections of label/control rows, callouts, autosave status. */
|
|
3063
|
+
.dsh-super-code-settings form.dsh-super-code-stack,.dsh-super-code-stack{display:grid;gap:24px;margin:0;min-width:0}
|
|
3064
|
+
.dsh-super-code-section{display:grid;gap:12px;min-width:0}
|
|
3065
|
+
.dsh-super-code-section-head{display:flex;align-items:flex-end;justify-content:space-between;gap:12px}
|
|
3066
|
+
.dsh-super-code-section-heading{display:grid;gap:4px;min-width:0}
|
|
3067
|
+
.dsh-super-code-section-heading h3{margin:0;font-size:14px;font-weight:600;line-height:1.4}
|
|
3068
|
+
.dsh-super-code-section-heading p{margin:0;font-size:12px;line-height:1.6;color:var(--sc-muted);text-wrap:pretty}
|
|
3069
|
+
.dsh-super-code-section-actions{display:flex;flex:none;gap:8px}
|
|
3070
|
+
.dsh-super-code-section-body{border:1px solid var(--sc-line);border-radius:12px;background:var(--sc-bg);min-width:0}
|
|
3071
|
+
.dsh-super-code-section-body[data-plain]{border:0;border-radius:0;background:none}
|
|
3072
|
+
.dsh-super-code-setting-row{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:8px 24px;padding:12px 16px}
|
|
3073
|
+
.dsh-super-code-section-body>*+.dsh-super-code-setting-row,.dsh-super-code-section-body>*+.dsh-super-code-section-more,.dsh-super-code-section-more>.dsh-super-code-setting-row{border-top:1px solid var(--sc-line)}
|
|
3074
|
+
.dsh-super-code-setting-row-text{display:grid;gap:2px;min-width:0}
|
|
3075
|
+
.dsh-super-code-settings .dsh-super-code-setting-row-text>label{font-size:13px;font-weight:500;line-height:1.5}
|
|
3076
|
+
.dsh-super-code-setting-row-text>p{margin:0;font-size:12px;line-height:1.6;color:var(--sc-muted);overflow-wrap:anywhere;text-wrap:pretty}
|
|
3077
|
+
.dsh-super-code-setting-row-text>.dsh-super-code-setting-row-error{color:var(--sc-error)}
|
|
3078
|
+
.dsh-super-code-setting-row-control{display:flex;justify-content:flex-end;min-width:0}
|
|
3079
|
+
.dsh-super-code-setting-row-control>:is(input,.dsh-super-code-select){width:180px}
|
|
3080
|
+
.dsh-super-code-setting-row-control>input{height:32px;padding:5px 10px;border-radius:8px;font-variant-numeric:tabular-nums;text-align:right}
|
|
3081
|
+
.dsh-super-code-setting-row[data-invalid] input{border-color:var(--sc-error)}
|
|
3082
|
+
.dsh-super-code-settings input[type=number]{appearance:textfield}
|
|
3083
|
+
.dsh-super-code-settings input::-webkit-inner-spin-button{appearance:none}
|
|
3084
|
+
.dsh-super-code-section-more>summary{display:flex;align-items:center;gap:6px;padding:10px 16px;list-style:none;cursor:pointer;font-size:13px;color:var(--sc-muted)}
|
|
3085
|
+
.dsh-super-code-section-more>summary::-webkit-details-marker{display:none}
|
|
3086
|
+
.dsh-super-code-section-more>summary:hover{color:var(--sc-text)}
|
|
3087
|
+
.dsh-super-code-section-more>summary .dsh-super-code-glyph{width:14px;height:14px;transform:rotate(-90deg);transition:transform .15s}
|
|
3088
|
+
.dsh-super-code-section-more[open]>summary .dsh-super-code-glyph{transform:none}
|
|
3089
|
+
.dsh-super-code-callout{--tone:var(--sc-accent);display:flex;align-items:flex-start;gap:10px;padding:12px 14px;border:1px solid color-mix(in srgb,var(--tone) 26%,var(--sc-line));border-radius:8px;background:color-mix(in srgb,var(--tone) 6%,var(--sc-bg))}
|
|
3090
|
+
.dsh-super-code-callout[data-tone=warning]{--tone:var(--sc-warning)}
|
|
3091
|
+
.dsh-super-code-callout[data-tone=error]{--tone:var(--sc-error)}
|
|
3092
|
+
.dsh-super-code-callout[data-tone=success]{--tone:var(--sc-success)}
|
|
3093
|
+
.dsh-super-code-callout>.dsh-super-code-glyph{width:16px;height:16px;margin-top:2px;color:var(--tone)}
|
|
3094
|
+
.dsh-super-code-callout-text{display:grid;gap:4px;flex:1;min-width:0}
|
|
3095
|
+
.dsh-super-code-callout-text p{margin:0;font-size:12px;line-height:1.6;color:var(--sc-text);overflow-wrap:anywhere;text-wrap:pretty}
|
|
3096
|
+
.dsh-super-code-callout-text p+p{color:var(--sc-muted)}
|
|
3097
|
+
.dsh-super-code-callout>button{flex:none;align-self:center;min-height:28px;padding:3px 12px;font-size:12px}
|
|
3098
|
+
.dsh-super-code-settings .dsh-super-code-callout[role=alert]{font-size:inherit;color:inherit}
|
|
3099
|
+
.dsh-super-code-save-indicator{display:inline-flex;align-items:center;gap:6px;margin:0;font-size:12px;color:var(--sc-muted)}
|
|
3100
|
+
.dsh-super-code-save-indicator>i{width:6px;height:6px;border-radius:50%;background:var(--sc-idle);transition:background .15s}
|
|
3101
|
+
.dsh-super-code-save-indicator[data-save-state=saving]>i{background:var(--sc-accent);animation:sc-pulse 1s ease-in-out infinite}
|
|
3102
|
+
.dsh-super-code-save-indicator[data-save-state=saved]>i{background:var(--sc-success)}
|
|
3103
|
+
.dsh-super-code-save-indicator[data-save-state=invalid]{color:var(--sc-warning)}
|
|
3104
|
+
.dsh-super-code-save-indicator[data-save-state=invalid]>i{background:var(--sc-warning)}
|
|
3105
|
+
.dsh-super-code-save-indicator[data-save-state=error]{color:var(--sc-error)}
|
|
3106
|
+
.dsh-super-code-save-indicator[data-save-state=error]>i{background:var(--sc-error)}
|
|
3107
|
+
@keyframes sc-pulse{50%{opacity:.35}}
|
|
3108
|
+
.dsh-super-code-settings-footer{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px 12px;padding-top:16px;border-top:1px solid var(--sc-line)}
|
|
3109
|
+
.dsh-super-code-settings-footer-actions{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin-left:auto;font-size:12px;color:var(--sc-muted)}
|
|
3110
|
+
.dsh-super-code-settings-footer-actions button{display:inline-flex;align-items:center;min-height:28px;border:1px solid var(--sc-line);border-radius:var(--sc-radius);background:var(--sc-bg);padding:3px 12px;font-size:12px}
|
|
3111
|
+
.dsh-super-code-settings-footer-actions button:hover:not(:disabled){background:var(--sc-hover)}
|
|
3112
|
+
.dsh-super-code-settings .dsh-super-code-danger-button{color:var(--sc-error);border-color:color-mix(in srgb,var(--sc-error) 35%,var(--sc-line))}
|
|
3113
|
+
.dsh-super-code-settings .dsh-super-code-danger-button:hover:not(:disabled){background:color-mix(in srgb,var(--sc-error) 8%,var(--sc-bg))}
|
|
3114
|
+
.dsh-super-code-choice-cards{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}
|
|
3115
|
+
.dsh-super-code-settings .dsh-super-code-choice-card{position:relative;display:flex;align-items:flex-start;gap:10px;padding:12px 14px;border:1px solid var(--sc-line);border-radius:12px;background:var(--sc-bg);cursor:pointer;font-weight:400;transition:border-color .12s,background .12s}
|
|
3116
|
+
.dsh-super-code-choice-card:hover{border-color:color-mix(in srgb,var(--sc-text) 22%,var(--sc-line))}
|
|
3117
|
+
.dsh-super-code-choice-card[data-checked]{border-color:var(--sc-accent);background:var(--sc-selected)}
|
|
3118
|
+
.dsh-super-code-choice-card>input{position:absolute;opacity:0;inset:0;width:100%;height:100%;margin:0;cursor:pointer}
|
|
3119
|
+
.dsh-super-code-choice-card>input:disabled{cursor:default}
|
|
3120
|
+
.dsh-super-code-choice-card:has(input:disabled){opacity:.55;cursor:default}
|
|
3121
|
+
.dsh-super-code-choice-card:has(input:focus-visible){outline:2px solid var(--sc-accent);outline-offset:2px}
|
|
3122
|
+
.dsh-super-code-choice-card>i{flex:none;width:16px;height:16px;margin-top:2px;border:1.5px solid var(--sc-line);border-radius:50%;background:var(--sc-bg);transition:border-color .12s,box-shadow .12s}
|
|
3123
|
+
.dsh-super-code-choice-card[data-checked]>i{border-color:var(--sc-accent);box-shadow:inset 0 0 0 3px var(--sc-bg),inset 0 0 0 8px var(--sc-accent)}
|
|
3124
|
+
.dsh-super-code-choice-card-text{display:grid;gap:4px;min-width:0}
|
|
3125
|
+
.dsh-super-code-choice-card-text strong{font-size:13px;font-weight:600}
|
|
3126
|
+
.dsh-super-code-choice-card-text small{font-size:12px;line-height:1.6;color:var(--sc-muted);text-wrap:pretty}
|
|
3127
|
+
@container(max-width:560px){.dsh-super-code-choice-cards{grid-template-columns:1fr}}
|
|
3128
|
+
@container(max-width:480px){.dsh-super-code-setting-row:not(:has(.dsh-super-code-connection-switch)){grid-template-columns:minmax(0,1fr)}.dsh-super-code-setting-row:not(:has(.dsh-super-code-connection-switch)) .dsh-super-code-setting-row-control{justify-content:flex-start}.dsh-super-code-setting-row-control>:is(input,.dsh-super-code-select){width:100%;text-align:left}}
|
|
3129
|
+
|
|
3130
|
+
.dsh-super-code-connection{display:grid;gap:18px}
|
|
3131
|
+
.dsh-super-code-connection form{display:grid;gap:16px}
|
|
3132
|
+
.dsh-super-code-connection-hint{margin:0;color:var(--sc-muted);font-size:12px;line-height:1.6}
|
|
3133
|
+
.dsh-super-code-connection-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px}
|
|
3134
|
+
.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}
|
|
3135
|
+
.dsh-super-code-connection-client{display:grid;gap:12px;border-top:1px solid var(--sc-line);padding-top:18px;min-width:0}
|
|
3136
|
+
.dsh-super-code-connection-client h3{margin:0;font-size:13px}
|
|
3137
|
+
.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}
|
|
3138
|
+
.dsh-super-code-connection small{color:var(--sc-muted);font-size:12px;line-height:1.6}
|
|
3139
|
+
.dsh-super-code-connection .dsh-super-code-settings-actions{flex-wrap:wrap;gap:8px}
|
|
3140
|
+
.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}
|
|
3141
|
+
.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}
|
|
3142
|
+
.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}
|
|
3143
|
+
.dsh-super-code-connection-track i{display:block;width:14px;height:14px;border-radius:50%;background:var(--sc-muted);transition:transform .15s}
|
|
3144
|
+
.dsh-super-code-connection-switch input:checked+.dsh-super-code-connection-track{background:var(--sc-accent);border-color:var(--sc-accent)}
|
|
3145
|
+
.dsh-super-code-connection-switch input:checked+.dsh-super-code-connection-track i{transform:translateX(14px);background:var(--sc-on-accent)}
|
|
3146
|
+
.dsh-super-code-connection-switch input:focus-visible+.dsh-super-code-connection-track{outline:2px solid var(--sc-accent);outline-offset:3px}
|
|
3147
|
+
.dsh-super-code-connection-switch input:disabled+.dsh-super-code-connection-track{opacity:.5}
|
|
3148
|
+
.dsh-super-code-connection input[type=number]{appearance:textfield}
|
|
3149
|
+
.dsh-super-code-connection input::-webkit-inner-spin-button{appearance:none}
|
|
3150
|
+
|
|
2418
3151
|
.dsh-super-code-budget-default{color:var(--sc-muted);font-weight:400;font-size:12px}
|
|
2419
3152
|
.dsh-super-code-budgets fieldset{min-width:0;margin:0;border:0;padding:0}
|
|
2420
3153
|
.dsh-super-code-budgets legend{font-size:13px;font-weight:500;margin-bottom:12px;padding:0}
|
|
@@ -2425,7 +3158,106 @@ function apply(ctx) {
|
|
|
2425
3158
|
.dsh-super-code-budgets .dsh-super-code-settings-form-actions{border-top:1px solid var(--sc-line);padding-top:16px}
|
|
2426
3159
|
@container(max-width:420px){.dsh-super-code-budget-grid{grid-template-columns:1fr}.dsh-super-code-settings-tabs{padding:0 16px}}
|
|
2427
3160
|
.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}
|
|
2428
|
-
@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-
|
|
3161
|
+
@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-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%}}
|
|
3162
|
+
|
|
3163
|
+
.dsh-super-code-connection-activity{display:grid;gap:10px;border:1px solid var(--sc-line);border-radius:var(--sc-radius);padding:14px}
|
|
3164
|
+
.dsh-super-code-connection-activity h3{margin:0;font-size:13px;font-weight:600}
|
|
3165
|
+
.dsh-super-code-connection-activity h3:not(:first-child){margin-top:6px}
|
|
3166
|
+
.dsh-super-code-connection-activity h4{margin:0 0 6px;font-size:12px;color:var(--sc-muted);font-weight:500}
|
|
3167
|
+
.dsh-super-code-connection-activity p{margin:0;font-size:12px;color:var(--sc-muted)}
|
|
3168
|
+
.dsh-super-code-connection-activity button{min-height:28px;padding:3px 10px;font-size:12px}
|
|
3169
|
+
/* Settings overview and directory consent use the same host theme aliases. */
|
|
3170
|
+
.dsh-super-code-settings-tabs{gap:20px;overflow-x:auto;white-space:nowrap}
|
|
3171
|
+
/* Home: one status header, then a settings list whose rows act in place and open details. */
|
|
3172
|
+
.dsh-super-code-home{display:grid;gap:16px}
|
|
3173
|
+
.dsh-super-code-home .dsh-super-code-settings-header{align-items:flex-start;padding-bottom:16px;margin-bottom:0}
|
|
3174
|
+
.dsh-super-code-home .dsh-super-code-settings-head-text{flex:1}
|
|
3175
|
+
.dsh-super-code-settings-head-text .dsh-super-code-settings-status-row{justify-content:flex-start;align-items:center;gap:2px 10px}
|
|
3176
|
+
.dsh-super-code-settings-head-text .dsh-super-code-preset-status{flex:none;margin:0;font-size:12px;gap:6px}
|
|
3177
|
+
.dsh-super-code-settings-head-text .dsh-super-code-preset-status>span{font-weight:400;color:var(--sc-muted)}
|
|
3178
|
+
.dsh-super-code-settings-head-text .dsh-super-code-preset-status code{font:12px var(--sc-mono)}
|
|
3179
|
+
.dsh-super-code-preset-meta{display:flex;flex-wrap:wrap;gap:0 10px;margin:0;font-size:12px;color:var(--sc-muted)}
|
|
3180
|
+
.dsh-super-code-preset-meta>div{display:flex;gap:4px}
|
|
3181
|
+
.dsh-super-code-preset-meta>div:before{content:"·";color:var(--sc-faint);margin-right:6px}
|
|
3182
|
+
.dsh-super-code-preset-meta dd{margin:0;font-family:var(--sc-mono);font-variant-numeric:tabular-nums;color:var(--sc-text)}
|
|
3183
|
+
.dsh-super-code-preset-meta dd[data-stale]{color:var(--sc-warning);font-weight:600}
|
|
3184
|
+
.dsh-super-code-version-badge{display:inline-flex;align-items:center;height:18px;padding:0 7px;border-radius:9px;font-size:11px;font-weight:500;color:var(--sc-success);background:color-mix(in srgb,var(--sc-success) 10%,transparent)}
|
|
3185
|
+
.dsh-super-code-settings-actions>.dsh-super-code-icon-button{min-height:0;width:30px;height:30px;padding:0;border:0;background:transparent;color:var(--sc-muted)}
|
|
3186
|
+
.dsh-super-code-settings-actions>.dsh-super-code-icon-button:hover{background:var(--sc-hover);color:var(--sc-text)}
|
|
3187
|
+
.dsh-super-code-settings-actions>.dsh-super-code-icon-button svg{width:16px;height:16px}
|
|
3188
|
+
.dsh-super-code-preset-notice{display:flex;align-items:flex-start;gap:10px;padding:12px 14px;border:1px solid color-mix(in srgb,var(--sc-warning) 28%,var(--sc-line));border-radius:8px;background:color-mix(in srgb,var(--sc-warning) 7%,var(--sc-bg))}
|
|
3189
|
+
.dsh-super-code-preset-notice[data-tone=error]{border-color:color-mix(in srgb,var(--sc-error) 28%,var(--sc-line));background:color-mix(in srgb,var(--sc-error) 6%,var(--sc-bg))}
|
|
3190
|
+
.dsh-super-code-preset-notice>.dsh-super-code-glyph{width:16px;height:16px;margin-top:2px;color:var(--sc-warning)}
|
|
3191
|
+
.dsh-super-code-preset-notice[data-tone=error]>.dsh-super-code-glyph{color:var(--sc-error)}
|
|
3192
|
+
.dsh-super-code-preset-notice-text{display:grid;gap:4px;flex:1;min-width:0}
|
|
3193
|
+
.dsh-super-code-preset-notice-text p{margin:0;font-size:12px;line-height:1.6;overflow-wrap:anywhere;text-wrap:pretty}
|
|
3194
|
+
.dsh-super-code-preset-notice-text .dsh-super-code-preset-notice-fix{color:var(--sc-muted)}
|
|
3195
|
+
.dsh-super-code-preset-notice>button{flex:none;align-self:center;min-height:28px;padding:3px 12px;font-size:12px}
|
|
3196
|
+
.dsh-super-code-home-list{border:1px solid var(--sc-line);border-radius:10px;background:var(--sc-bg)}
|
|
3197
|
+
.dsh-super-code-home-row{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:4px 14px;padding:14px 16px}
|
|
3198
|
+
.dsh-super-code-home-row+.dsh-super-code-home-row{border-top:1px solid var(--sc-line)}
|
|
3199
|
+
.dsh-super-code-home-row-icon{display:grid;place-items:center;align-self:start;width:32px;height:32px;border-radius:8px;background:var(--sc-surface);color:var(--sc-muted);transition:background .15s,color .15s}
|
|
3200
|
+
.dsh-super-code-home-row-icon .dsh-super-code-glyph{width:17px;height:17px;color:inherit}
|
|
3201
|
+
.dsh-super-code-home-row[data-active] .dsh-super-code-home-row-icon{background:color-mix(in srgb,var(--sc-accent) 10%,var(--sc-bg));color:var(--sc-accent)}
|
|
3202
|
+
.dsh-super-code-home-row-text{display:grid;gap:2px;min-width:0}
|
|
3203
|
+
.dsh-super-code-home-row-title{margin:0;font-size:13px;font-weight:600;line-height:1.5}
|
|
3204
|
+
.dsh-super-code-home-row-title>button{display:inline-flex;align-items:center;gap:2px;border:0;border-radius:4px;background:none;padding:0;font:inherit;color:var(--sc-text)}
|
|
3205
|
+
.dsh-super-code-home-row-title>button:hover{color:var(--sc-accent)}
|
|
3206
|
+
.dsh-super-code-home-row-title .dsh-super-code-glyph{width:14px;height:14px;color:var(--sc-faint);transform:rotate(-90deg);transition:transform .12s,color .12s}
|
|
3207
|
+
.dsh-super-code-home-row-title>button:hover .dsh-super-code-glyph{color:currentColor;transform:translateX(2px) rotate(-90deg)}
|
|
3208
|
+
.dsh-super-code-home-row :is(.dsh-super-code-home-row-hint,.dsh-super-code-home-row-status){margin:0;font-size:12px;line-height:1.55;color:var(--sc-muted)}
|
|
3209
|
+
/* Avoid a lone trailing character on wrapped CJK hints. */
|
|
3210
|
+
.dsh-super-code-home-row-hint{text-wrap:pretty}
|
|
3211
|
+
.dsh-super-code-home-row-status{display:flex;flex-wrap:wrap;align-items:center;gap:0 6px}
|
|
3212
|
+
.dsh-super-code-home-row-status>i{width:6px;height:6px;flex:none;border-radius:50%;background:var(--sc-idle)}
|
|
3213
|
+
.dsh-super-code-home-row-status[data-tone=success]>i{background:var(--sc-success)}
|
|
3214
|
+
.dsh-super-code-home-row-status[data-tone=error]>i{background:var(--sc-error)}
|
|
3215
|
+
.dsh-super-code-home-row-status>span+span:before{content:"·";margin-right:6px;color:var(--sc-faint)}
|
|
3216
|
+
.dsh-super-code-home-row-control{display:flex;justify-content:flex-end}
|
|
3217
|
+
.dsh-super-code-home-row-control>button{min-height:28px;padding:3px 12px;font-size:12px}
|
|
3218
|
+
.dsh-super-code-home-row>[role=alert]{grid-column:2/-1;margin:2px 0 0;font-size:12px}
|
|
3219
|
+
@container(max-width:560px){.dsh-super-code-home-row[data-home-row=protection]{grid-template-columns:auto minmax(0,1fr)}.dsh-super-code-home-row[data-home-row=protection]>.dsh-super-code-home-row-control{grid-column:2;justify-content:flex-start;margin-top:6px}}
|
|
3220
|
+
.dsh-super-code-segmented{display:inline-flex;gap:2px;padding:2px;border:1px solid var(--sc-line);border-radius:8px;background:var(--sc-surface)}
|
|
3221
|
+
.dsh-super-code-segmented>[role=radio]{min-height:26px;border:0;border-radius:6px;background:transparent;padding:3px 12px;font-size:12px;color:var(--sc-muted);white-space:nowrap}
|
|
3222
|
+
.dsh-super-code-segmented>[role=radio]:hover:not(:disabled){color:var(--sc-text)}
|
|
3223
|
+
.dsh-super-code-segmented>[role=radio][aria-checked=true]{background:var(--sc-bg);color:var(--sc-text);font-weight:500;box-shadow:0 1px 2px var(--sc-shadow)}
|
|
3224
|
+
.dsh-super-code-segmented[aria-busy=true]{cursor:progress}
|
|
3225
|
+
.dsh-super-code-segmented[aria-busy=true]>[role=radio]{pointer-events:none}
|
|
3226
|
+
.dsh-super-code-skeleton{display:block;border-radius:4px;background:linear-gradient(90deg,var(--sc-surface) 25%,var(--sc-hover) 50%,var(--sc-surface) 75%);background-size:200% 100%;animation:sc-skeleton 1.2s ease-in-out infinite}
|
|
3227
|
+
.dsh-super-code-skeleton[data-skeleton=text]{width:min(60%,280px);height:12px;margin-top:5px}
|
|
3228
|
+
.dsh-super-code-skeleton[data-skeleton=control]{width:96px;height:28px;border-radius:8px}
|
|
3229
|
+
@keyframes sc-skeleton{to{background-position:-200% 0}}
|
|
3230
|
+
.dsh-super-code-connection-list{list-style:none;padding:0;margin:0;max-height:280px;overflow:auto}
|
|
3231
|
+
.dsh-super-code-connection-list li{display:flex;align-items:center;gap:10px;padding:9px 0;border-bottom:1px solid var(--sc-line)}
|
|
3232
|
+
.dsh-super-code-connection-list li:last-child{border:0}
|
|
3233
|
+
.dsh-super-code-connection-list li>span{display:grid;gap:2px;flex:1;min-width:0;overflow-wrap:anywhere}
|
|
3234
|
+
.dsh-super-code-connection-list strong{font-size:12px;font-weight:500}
|
|
3235
|
+
.dsh-super-code-connection-list code{font:11px/1.5 var(--sc-mono);color:var(--sc-muted)}
|
|
3236
|
+
.dsh-super-code-connection-list button{flex:none}
|
|
3237
|
+
.dsh-super-code-directory-fieldset{border:0;padding:0;margin:0;min-width:0;display:grid;gap:10px}
|
|
3238
|
+
.dsh-super-code-directory-fieldset legend{font-size:13px;font-weight:500;margin-bottom:10px}
|
|
3239
|
+
.dsh-super-code-directory-list{border:1px solid var(--sc-line);border-radius:9px;max-height:330px;overflow:auto}
|
|
3240
|
+
.dsh-super-code-directory-list>p{margin:0;padding:18px;color:var(--sc-muted);font-size:12px}
|
|
3241
|
+
.dsh-super-code-directory-row{display:flex;align-items:center;gap:8px;padding:10px 12px}
|
|
3242
|
+
.dsh-super-code-directory-row+.dsh-super-code-directory-row{border-top:1px solid var(--sc-line)}
|
|
3243
|
+
.dsh-super-code-directory-row label{display:flex;align-items:center;flex:1;min-width:0;gap:10px;cursor:pointer}
|
|
3244
|
+
.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}
|
|
3245
|
+
.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)}
|
|
3246
|
+
.dsh-super-code-directory-row input:disabled{opacity:.5;cursor:default}
|
|
3247
|
+
.dsh-super-code-directory-row label>span{display:grid;min-width:0;gap:1px}
|
|
3248
|
+
.dsh-super-code-directory-row strong{font-size:12px;font-weight:500}
|
|
3249
|
+
.dsh-super-code-directory-row code{font:11px/1.6 var(--sc-mono);overflow-wrap:anywhere}
|
|
3250
|
+
.dsh-super-code-directory-row small[data-unavailable=true]{color:var(--sc-warning)}
|
|
3251
|
+
.dsh-super-code-directory-row>button{font-size:11px;border:0;background:none;padding:5px;color:var(--sc-muted)}
|
|
3252
|
+
.dsh-super-code-directory-crumbs{display:flex;flex-wrap:wrap;gap:4px;margin:12px 0}
|
|
3253
|
+
.dsh-super-code-directory-crumbs button{font-size:11px}
|
|
3254
|
+
.dsh-super-code-directory-path{display:block;overflow-wrap:anywhere;font-size:12px;margin-bottom:10px}
|
|
3255
|
+
.dsh-super-code-directory-browser{display:grid;max-height:320px;overflow:auto;gap:2px}
|
|
3256
|
+
.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}
|
|
3257
|
+
.dsh-super-code-directory-request{display:grid;gap:12px;padding:14px;border:1px solid var(--sc-line);border-radius:9px;background:var(--sc-surface)}
|
|
3258
|
+
.dsh-super-code-directory-request dt{font-size:11px;color:var(--sc-muted);margin-bottom:3px}
|
|
3259
|
+
.dsh-super-code-directory-request dd{font-size:13px;line-height:1.6;overflow-wrap:anywhere;white-space:pre-wrap;margin:0}
|
|
3260
|
+
.dsh-super-code-directory-request>div:nth-child(2) dd{font-family:var(--sc-mono);font-size:12px}
|
|
2429
3261
|
|
|
2430
3262
|
/* Confirmation. */
|
|
2431
3263
|
.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}
|
|
@@ -2496,10 +3328,16 @@ function apply(ctx) {
|
|
|
2496
3328
|
[data-feedback-live=false] .dsh-super-code-feedback-step .dsh-super-code-feedback-mark.active:before{animation:none}
|
|
2497
3329
|
@keyframes sc-feedback-spin{to{transform:rotate(360deg)}}
|
|
2498
3330
|
@media(prefers-reduced-motion:reduce){.dsh-super-code-feedback-mark.active:before{animation:none!important}}
|
|
2499
|
-
.dsh-super-code-feedback-
|
|
2500
|
-
.dsh-super-code-feedback-
|
|
2501
|
-
.dsh-super-code-feedback-
|
|
2502
|
-
.dsh-super-code-
|
|
3331
|
+
.dsh-super-code-feedback-tools{display:flex;flex-wrap:wrap;gap:4px 10px;list-style:none;margin:4px 0;padding:0 5px}
|
|
3332
|
+
.dsh-super-code-feedback-tools>li{display:inline-flex;align-items:center;gap:3px;min-height:22px;font-family:var(--sc-mono);font-size:11px;line-height:18px;color:var(--sc-muted)}
|
|
3333
|
+
.dsh-super-code-feedback-tools>li[data-running]>span{color:var(--sc-active)}
|
|
3334
|
+
.dsh-super-code-tool-badge{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 4px;border:1px solid currentColor;border-radius:9px;font-family:inherit;font-size:11px;font-weight:600;font-variant-numeric:tabular-nums;line-height:16px}
|
|
3335
|
+
.dsh-super-code-tool-badge[data-tone=success]{color:var(--sc-success);background:color-mix(in srgb,var(--sc-success) 10%,transparent)}
|
|
3336
|
+
.dsh-super-code-tool-badge[data-tone=warning]{color:var(--sc-warning);background:color-mix(in srgb,var(--sc-warning) 12%,transparent)}
|
|
3337
|
+
.dsh-super-code-tool-badge[data-zero]{color:var(--sc-faint);background:transparent;border-color:color-mix(in srgb,var(--sc-faint) 40%,transparent);font-weight:400}
|
|
3338
|
+
.dsh-super-code-state{display:flex;align-items:center;gap:6px;margin:6px 0;padding:6px 8px;border-radius:var(--sc-radius);font-size:12px;line-height:18px;color:var(--sc-muted);background:color-mix(in srgb,var(--sc-line) 40%,transparent)}
|
|
3339
|
+
.dsh-super-code-state>.dsh-super-code-glyph{flex:none;width:14px;height:14px}
|
|
3340
|
+
.dsh-super-code-state[data-tone=error]{color:var(--sc-error);background:color-mix(in srgb,var(--sc-error) 8%,transparent)}
|
|
2503
3341
|
.dsh-super-code-feedback-list>li[data-feedback-kind=open]{padding-bottom:8px}
|
|
2504
3342
|
.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}
|
|
2505
3343
|
.dsh-super-code-feedback-open>.dsh-super-code-glyph{width:12px;height:12px;transition:transform .15s}
|
|
@@ -2512,7 +3350,7 @@ function apply(ctx) {
|
|
|
2512
3350
|
.dsh-super-code-feedback-file>.dsh-super-code-glyph{flex:none;width:14px;height:14px;color:var(--sc-muted)}
|
|
2513
3351
|
.dsh-super-code-feedback-file strong{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;font-weight:400}
|
|
2514
3352
|
.dsh-super-code-feedback-file small{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
2515
|
-
.dsh-super-code-feedback-file-operation{flex:none;margin-left:auto;font-size:
|
|
3353
|
+
.dsh-super-code-feedback-file-operation{flex:none;margin-left:auto;font-size:11px;color:var(--sc-success)}
|
|
2516
3354
|
.dsh-super-code-feedback button:focus-visible,.dsh-super-code-feedback-scroll:focus-visible{outline:2px solid var(--sc-accent);outline-offset:-2px}
|
|
2517
3355
|
|
|
2518
3356
|
/* Memory library: geometry stays at 76px per row. */
|
|
@@ -2556,7 +3394,7 @@ function apply(ctx) {
|
|
|
2556
3394
|
.dsh-super-code-memory-row-topic{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--sc-muted);font-size:11px}
|
|
2557
3395
|
.dsh-super-code-memory-row-kind{color:var(--sc-muted);white-space:nowrap}
|
|
2558
3396
|
.dsh-super-code-memory-row strong{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:500;line-height:16px}
|
|
2559
|
-
.dsh-super-code-memory-row-meta{display:flex;align-items:center;gap:8px;font-size:
|
|
3397
|
+
.dsh-super-code-memory-row-meta{display:flex;align-items:center;gap:8px;font-size:11px;color:var(--sc-muted);white-space:nowrap;line-height:12px}
|
|
2560
3398
|
.dsh-super-code-memory-row-meta>span+span{border-left:1px solid var(--sc-line);padding-left:8px;font-family:var(--sc-mono)}
|
|
2561
3399
|
.dsh-super-code-memory-empty{padding:24px 8px;color:var(--sc-muted);font-size:13px;line-height:1.8}
|
|
2562
3400
|
.dsh-super-code-memory-body{flex:1;min-height:0;overflow:auto;padding:16px;background:var(--sc-surface)}
|
|
@@ -2594,7 +3432,10 @@ function apply(ctx) {
|
|
|
2594
3432
|
.dsh-super-code-memory-action[data-action=read]{color:var(--sc-accent)}
|
|
2595
3433
|
.dsh-super-code-memory-action[data-action=remember]{color:var(--sc-success)}
|
|
2596
3434
|
|
|
2597
|
-
|
|
3435
|
+
.dsh-super-code-memory-editor fieldset{display:grid;gap:8px;border:1px solid var(--sc-line);border-radius:6px;min-width:0}
|
|
3436
|
+
|
|
3437
|
+
|
|
3438
|
+
/* Legacy memory retains its original editor. */
|
|
2598
3439
|
.dsh-super-code-memory-editor{display:grid;align-content:start;gap:12px;flex:1;min-height:0;overflow:auto;padding:12px 16px}
|
|
2599
3440
|
.dsh-super-code-memory-editor-heading{display:flex;align-items:center;justify-content:space-between;gap:8px}
|
|
2600
3441
|
.dsh-super-code-memory-editor label{display:grid;grid-template-columns:1fr auto;gap:6px;font-size:12px}
|
|
@@ -2631,12 +3472,12 @@ function apply(ctx) {
|
|
|
2631
3472
|
.dsh-super-code-tree-toolbar{display:flex;align-items:center;gap:6px;min-width:0}
|
|
2632
3473
|
.dsh-super-code-tree-toolbar>.dsh-super-code-icon-button{width:28px;height:28px;border-radius:6px}
|
|
2633
3474
|
.dsh-super-code-tree-toolbar>.dsh-super-code-icon-button svg{width:16px;height:16px}
|
|
2634
|
-
.dsh-super-code-graph-legend{display:flex;flex-wrap:wrap;gap:6px 12px;font-size:
|
|
3475
|
+
.dsh-super-code-graph-legend{display:flex;flex-wrap:wrap;gap:6px 12px;font-size:11px;color:var(--sc-muted)}
|
|
2635
3476
|
.dsh-super-code-graph-legend>span{display:flex;align-items:center;gap:5px}
|
|
2636
|
-
.dsh-super-code-circle.active{--state-color
|
|
2637
|
-
.dsh-super-code-circle.idle{--state-color
|
|
2638
|
-
.dsh-super-code-circle.stopped{--state-color
|
|
2639
|
-
.dsh-super-code-circle.unavailable{--state-color
|
|
3477
|
+
.dsh-super-code-circle.active{--state-color:var(--sc-success)}
|
|
3478
|
+
.dsh-super-code-circle.idle{--state-color:var(--sc-accent)}
|
|
3479
|
+
.dsh-super-code-circle.stopped{--state-color:var(--sc-faint)}
|
|
3480
|
+
.dsh-super-code-circle.unavailable{--state-color:var(--sc-warning)}
|
|
2640
3481
|
.dsh-super-code-graph-legend .dsh-super-code-circle,.dsh-super-code-history-list .dsh-super-code-circle{display:block;width:7px;height:7px;flex:none;border-radius:50%;background:var(--state-color)}
|
|
2641
3482
|
.dsh-super-code-graph-legend .dsh-super-code-circle.idle{background:transparent;border:1px solid var(--state-color)}
|
|
2642
3483
|
.dsh-super-code-graph-controls{display:flex;align-items:center;gap:0;padding:0;border:1px solid var(--sc-line);border-radius:7px;color:var(--sc-muted);font-size:12px;flex:none}
|
|
@@ -2680,7 +3521,7 @@ function apply(ctx) {
|
|
|
2680
3521
|
.dsh-super-code-node-status[data-tone=active]{color:var(--sc-accent)}
|
|
2681
3522
|
.dsh-super-code-node-status[data-tone=unavailable]{color:var(--sc-warning)}
|
|
2682
3523
|
.dsh-super-code-node-status[data-tone=idle]>i{background:transparent;border:1px solid currentColor}
|
|
2683
|
-
.dsh-super-code-tree-usage{display:flex;flex-direction:column;gap:0;font-size:
|
|
3524
|
+
.dsh-super-code-tree-usage{display:flex;flex-direction:column;gap:0;font-size:11px;line-height:12px;color:var(--sc-muted);font-variant-numeric:tabular-nums}
|
|
2684
3525
|
.dsh-super-code-tree-usage>span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
2685
3526
|
.dsh-super-code-branch-toggle{position:absolute;display:grid;place-items:center;width:22px;height:22px;border:1px solid var(--sc-line);border-radius:50%;padding:0;background:var(--sc-bg);color:var(--sc-muted)}
|
|
2686
3527
|
.dsh-super-code-branch-toggle:hover{color:var(--sc-accent);border-color:currentColor}
|
|
@@ -2742,7 +3583,7 @@ function apply(ctx) {
|
|
|
2742
3583
|
.dsh-super-code-detail-avatar .dsh-super-code-glyph{width:16px;height:16px;color:inherit}
|
|
2743
3584
|
.dsh-super-code-detail-message{flex:1;min-width:0;padding:10px 12px;border:1px solid var(--sc-line);border-radius:4px 12px 12px 12px;background:var(--sc-bg)}
|
|
2744
3585
|
.dsh-super-code-detail-entry.user{flex-direction:row-reverse;margin-left:28px}
|
|
2745
|
-
.dsh-super-code-detail-entry.user .dsh-super-code-detail-avatar{background:var(--sc-accent);color
|
|
3586
|
+
.dsh-super-code-detail-entry.user .dsh-super-code-detail-avatar{background:var(--sc-accent);color:var(--sc-on-accent)}
|
|
2746
3587
|
.dsh-super-code-detail-entry.user .dsh-super-code-detail-message{border-color:color-mix(in srgb,var(--sc-accent) 22%,var(--sc-line));border-radius:12px 4px 12px 12px;background:var(--sc-selected)}
|
|
2747
3588
|
.dsh-super-code-detail-entry.agent .dsh-super-code-detail-avatar{background:color-mix(in srgb,var(--sc-success) 10%,var(--sc-bg));color:var(--sc-success)}
|
|
2748
3589
|
.dsh-super-code-detail-entry.tool{margin-bottom:12px}
|
|
@@ -2854,5 +3695,5 @@ function apply(ctx) {
|
|
|
2854
3695
|
ctx.effect(() => disposeMemoryType, 'super-code: memory library tab')
|
|
2855
3696
|
}
|
|
2856
3697
|
|
|
2857
|
-
return { inject, apply, taskFeedbackRows, feedbackStatus, feedbackListItems, feedbackListLayout, feedbackListIndex, feedbackListWindow, feedbackListAnchor, budgetDraft, budgetConfiguration, synchronizePresetDisplay, agentSessionState, buildAgentView, visibleAgentRows, agentTreeDisplay, agentTreeTotals, agentTreeLayout, agentExecutionStatus, agentDetailAddress, agentDetailTarget, agentPath, detailRecord, createAgentInspector, agentHistoryRange, agentHistoryRows, agentHistoryWindow, memoryLibraryAddress, memoryLibraryTarget, memoryLibraryNavigation, memoryLibraryWindow, createMemoryEditor }
|
|
3698
|
+
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, MemoryLibrary }
|
|
2858
3699
|
} })
|