dsh-super-code 0.3.7 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -10
- package/client/index.js +576 -24
- package/cordis.patch.yml +1 -1
- package/docs/Changelog.md +9 -0
- package/docs/Compatibility.md +30 -0
- package/docs/UsageGuide.md +60 -0
- package/lib/connection/directories.d.ts +30 -0
- package/lib/connection/directories.d.ts.map +1 -0
- package/lib/connection/directories.js +82 -0
- package/lib/connection/directories.js.map +1 -0
- package/lib/connection/git.d.ts +5 -0
- package/lib/connection/git.d.ts.map +1 -0
- package/lib/connection/git.js +86 -0
- package/lib/connection/git.js.map +1 -0
- package/lib/connection/http.d.ts +38 -0
- package/lib/connection/http.d.ts.map +1 -0
- package/lib/connection/http.js +275 -0
- package/lib/connection/http.js.map +1 -0
- package/lib/connection/service.d.ts +57 -0
- package/lib/connection/service.d.ts.map +1 -0
- package/lib/connection/service.js +168 -0
- package/lib/connection/service.js.map +1 -0
- package/lib/connection/store.d.ts +21 -0
- package/lib/connection/store.d.ts.map +1 -0
- package/lib/connection/store.js +139 -0
- package/lib/connection/store.js.map +1 -0
- package/lib/connection/tasks.d.ts +84 -0
- package/lib/connection/tasks.d.ts.map +1 -0
- package/lib/connection/tasks.js +270 -0
- package/lib/connection/tasks.js.map +1 -0
- package/lib/connection/types.d.ts +90 -0
- package/lib/connection/types.d.ts.map +1 -0
- package/lib/connection/types.js +21 -0
- package/lib/connection/types.js.map +1 -0
- package/lib/dsh/connection-host.d.ts +81 -0
- package/lib/dsh/connection-host.d.ts.map +1 -0
- package/lib/dsh/connection-host.js +130 -0
- package/lib/dsh/connection-host.js.map +1 -0
- package/lib/dsh/connection.d.ts +72 -0
- package/lib/dsh/connection.d.ts.map +1 -0
- package/lib/dsh/connection.js +183 -0
- package/lib/dsh/connection.js.map +1 -0
- package/lib/dsh/index.d.ts.map +1 -1
- package/lib/dsh/index.js +2 -0
- package/lib/dsh/index.js.map +1 -1
- package/package.json +7 -6
- package/presets/super-code/agent.cordis.yml +1 -1
package/client/index.js
CHANGED
|
@@ -292,7 +292,47 @@ const zh = {
|
|
|
292
292
|
'feedback.agentsPartial': '任务条目超过显示上限,可在动作执行中查看其他 Agent。',
|
|
293
293
|
'status.blocked': '受阻',
|
|
294
294
|
|
|
295
|
-
'settings.
|
|
295
|
+
'settings.homeTab': '首页', 'settings.presetTab': '预设配置', 'settings.protectionTab': '删除保护',
|
|
296
|
+
"connection.requestTitle": "请求访问工作目录",
|
|
297
|
+
"connection.requestHint": "外部 MCP 客户端申请访问以下目录及其子目录。同意后会加入允许列表,后续委派仍使用 DSH 的权限设置。",
|
|
298
|
+
"connection.requestClient": "请求客户端",
|
|
299
|
+
"connection.requestPath": "目录",
|
|
300
|
+
"connection.requestReason": "申请理由",
|
|
301
|
+
"connection.requestError": "操作失败,目录可能已变化或设置无法保存。请重试或拒绝。",
|
|
302
|
+
"connection.approveDirectory": "允许访问",
|
|
303
|
+
"connection.rejectDirectory": "拒绝",
|
|
304
|
+
"connection.error.directory-limit": "最多允许选择 32 个目录。",
|
|
305
|
+
"connection.error.picker-unavailable": "无法打开目录选择器,请重新读取后重试。",
|
|
306
|
+
'home.protection': '保护', 'home.extensions': '扩展', 'home.configure': '配置', 'home.managePreset': '配置预设',
|
|
307
|
+
'home.connectedSessions': '正在连接的对话', 'home.historySessions': '历史对话',
|
|
308
|
+
'home.connections': '已连接客户端', 'home.noConnections': '暂无客户端连接', 'home.sessions': '关联会话', 'home.noSessions': '暂无关联会话',
|
|
309
|
+
'home.disconnect': '断开', 'home.openSession': '打开会话', 'home.connected': '已连接', 'home.enabled': '已开启', 'home.disabled': '已关闭',
|
|
310
|
+
'home.mcp': 'MCP 服务', 'home.connectionCount': '{count} / {max} 个连接', 'home.setupConnection': '配置并启用',
|
|
311
|
+
'home.lastSeen': '最近活动', 'home.refresh': '刷新概览', 'home.error': '状态读取或操作失败,请刷新后重试。',
|
|
312
|
+
'connection.maxConnections': '最大连接数', 'connection.connectionsHint': '按 MCP 客户端会话计数。断开或空闲 10 分钟后释放连接;已接收任务继续执行。',
|
|
313
|
+
'connection.addDirectory': '添加目录', 'connection.noDirectories': '暂无目录,请通过文件管理器添加。',
|
|
314
|
+
'connection.workspaceDirectory': 'DSH 工作区', 'connection.customDirectory': '添加的目录', 'connection.directoryUnavailable': '目录不可用',
|
|
315
|
+
'connection.removeDirectory': '移除目录', 'connection.directoryRefresh': '刷新目录',
|
|
316
|
+
'connection.directoryBrowser': '选择目录', 'connection.selectDirectory': '选择此目录', 'connection.parentDirectory': '上一级',
|
|
317
|
+
'connection.emptyDirectory': '没有子目录', 'connection.browserError': '无法读取此目录,请重试或返回上一级。',
|
|
318
|
+
'connection.directoryLimit': '最多允许选择 32 个目录。',
|
|
319
|
+
'taskStatus.starting': '正在启动', 'taskStatus.running': '执行中', 'taskStatus.waiting_confirmation': '等待确认',
|
|
320
|
+
'taskStatus.cancelling': '正在取消', 'taskStatus.finished': '已结束', 'taskStatus.failed': '失败',
|
|
321
|
+
'taskStatus.cancelled': '已取消', 'taskStatus.interrupted': '已中断', 'taskStatus.unavailable': '状态不可用',
|
|
322
|
+
'settings.connectionTab': '连接', 'connection.title': '连接', 'connection.enabled': '启用 MCP 连接',
|
|
323
|
+
'connection.state.disabled': '连接已关闭', 'connection.state.listening': '正在监听', 'connection.state.error': '连接不可用', 'connection.state.loading': '正在读取…',
|
|
324
|
+
'connection.scope': '允许外部客户端委派任务到当前 DSH。仅监听本机,使用当前模型、Super Code 预设和宿主权限。关闭连接不会停止已接收的任务。',
|
|
325
|
+
'connection.autosave': '修改后自动保存', 'connection.saving': '正在保存…',
|
|
326
|
+
'connection.port': '本机端口', 'connection.directories': '允许的工作目录',
|
|
327
|
+
'connection.directoriesHint': '勾选允许的目录,包含其子目录。每次打开设置会同步 DSH 工作区,保留已保存的选择。',
|
|
328
|
+
'connection.save': '保存连接', 'connection.cancel': '取消修改', 'connection.reload': '重新读取', 'connection.saved': '已保存连接设置。',
|
|
329
|
+
'connection.client': '连接 Prompt', 'connection.copyCommand': '复制连接 Prompt', 'connection.showToken': '显示凭证', 'connection.hideToken': '隐藏凭证', 'connection.copyToken': '复制凭证', 'connection.token': '连接凭证', 'connection.rotate': '更换凭证',
|
|
330
|
+
'connection.prompt': '请配置并连接 DSH Super Code MCP 服务:\n地址:{url}\n传输:Streamable HTTP\n认证:Authorization: Bearer <连接凭证>,凭证由我单独提供,请妥善保存在客户端。\n连接后先使用 dsh_sessions 按工作目录查看最近对话、状态和连接额度,由调用方选择传 taskId 复用历史,或传 newConversation: true 新建;不要默认复用最新对话。然后使用 dsh_run 委派任务、dsh_wait 查询状态或找回任务、dsh_send 补充要求、dsh_cancel 取消任务。使用稳定的 requestId 避免重复提交,只使用允许的绝对工作目录;新增目录请用 dsh_request_directory 提交申请,并等待用户在 DSH 中同意。权限确认需在 DSH 中处理;finished 仅表示执行结束,仍需验证结果。',
|
|
331
|
+
'connection.tokenHint': '请将凭证安全保存在客户端。更换凭证后,旧凭证立即失效。',
|
|
332
|
+
'connection.rotated': '已更换凭证,请更新客户端。', 'connection.copied': '已复制。',
|
|
333
|
+
'connection.error.unavailable': '连接服务尚未就绪或当前配置目录不可写,请重新读取。', 'connection.error.invalid': '请检查端口、最大连接数和目录。启用连接至少需要一个有效目录。',
|
|
334
|
+
'connection.error.conflict': '连接设置已在别处修改。草稿已保留,请重新读取。', 'connection.error.save-failed': '保存失败,草稿已保留。', 'connection.error.port-unavailable': '端口被占用或无法监听。请更换端口并保存。', 'connection.error.storage-unavailable': '任务状态保存失败,请检查配置目录。', 'connection.error.clipboard': '复制失败,请手动复制。',
|
|
335
|
+
'settings.contextTab': '上下文配置', 'settings.tabs': 'Super Code 配置',
|
|
296
336
|
'budget.common': '通用预算', 'budget.superCode': 'Super Code 专属预算',
|
|
297
337
|
'budget.applyToOfficialPresets': '同时应用到官方预设', 'budget.officialHint': '开启后,通用预算也用于标准、PTC、极简和创造模式;不包含下方的 Super Code 专属预算。',
|
|
298
338
|
'budget.superCodeHint': '仅用于 Super Code 的任务记录和任务记忆,始终不影响官方预设。',
|
|
@@ -426,7 +466,47 @@ const en = {
|
|
|
426
466
|
'feedback.agentsPartial': 'The task list reached its limit; view other Agents in the execution tree.',
|
|
427
467
|
'status.blocked': 'Blocked',
|
|
428
468
|
|
|
429
|
-
'settings.
|
|
469
|
+
'settings.homeTab': 'Home', 'settings.presetTab': 'Preset configuration', 'settings.protectionTab': 'Deletion protection',
|
|
470
|
+
"connection.requestTitle": "Working directory access request",
|
|
471
|
+
"connection.requestHint": "An external MCP client requests access to this directory and its descendants. Approval adds it to the allowlist. DSH permissions still apply to delegated tasks.",
|
|
472
|
+
"connection.requestClient": "Requesting client",
|
|
473
|
+
"connection.requestPath": "Directory",
|
|
474
|
+
"connection.requestReason": "Reason",
|
|
475
|
+
"connection.requestError": "Could not save this decision. The directory may have changed or settings may be unwritable. Retry or reject.",
|
|
476
|
+
"connection.approveDirectory": "Allow access",
|
|
477
|
+
"connection.rejectDirectory": "Reject",
|
|
478
|
+
"connection.error.directory-limit": "Select up to 32 directories.",
|
|
479
|
+
"connection.error.picker-unavailable": "Could not open the directory picker. Reload and try again.",
|
|
480
|
+
'home.protection': 'Protection', 'home.extensions': 'Extensions', 'home.configure': 'Configure', 'home.managePreset': 'Configure preset',
|
|
481
|
+
'home.connectedSessions': 'Connected conversations', 'home.historySessions': 'Past conversations',
|
|
482
|
+
'home.connections': 'Connected clients', 'home.noConnections': 'No clients connected', 'home.sessions': 'Linked sessions', 'home.noSessions': 'No linked sessions',
|
|
483
|
+
'home.disconnect': 'Disconnect', 'home.openSession': 'Open session', 'home.connected': 'Connected', 'home.enabled': 'On', 'home.disabled': 'Off',
|
|
484
|
+
'home.mcp': 'MCP service', 'home.connectionCount': '{count} / {max} connections', 'home.setupConnection': 'Set up connection',
|
|
485
|
+
'home.lastSeen': 'Last activity', 'home.refresh': 'Refresh overview', 'home.error': 'Could not read or update the status. Refresh and try again.',
|
|
486
|
+
'connection.maxConnections': 'Maximum connections', 'connection.connectionsHint': 'Counts MCP client sessions. Disconnect or 10 minutes of inactivity releases a connection; accepted tasks continue.',
|
|
487
|
+
'connection.addDirectory': 'Add directory', 'connection.noDirectories': 'No directories. Add one with the directory picker.',
|
|
488
|
+
'connection.workspaceDirectory': 'DSH workspace', 'connection.customDirectory': 'Added directory', 'connection.directoryUnavailable': 'Directory unavailable',
|
|
489
|
+
'connection.removeDirectory': 'Remove directory', 'connection.directoryRefresh': 'Refresh directories',
|
|
490
|
+
'connection.directoryBrowser': 'Choose directory', 'connection.selectDirectory': 'Select this directory', 'connection.parentDirectory': 'Parent directory',
|
|
491
|
+
'connection.emptyDirectory': 'No subdirectories', 'connection.browserError': 'Could not read this directory. Retry or return to its parent.',
|
|
492
|
+
'connection.directoryLimit': 'Select up to 32 directories.',
|
|
493
|
+
'taskStatus.starting': 'Starting', 'taskStatus.running': 'Running', 'taskStatus.waiting_confirmation': 'Waiting for confirmation',
|
|
494
|
+
'taskStatus.cancelling': 'Cancelling', 'taskStatus.finished': 'Finished', 'taskStatus.failed': 'Failed',
|
|
495
|
+
'taskStatus.cancelled': 'Cancelled', 'taskStatus.interrupted': 'Interrupted', 'taskStatus.unavailable': 'Unavailable',
|
|
496
|
+
'settings.connectionTab': 'Connection', 'connection.title': 'Connection', 'connection.enabled': 'Enable MCP connection',
|
|
497
|
+
'connection.state.disabled': 'Connection off', 'connection.state.listening': 'Listening', 'connection.state.error': 'Connection unavailable', 'connection.state.loading': 'Loading…',
|
|
498
|
+
'connection.scope': 'Let external clients delegate tasks to this DSH host using its model, Super Code preset and permissions. Listens on localhost only. Disabling the connection does not stop accepted tasks.',
|
|
499
|
+
'connection.autosave': 'Changes save automatically', 'connection.saving': 'Saving…',
|
|
500
|
+
'connection.port': 'Local port', 'connection.directories': 'Allowed working directories',
|
|
501
|
+
'connection.directoriesHint': 'Select allowed directories, including their subdirectories. Reopening settings refreshes DSH workspaces and retains saved selections.',
|
|
502
|
+
'connection.save': 'Save connection', 'connection.cancel': 'Cancel changes', 'connection.reload': 'Reload', 'connection.saved': 'Connection settings saved.',
|
|
503
|
+
'connection.client': 'Connection Prompt', 'connection.copyCommand': 'Copy connection prompt', 'connection.showToken': 'Show token', 'connection.hideToken': 'Hide token', 'connection.copyToken': 'Copy token', 'connection.token': 'Connection token', 'connection.rotate': 'Rotate token',
|
|
504
|
+
'connection.prompt': 'Configure and connect to the DSH Super Code MCP service:\nURL: {url}\nTransport: Streamable HTTP\nAuthentication: Authorization: Bearer <connection token>. I will supply the token separately; store it securely in the client.\nAfter connecting, call dsh_sessions for the working directory to show recent conversations, statuses and connection capacity. Let the caller choose whether to pass taskId to reuse history or newConversation: true to create a new root; never default to the latest conversation. Then use dsh_run to delegate, dsh_wait to observe or recover, dsh_send for follow-ups, and dsh_cancel to cancel. Use stable requestId values to avoid duplicate submissions and only allowed absolute working directories. Request additional access with dsh_request_directory and wait for user approval in DSH. Resolve permissions in DSH. A finished task still requires result verification.',
|
|
505
|
+
'connection.tokenHint': 'Store the token securely in the client. Rotation invalidates the previous token immediately.',
|
|
506
|
+
'connection.rotated': 'Token rotated. Update your client.', 'connection.copied': 'Copied.',
|
|
507
|
+
'connection.error.unavailable': 'The service is not ready or the profile directory is not writable. Reload to retry.', 'connection.error.invalid': 'Check the port, maximum connections and directories. Enabling requires at least one valid directory.',
|
|
508
|
+
'connection.error.conflict': 'Connection settings changed elsewhere. Your draft is retained; reload to continue.', 'connection.error.save-failed': 'Save failed. Your draft is retained.', 'connection.error.port-unavailable': 'The port is occupied or cannot be opened. Save with another port.', 'connection.error.storage-unavailable': 'Task state could not be saved. Check the profile directory.', 'connection.error.clipboard': 'Copy failed. Copy the text manually.',
|
|
509
|
+
'settings.contextTab': 'Context settings', 'settings.tabs': 'Super Code settings',
|
|
430
510
|
'budget.common': 'Common budgets', 'budget.superCode': 'Super Code budgets',
|
|
431
511
|
'budget.applyToOfficialPresets': 'Also apply to official presets', 'budget.officialHint': 'Apply common budgets to Standard, PTC, Minimal and Create. Super Code task and memory limits below remain exclusive to Super Code.',
|
|
432
512
|
'budget.superCodeHint': 'Task records and task memory for Super Code. These limits never affect official presets.',
|
|
@@ -1956,6 +2036,14 @@ const budgetRemoteContribution = {
|
|
|
1956
2036
|
})),
|
|
1957
2037
|
}
|
|
1958
2038
|
|
|
2039
|
+
const connectionRemoteContribution = {
|
|
2040
|
+
package: 'dsh-super-code', descriptors: ['readConfig', 'saveConfig', 'revealToken', 'rotateToken', 'overview', 'disconnectClient', 'directories', 'pickDirectory', 'browseDirectory', 'pendingDirectories', 'resolveDirectory'].map(method => ({
|
|
2041
|
+
id: `dsh-super-code:superCodeConnection/${method}`, service: 'superCodeConnection', namespace: 'superCodeConnection', method, invocation: { kind: 'direct' },
|
|
2042
|
+
parameters: (method === 'saveConfig' ? ['content', 'expected'] : method === 'rotateToken' ? ['expected'] : method === 'resolveDirectory' ? ['id', 'decision'] : method === 'disconnectClient' ? ['id'] : method === 'browseDirectory' ? ['path'] : [])
|
|
2043
|
+
.map(name => ({ name, wire: name, source: 'json', codec: remoteStringCodec })), result: { mode: 'src-json' },
|
|
2044
|
+
})),
|
|
2045
|
+
}
|
|
2046
|
+
|
|
1959
2047
|
const budgetFields = [
|
|
1960
2048
|
{ key: 'thresholdRatio', group: 'context', factor: 0.01, min: 1, max: 100, step: 'any' },
|
|
1961
2049
|
{ key: 'retainRatio', group: 'context', factor: 0.01, min: 0, max: 100, step: 'any' },
|
|
@@ -2009,7 +2097,7 @@ function budgetConfiguration(draft) {
|
|
|
2009
2097
|
return { ok: true, configuration: config }
|
|
2010
2098
|
}
|
|
2011
2099
|
|
|
2012
|
-
function BudgetSettings({ api, t, active }) {
|
|
2100
|
+
function BudgetSettings({ api, t, active, section = 'context', onSaved = () => {} }) {
|
|
2013
2101
|
const [snapshot, setSnapshot] = useState(null)
|
|
2014
2102
|
const [draft, setDraft] = useState(() => budgetDraft({})), [busy, setBusy] = useState(false)
|
|
2015
2103
|
const [error, setError] = useState(''), [notice, setNotice] = useState('')
|
|
@@ -2026,24 +2114,33 @@ function BudgetSettings({ api, t, active }) {
|
|
|
2026
2114
|
finally { pending.current = false; if (alive.current) setBusy(false) }
|
|
2027
2115
|
}
|
|
2028
2116
|
useEffect(() => { alive.current = true; return () => { alive.current = false } }, [api])
|
|
2029
|
-
useEffect(() => { if (active && !snapshot && !error) read() }, [api, active])
|
|
2117
|
+
useEffect(() => { if (active && (!snapshot || !dirty) && !error) read() }, [api, active])
|
|
2030
2118
|
const save = async reset => {
|
|
2031
2119
|
if (pending.current || !snapshot?.writable) return
|
|
2032
|
-
const
|
|
2120
|
+
const protection = section === 'protection'
|
|
2121
|
+
const parsed = budgetConfiguration(draft)
|
|
2122
|
+
if (parsed.ok) {
|
|
2123
|
+
const selected = protection
|
|
2124
|
+
? { ...snapshot.configuration, deletionProtection: reset ? 'relaxed' : draft.deletionProtection, deletionProtectionForOfficialPresets: reset ? false : draft.deletionProtectionForOfficialPresets }
|
|
2125
|
+
: { ...(reset ? {} : parsed.configuration), deletionProtection: snapshot.configuration.deletionProtection, deletionProtectionForOfficialPresets: snapshot.configuration.deletionProtectionForOfficialPresets }
|
|
2126
|
+
parsed.configuration = selected
|
|
2127
|
+
}
|
|
2033
2128
|
if (!parsed.ok) { setError(parsed.error); setNotice(''); return }
|
|
2034
2129
|
pending.current = true; setBusy(true); setError(''); setNotice('')
|
|
2035
2130
|
try {
|
|
2036
|
-
const response =
|
|
2037
|
-
: await api.saveConfig(JSON.stringify(parsed.configuration), String(snapshot.revision))
|
|
2131
|
+
const response = await api.saveConfig(JSON.stringify(parsed.configuration), String(snapshot.revision))
|
|
2038
2132
|
if (!response.ok) throw new Error('Remote unavailable')
|
|
2039
2133
|
if (!alive.current) return
|
|
2040
|
-
if (response.value.ok) { receive(response.value.snapshot); setNotice(reset ? 'resetDone' : 'saved') }
|
|
2134
|
+
if (response.value.ok) { receive(response.value.snapshot); onSaved(); setNotice(reset ? 'resetDone' : 'saved') }
|
|
2041
2135
|
else setError(response.value.error)
|
|
2042
2136
|
} catch { if (alive.current) setError('save-failed') }
|
|
2043
2137
|
finally { pending.current = false; if (alive.current) setBusy(false) }
|
|
2044
2138
|
}
|
|
2045
2139
|
const disabled = busy || !snapshot?.writable
|
|
2046
|
-
const
|
|
2140
|
+
const sectionDraft = value => section === 'protection'
|
|
2141
|
+
? [value.deletionProtection, value.deletionProtectionForOfficialPresets]
|
|
2142
|
+
: Object.fromEntries(Object.entries(value).filter(([key]) => !key.startsWith('deletionProtection')))
|
|
2143
|
+
const dirty = snapshot && JSON.stringify(sectionDraft(draft)) !== JSON.stringify(sectionDraft(budgetDraft(snapshot.configuration)))
|
|
2047
2144
|
const edit = (key, value) => { setDraft(current => ({ ...current, [key]: value })); setError(''); setNotice('') }
|
|
2048
2145
|
const defaultLabel = (key, field) => {
|
|
2049
2146
|
const value = field?.root ? snapshot?.defaults?.[key] : snapshot?.defaults?.budgets?.[key]
|
|
@@ -2060,8 +2157,8 @@ function BudgetSettings({ api, t, active }) {
|
|
|
2060
2157
|
onChange: event => edit(field.key, event.target.value) }))
|
|
2061
2158
|
})
|
|
2062
2159
|
return React.createElement('section', { className: 'dsh-super-code-budgets' },
|
|
2063
|
-
React.createElement('form', { 'aria-label': t('budget.title'), 'aria-busy': busy, onSubmit: event => { event.preventDefault(); save(false) } },
|
|
2064
|
-
React.createElement('section', { className: 'dsh-super-code-budget-group', 'aria-labelledby': 'super-code-deletion-title' },
|
|
2160
|
+
React.createElement('form', { 'aria-label': t(section === 'protection' ? 'delete.title' : 'budget.title'), 'aria-busy': busy, onSubmit: event => { event.preventDefault(); save(false) } },
|
|
2161
|
+
section === 'protection' && React.createElement('section', { className: 'dsh-super-code-budget-group', 'aria-labelledby': 'super-code-deletion-title' },
|
|
2065
2162
|
React.createElement('h3', { id: 'super-code-deletion-title' }, t('delete.title')),
|
|
2066
2163
|
React.createElement('div', { className: 'dsh-super-code-settings-field' },
|
|
2067
2164
|
React.createElement('label', { id: 'super-code-deletion-mode-label' }, t('delete.mode')),
|
|
@@ -2074,6 +2171,7 @@ function BudgetSettings({ api, t, active }) {
|
|
|
2074
2171
|
'aria-describedby': 'super-code-deletion-official-hint', onChange: event => edit('deletionProtectionForOfficialPresets', event.target.checked) }),
|
|
2075
2172
|
React.createElement('span', null, t('delete.official'))),
|
|
2076
2173
|
React.createElement('p', { id: 'super-code-deletion-official-hint', className: 'dsh-super-code-budget-hint' }, t('delete.officialHint'))),
|
|
2174
|
+
section !== 'protection' && React.createElement(React.Fragment, null,
|
|
2077
2175
|
React.createElement('p', { className: 'dsh-super-code-budget-hint' }, t('budget.scope')),
|
|
2078
2176
|
React.createElement('section', { className: 'dsh-super-code-budget-group', 'aria-labelledby': 'super-code-common-title' },
|
|
2079
2177
|
React.createElement('h3', { id: 'super-code-common-title' }, t('budget.common')),
|
|
@@ -2101,7 +2199,7 @@ function BudgetSettings({ api, t, active }) {
|
|
|
2101
2199
|
React.createElement('section', { className: 'dsh-super-code-budget-group dsh-super-code-budget-exclusive', 'aria-labelledby': 'super-code-exclusive-title' },
|
|
2102
2200
|
React.createElement('h3', { id: 'super-code-exclusive-title' }, t('budget.superCode')),
|
|
2103
2201
|
React.createElement('p', { className: 'dsh-super-code-budget-hint' }, t('budget.superCodeHint')),
|
|
2104
|
-
React.createElement('div', { className: 'dsh-super-code-budget-grid' }, fields('superCode'))),
|
|
2202
|
+
React.createElement('div', { className: 'dsh-super-code-budget-grid' }, fields('superCode')))),
|
|
2105
2203
|
!busy && snapshot && !snapshot.writable && React.createElement('small', null, t('budget.error.read-only')),
|
|
2106
2204
|
error && React.createElement('p', { role: 'alert' }, t(`budget.error.${error}`)),
|
|
2107
2205
|
notice && React.createElement('p', { role: 'status', className: 'dsh-super-code-budget-hint' }, t(`budget.${notice}`)),
|
|
@@ -2151,8 +2249,364 @@ function synchronizePresetDisplay(api, locale, reload, onError, preferenceScope)
|
|
|
2151
2249
|
return () => { disposed = true; unsubscribe(); unsubscribePreference() }
|
|
2152
2250
|
}
|
|
2153
2251
|
|
|
2154
|
-
function
|
|
2155
|
-
|
|
2252
|
+
function connectionDraft(config) {
|
|
2253
|
+
return { enabled: config.enabled, port: String(config.port), allowedDirectories: [...config.allowedDirectories], knownDirectories: [...config.knownDirectories ?? []],
|
|
2254
|
+
maxConcurrent: config.maxConnections ?? 3, maxConnections: String(config.maxConnections ?? 3), directoriesConfigured: config.directoriesConfigured ?? false }
|
|
2255
|
+
}
|
|
2256
|
+
function connectionConfiguration(draft) {
|
|
2257
|
+
const port = Number(draft.port), maxConnections = Number(draft.maxConnections)
|
|
2258
|
+
const allowedDirectories = [...new Set(draft.allowedDirectories)]
|
|
2259
|
+
if (typeof draft.enabled !== 'boolean' || !Number.isInteger(port) || port < 1024 || port > 65535 || !Number.isInteger(maxConnections) || maxConnections < 1 || maxConnections > 32 || allowedDirectories.length > 32 || allowedDirectories.some(path => typeof path !== 'string' || !path) || (draft.enabled && !allowedDirectories.length)) return { ok: false }
|
|
2260
|
+
return { ok: true, configuration: { enabled: draft.enabled, port, allowedDirectories, maxConcurrent: maxConnections, maxConnections, directoriesConfigured: true, knownDirectories: draft.knownDirectories } }
|
|
2261
|
+
}
|
|
2262
|
+
/** Serialize writes and retain newer edits while an earlier revision is being saved. */
|
|
2263
|
+
function createConnectionAutosave(api) {
|
|
2264
|
+
let state = { snapshot: false, draft: connectionDraft({ enabled: false, port: 23457, allowedDirectories: [] }), saving: false, error: '', notice: '', saveCount: 0 }
|
|
2265
|
+
let version = 0, acknowledged = 0, blocked = -1, timer = false, running = false
|
|
2266
|
+
const listeners = new Set()
|
|
2267
|
+
const update = patch => { state = { ...state, ...patch }; for (const listener of listeners) listener() }
|
|
2268
|
+
const flush = () => {
|
|
2269
|
+
if (timer) { clearTimeout(timer); timer = false }
|
|
2270
|
+
if (running) return running
|
|
2271
|
+
if (version === acknowledged || version === blocked || !state.snapshot) return Promise.resolve()
|
|
2272
|
+
running = (async () => {
|
|
2273
|
+
update({ saving: true, error: '', notice: '' })
|
|
2274
|
+
try {
|
|
2275
|
+
while (acknowledged < version) {
|
|
2276
|
+
const sent = version, parsed = connectionConfiguration(state.draft)
|
|
2277
|
+
if (!parsed.ok) { blocked = version; update({ error: 'invalid' }); break }
|
|
2278
|
+
const result = await api.saveConfig(JSON.stringify(parsed.configuration), String(state.snapshot.revision))
|
|
2279
|
+
if (!result.ok) throw new Error('Remote unavailable')
|
|
2280
|
+
if (!result.value.ok) { blocked = version; update({ error: result.value.error }); break }
|
|
2281
|
+
acknowledged = sent
|
|
2282
|
+
update({ snapshot: result.value.snapshot, saveCount: state.saveCount + 1,
|
|
2283
|
+
...(sent === version ? { draft: connectionDraft(result.value.snapshot.configuration), notice: 'saved' } : {}) })
|
|
2284
|
+
}
|
|
2285
|
+
} catch { blocked = version; update({ error: 'save-failed' }) }
|
|
2286
|
+
finally { update({ saving: false }) }
|
|
2287
|
+
})().finally(() => { running = false })
|
|
2288
|
+
return running
|
|
2289
|
+
}
|
|
2290
|
+
return {
|
|
2291
|
+
getSnapshot: () => state,
|
|
2292
|
+
subscribe: listener => { listeners.add(listener); return () => listeners.delete(listener) },
|
|
2293
|
+
hasChanges: () => version !== acknowledged || state.saving,
|
|
2294
|
+
receive: (snapshot, draft = connectionDraft(snapshot.configuration)) => {
|
|
2295
|
+
acknowledged = ++version
|
|
2296
|
+
update({ snapshot, draft, error: '', notice: '' })
|
|
2297
|
+
},
|
|
2298
|
+
revision: snapshot => update({ snapshot }),
|
|
2299
|
+
change: (patch, delay = 0) => {
|
|
2300
|
+
version++
|
|
2301
|
+
update({ draft: { ...state.draft, ...patch }, error: '', notice: '' })
|
|
2302
|
+
if (timer) { clearTimeout(timer); timer = false }
|
|
2303
|
+
if (delay) timer = setTimeout(() => { timer = false; void flush() }, delay)
|
|
2304
|
+
else void flush()
|
|
2305
|
+
},
|
|
2306
|
+
flush,
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
function ConnectionSwitch({ checked, disabled, onChange, children }) {
|
|
2310
|
+
return React.createElement('label', { className: 'dsh-super-code-connection-switch' },
|
|
2311
|
+
React.createElement('input', { type: 'checkbox', role: 'switch', checked, disabled, onChange: event => onChange(event.target.checked) }),
|
|
2312
|
+
React.createElement('span', { className: 'dsh-super-code-connection-track', 'aria-hidden': true }, React.createElement('i')), children)
|
|
2313
|
+
}
|
|
2314
|
+
function DirectoryBrowser({ api, t, onSelect, onClose }) {
|
|
2315
|
+
const [listing, setListing] = useState(null), [busy, setBusy] = useState(false), [error, setError] = useState(false)
|
|
2316
|
+
const dialog = useRef(null), generation = useRef(0), lastPath = useRef('')
|
|
2317
|
+
const browse = async path => {
|
|
2318
|
+
const version = ++generation.current; lastPath.current = path; setBusy(true); setError(false)
|
|
2319
|
+
try { const result = await api.browseDirectory(path); if (!result.ok) throw new Error(); if (version === generation.current) setListing(result.value) }
|
|
2320
|
+
catch { if (version === generation.current) setError(true) }
|
|
2321
|
+
finally { if (version === generation.current) setBusy(false) }
|
|
2322
|
+
}
|
|
2323
|
+
useEffect(() => {
|
|
2324
|
+
const element = dialog.current; element.showModal(); browse('')
|
|
2325
|
+
return () => { generation.current++; if (element.open) element.close() }
|
|
2326
|
+
}, [api])
|
|
2327
|
+
return React.createElement('dialog', { ref: dialog, className: 'dsh-super-code-confirm', 'aria-labelledby': 'super-code-directory-title',
|
|
2328
|
+
onCancel: event => { event.preventDefault(); event.stopPropagation(); onClose() }, onKeyDown: event => event.stopPropagation() },
|
|
2329
|
+
React.createElement('h3', { id: 'super-code-directory-title' }, t('connection.directoryBrowser')),
|
|
2330
|
+
React.createElement('nav', { className: 'dsh-super-code-directory-crumbs', 'aria-label': t('connection.directoryBrowser') },
|
|
2331
|
+
listing?.crumbs.map((row, index) => React.createElement(ActionButton, { key: `${row.path}-${index}`, type: 'button', disabled: busy, onClick: () => browse(row.path) }, row.name || row.path))),
|
|
2332
|
+
listing && React.createElement('code', { className: 'dsh-super-code-directory-path' }, listing.path),
|
|
2333
|
+
React.createElement('div', { className: 'dsh-super-code-directory-browser', 'aria-busy': busy },
|
|
2334
|
+
listing && listing.crumbs.length > 1 && React.createElement(ActionButton, { type: 'button', disabled: busy, onClick: () => browse(listing.crumbs[listing.crumbs.length - 2].path) }, '↑ ', t('connection.parentDirectory')),
|
|
2335
|
+
listing?.entries.map(row => React.createElement(ActionButton, { key: row.path, type: 'button', disabled: busy, onClick: () => browse(row.path) }, React.createElement(Glyph, { type: 'folder' }), row.name)),
|
|
2336
|
+
listing && !listing.entries.length && React.createElement('p', null, t('connection.emptyDirectory'))),
|
|
2337
|
+
error && React.createElement('p', { role: 'alert' }, t('connection.browserError')),
|
|
2338
|
+
React.createElement('div', { className: 'dsh-super-code-confirm-actions' },
|
|
2339
|
+
error && React.createElement(ActionButton, { type: 'button', disabled: busy, onClick: () => browse(lastPath.current) }, t('connection.reload')),
|
|
2340
|
+
React.createElement(ActionButton, { type: 'button', onClick: onClose }, t('preset.cancel')),
|
|
2341
|
+
React.createElement(ActionButton, { type: 'button', className: 'dsh-super-code-primary-button', disabled: busy || error || !listing,
|
|
2342
|
+
onClick: () => onSelect(listing.path) }, t('connection.selectDirectory'))))
|
|
2343
|
+
}
|
|
2344
|
+
function connectionSessions(overview) {
|
|
2345
|
+
const online = new Set(overview.connections.map(row => row.id)), connected = [], history = []
|
|
2346
|
+
for (const row of overview.sessions) (row.connectionId && online.has(row.connectionId) ? connected : history).push(row)
|
|
2347
|
+
return { connected, history }
|
|
2348
|
+
}
|
|
2349
|
+
function ConnectionActivity({ api, t, active, openSession }) {
|
|
2350
|
+
const [overview, setOverview] = useState(null), [busy, setBusy] = useState(false), [error, setError] = useState(false)
|
|
2351
|
+
const alive = useRef(false), pending = useRef(false)
|
|
2352
|
+
const load = async () => {
|
|
2353
|
+
if (pending.current) return
|
|
2354
|
+
pending.current = true
|
|
2355
|
+
try { const result = await api.overview(); if (!result.ok) throw new Error(); if (alive.current) { setOverview(result.value); setError(false) } }
|
|
2356
|
+
catch { if (alive.current) setError(true) }
|
|
2357
|
+
finally { pending.current = false }
|
|
2358
|
+
}
|
|
2359
|
+
useEffect(() => { alive.current = true; return () => { alive.current = false } }, [api])
|
|
2360
|
+
useEffect(() => { if (!active) return; load(); const timer = setInterval(load, 2000); return () => clearInterval(timer) }, [active, api])
|
|
2361
|
+
const disconnect = async id => {
|
|
2362
|
+
if (pending.current) return
|
|
2363
|
+
pending.current = true; setBusy(true)
|
|
2364
|
+
try { const result = await api.disconnectClient(id); if (!result.ok) throw new Error() }
|
|
2365
|
+
catch { if (alive.current) setError(true); return }
|
|
2366
|
+
finally { pending.current = false; if (alive.current) setBusy(false) }
|
|
2367
|
+
if (alive.current) load()
|
|
2368
|
+
}
|
|
2369
|
+
const groups = overview ? connectionSessions(overview) : { connected: [], history: [] }
|
|
2370
|
+
return React.createElement('section', { className: 'dsh-super-code-connection-activity', 'aria-label': t('home.sessions') },
|
|
2371
|
+
React.createElement('h3', null, t('home.connections')),
|
|
2372
|
+
!overview ? React.createElement('p', { role: 'status' }, t('connection.state.loading'))
|
|
2373
|
+
: overview.connections.length ? React.createElement('ul', { className: 'dsh-super-code-connection-list' }, overview.connections.map(row => React.createElement('li', { key: row.id },
|
|
2374
|
+
React.createElement('span', null, React.createElement('strong', null, row.name), React.createElement('small', null, row.version, ' · ', t('home.lastSeen'), ' ', new Date(row.lastSeen).toLocaleTimeString())),
|
|
2375
|
+
React.createElement(ActionButton, { type: 'button', disabled: busy, onClick: () => disconnect(row.id) }, t('home.disconnect'))))) : React.createElement('p', null, t('home.noConnections')),
|
|
2376
|
+
React.createElement('h3', null, t('home.sessions')),
|
|
2377
|
+
...['connected', 'history'].map(kind => React.createElement('div', { key: kind, 'data-session-group': kind },
|
|
2378
|
+
React.createElement('h4', null, t(kind === 'connected' ? 'home.connectedSessions' : 'home.historySessions'), ' · ', overview ? groups[kind].length : '—'),
|
|
2379
|
+
groups[kind].length ? React.createElement('ul', { className: 'dsh-super-code-connection-list' }, groups[kind].map(row => React.createElement('li', { key: row.taskId },
|
|
2380
|
+
React.createElement('span', null, React.createElement('strong', null, row.client || row.sessionId), React.createElement('small', null, t(`taskStatus.${row.status}`), ' · ', row.cwd), React.createElement('code', null, row.sessionId)),
|
|
2381
|
+
React.createElement(ActionButton, { type: 'button', onClick: () => openSession(row.sessionId) }, t('home.openSession'))))) : overview && React.createElement('p', null, t('home.noSessions')))),
|
|
2382
|
+
error && React.createElement('p', { role: 'alert' }, t('home.error')))
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2385
|
+
function ConnectionSettings({ api, t, active, openSession, onSaved = () => {} }) {
|
|
2386
|
+
const editor = useMemo(() => createConnectionAutosave(api), [api])
|
|
2387
|
+
const editing = useSyncExternalStore(editor.subscribe, editor.getSnapshot, editor.getSnapshot)
|
|
2388
|
+
const { snapshot, draft, saving } = editing
|
|
2389
|
+
const [catalog, setCatalog] = useState({ items: [], picker: 'unavailable' }), [custom, setCustom] = useState([]), [browser, setBrowser] = useState(false)
|
|
2390
|
+
const [busy, setBusy] = useState(false), [error, setError] = useState(''), [notice, setNotice] = useState(''), [token, setToken] = useState('')
|
|
2391
|
+
const alive = useRef(false), pending = useRef(false), visible = useRef(active)
|
|
2392
|
+
visible.current = active
|
|
2393
|
+
const dirty = editor.hasChanges()
|
|
2394
|
+
const receive = (value, directories = catalog) => {
|
|
2395
|
+
const next = connectionDraft(value.configuration)
|
|
2396
|
+
if (!next.directoriesConfigured && !next.allowedDirectories.length) next.allowedDirectories = directories.items.filter(row => row.workspace && row.available).slice(0, 32).map(row => row.path)
|
|
2397
|
+
editor.receive(value, next)
|
|
2398
|
+
}
|
|
2399
|
+
const load = async (discard = false) => {
|
|
2400
|
+
if (pending.current || (discard && editor.getSnapshot().saving)) return
|
|
2401
|
+
pending.current = true; setBusy(true); setError('')
|
|
2402
|
+
try {
|
|
2403
|
+
const [config, directories, credential] = await Promise.all([api.readConfig(), api.directories(), api.revealToken()])
|
|
2404
|
+
if (!config.ok || !directories.ok || !credential.ok) throw new Error()
|
|
2405
|
+
if (!alive.current) return
|
|
2406
|
+
setCatalog(directories.value)
|
|
2407
|
+
if (discard || !dirty) receive(config.value, directories.value)
|
|
2408
|
+
if (visible.current) setToken(credential.value.token)
|
|
2409
|
+
} catch { if (alive.current) setError('unavailable') }
|
|
2410
|
+
finally { pending.current = false; if (alive.current) setBusy(false) }
|
|
2411
|
+
}
|
|
2412
|
+
const action = async kind => {
|
|
2413
|
+
if (pending.current) return
|
|
2414
|
+
pending.current = true; setBusy(true); setError(''); setNotice('')
|
|
2415
|
+
try {
|
|
2416
|
+
const response = kind === 'rotate' ? await api.rotateToken(String(snapshot.revision)) : await api.revealToken()
|
|
2417
|
+
if (!response.ok) throw new Error()
|
|
2418
|
+
if (!alive.current) return
|
|
2419
|
+
if (kind === 'token') { if (visible.current) setToken(response.value.token) }
|
|
2420
|
+
else if (response.value.ok) {
|
|
2421
|
+
if (kind === 'rotate') {
|
|
2422
|
+
editor.revision(response.value.snapshot)
|
|
2423
|
+
const credential = await api.revealToken()
|
|
2424
|
+
if (credential.ok && alive.current && visible.current) setToken(credential.value.token)
|
|
2425
|
+
} else receive(response.value.snapshot)
|
|
2426
|
+
onSaved(); setNotice(kind === 'rotate' ? 'rotated' : 'saved')
|
|
2427
|
+
} else setError(response.value.error)
|
|
2428
|
+
} catch { if (alive.current) setError('unavailable') }
|
|
2429
|
+
finally { pending.current = false; if (alive.current) setBusy(false) }
|
|
2430
|
+
}
|
|
2431
|
+
useEffect(() => { alive.current = true; return () => { alive.current = false; void editor.flush() } }, [editor])
|
|
2432
|
+
useEffect(() => { if (editing.saveCount) onSaved() }, [editing.saveCount])
|
|
2433
|
+
useEffect(() => { if (active) load(); else { setToken(''); setBrowser(false) } }, [api, active])
|
|
2434
|
+
useEffect(() => {
|
|
2435
|
+
if (!active) return
|
|
2436
|
+
const changed = () => { if (dirty) setError('conflict'); else load(true) }
|
|
2437
|
+
window.addEventListener('super-code-directories-changed', changed)
|
|
2438
|
+
return () => window.removeEventListener('super-code-directories-changed', changed)
|
|
2439
|
+
}, [active, dirty, api])
|
|
2440
|
+
const disabled = busy || !snapshot?.writable
|
|
2441
|
+
const change = (patch, delay = 0) => {
|
|
2442
|
+
// Revoking the last directory also stops admission instead of retaining its old grant.
|
|
2443
|
+
editor.change(patch.allowedDirectories?.length === 0 ? { ...patch, enabled: false } : patch, delay)
|
|
2444
|
+
setNotice(''); setError('')
|
|
2445
|
+
}
|
|
2446
|
+
const edit = (key, value) => change({ [key]: value }, ['port', 'maxConnections'].includes(key) ? 350 : 0)
|
|
2447
|
+
const choose = path => {
|
|
2448
|
+
if (!draft.allowedDirectories.includes(path) && draft.allowedDirectories.length >= 32) { setError('directory-limit'); return }
|
|
2449
|
+
setCustom(rows => [...new Set([...rows, path])]); change({ allowedDirectories: [...new Set([...draft.allowedDirectories, path])], knownDirectories: [...new Set([...draft.knownDirectories, path])].slice(-128) }); setNotice(''); setError(''); setBrowser(false)
|
|
2450
|
+
}
|
|
2451
|
+
const addDirectory = async () => {
|
|
2452
|
+
if (pending.current) return
|
|
2453
|
+
if (catalog.picker !== 'native' && !globalThis.__DSH_DIRECTORY_PICKER__?.pick) { setBrowser(true); return }
|
|
2454
|
+
pending.current = true; setBusy(true)
|
|
2455
|
+
try {
|
|
2456
|
+
const result = globalThis.__DSH_DIRECTORY_PICKER__?.pick ? await globalThis.__DSH_DIRECTORY_PICKER__.pick()
|
|
2457
|
+
: await api.pickDirectory().then(result => { if (!result.ok) throw new Error(); return result.value.kind === 'selected' ? result.value.path : null })
|
|
2458
|
+
if (alive.current && result) choose(result)
|
|
2459
|
+
} catch { if (alive.current) setError('picker-unavailable') }
|
|
2460
|
+
finally { pending.current = false; if (alive.current) setBusy(false) }
|
|
2461
|
+
}
|
|
2462
|
+
const rows = [...catalog.items]
|
|
2463
|
+
for (const path of [...custom, ...draft.knownDirectories, ...draft.allowedDirectories]) if (!rows.some(row => row.path === path)) rows.push({ path, title: '', available: true, workspace: false })
|
|
2464
|
+
const copy = async value => {
|
|
2465
|
+
try { await navigator.clipboard.writeText(value); if (alive.current) { setNotice('copied'); setError('') } }
|
|
2466
|
+
catch { if (alive.current) setError('clipboard') }
|
|
2467
|
+
}
|
|
2468
|
+
const command = snapshot?.url ? t('connection.prompt', { url: snapshot.url }) : ''
|
|
2469
|
+
return React.createElement('section', { className: 'dsh-super-code-connection' },
|
|
2470
|
+
React.createElement('div', { className: 'dsh-super-code-settings-status-row' },
|
|
2471
|
+
React.createElement('p', { className: 'dsh-super-code-preset-status', role: 'status', 'data-available': snapshot?.state === 'listening' },
|
|
2472
|
+
React.createElement('i', { 'aria-hidden': true }), t(`connection.state.${snapshot?.state || 'loading'}`)),
|
|
2473
|
+
React.createElement('div', { className: 'dsh-super-code-settings-actions' }, React.createElement(ActionButton, { type: 'button', disabled: busy, onClick: () => load(true) }, t('connection.reload')))),
|
|
2474
|
+
snapshot?.error && React.createElement('p', { role: 'alert' }, t(`connection.error.${snapshot.error}`)),
|
|
2475
|
+
React.createElement(ConnectionActivity, { api, t, active, openSession }),
|
|
2476
|
+
React.createElement('form', { 'aria-label': t('connection.title'), 'aria-busy': saving, onSubmit: event => { event.preventDefault(); void editor.flush() } },
|
|
2477
|
+
React.createElement(ConnectionSwitch, { checked: draft.enabled, disabled, onChange: value => edit('enabled', value) }, t('connection.enabled')),
|
|
2478
|
+
React.createElement('p', { className: 'dsh-super-code-connection-hint' }, t('connection.scope')),
|
|
2479
|
+
React.createElement('div', { className: 'dsh-super-code-connection-grid' },
|
|
2480
|
+
...[['port', 1024, 65535], ['maxConnections', 1, 32]].map(([key, min, max]) => React.createElement('div', { key, className: 'dsh-super-code-settings-field' },
|
|
2481
|
+
React.createElement('label', { htmlFor: `super-code-connection-${key}` }, t(`connection.${key}`)),
|
|
2482
|
+
React.createElement('input', { id: `super-code-connection-${key}`, type: 'number', min, max, step: 1, required: true, value: draft[key], disabled, onChange: event => edit(key, event.target.value), onBlur: () => { void editor.flush() } })))),
|
|
2483
|
+
React.createElement('small', null, t('connection.connectionsHint')),
|
|
2484
|
+
React.createElement('fieldset', { className: 'dsh-super-code-directory-fieldset' },
|
|
2485
|
+
React.createElement('legend', null, t('connection.directories')),
|
|
2486
|
+
React.createElement('div', { className: 'dsh-super-code-directory-list' },
|
|
2487
|
+
...rows.map(row => React.createElement('div', { key: row.path, className: 'dsh-super-code-directory-row' },
|
|
2488
|
+
React.createElement('label', null,
|
|
2489
|
+
React.createElement('input', { type: 'checkbox', checked: draft.allowedDirectories.includes(row.path), disabled: disabled || (!row.available && !draft.allowedDirectories.includes(row.path)),
|
|
2490
|
+
onChange: event => {
|
|
2491
|
+
if (event.target.checked && draft.allowedDirectories.length >= 32) { setError('directory-limit'); return }
|
|
2492
|
+
edit('allowedDirectories', event.target.checked ? [...draft.allowedDirectories, row.path] : draft.allowedDirectories.filter(path => path !== row.path))
|
|
2493
|
+
} }),
|
|
2494
|
+
React.createElement('span', null, row.title && React.createElement('strong', null, row.title), React.createElement('code', null, row.path),
|
|
2495
|
+
React.createElement('small', { 'data-unavailable': !row.available }, t(!row.available ? 'connection.directoryUnavailable' : row.workspace ? 'connection.workspaceDirectory' : 'connection.customDirectory')))),
|
|
2496
|
+
!row.workspace && React.createElement(ActionButton, { type: 'button', disabled, 'aria-label': `${t('connection.removeDirectory')}: ${row.path}`,
|
|
2497
|
+
onClick: () => { setCustom(values => values.filter(path => path !== row.path)); setCatalog(value => ({ ...value, items: value.items.filter(item => item.path !== row.path) })); change({ knownDirectories: draft.knownDirectories.filter(path => path !== row.path), allowedDirectories: draft.allowedDirectories.filter(path => path !== row.path) }) } }, t('preset.remove')))),
|
|
2498
|
+
!rows.length && React.createElement('p', null, t('connection.noDirectories'))),
|
|
2499
|
+
React.createElement('div', { className: 'dsh-super-code-settings-actions' },
|
|
2500
|
+
React.createElement(ActionButton, { type: 'button', disabled: disabled || catalog.picker === 'unavailable', onClick: addDirectory }, t('connection.addDirectory'))),
|
|
2501
|
+
React.createElement('small', null, t('connection.directoriesHint'))),
|
|
2502
|
+
React.createElement('p', { role: 'status', className: 'dsh-super-code-connection-hint', 'data-connection-save': saving ? 'saving' : editing.error ? 'error' : editing.notice === 'saved' ? 'saved' : 'idle' },
|
|
2503
|
+
t(saving ? 'connection.saving' : editing.notice === 'saved' ? 'connection.saved' : 'connection.autosave'))),
|
|
2504
|
+
snapshot?.url && React.createElement('div', { className: 'dsh-super-code-connection-client' },
|
|
2505
|
+
React.createElement('h3', null, t('connection.client')), React.createElement('code', null, snapshot.url), React.createElement('pre', null, command),
|
|
2506
|
+
React.createElement('div', { className: 'dsh-super-code-settings-actions' },
|
|
2507
|
+
React.createElement(ActionButton, { type: 'button', disabled: busy, onClick: () => copy(command) }, t('connection.copyCommand')),
|
|
2508
|
+
React.createElement(ActionButton, { type: 'button', disabled, onClick: () => token ? setToken('') : action('token') }, t(token ? 'connection.hideToken' : 'connection.showToken')),
|
|
2509
|
+
active && token && React.createElement(ActionButton, { type: 'button', onClick: () => copy(token) }, t('connection.copyToken')),
|
|
2510
|
+
React.createElement(ActionButton, { type: 'button', disabled: disabled || dirty, onClick: () => action('rotate') }, t('connection.rotate'))),
|
|
2511
|
+
active && token && React.createElement('input', { className: 'dsh-super-code-connection-token', 'aria-label': t('connection.token'), value: token, readOnly: true, autoComplete: 'off', spellCheck: false }),
|
|
2512
|
+
React.createElement('small', null, t('connection.tokenHint'))),
|
|
2513
|
+
notice && React.createElement('p', { role: 'status' }, t(`connection.${notice}`)),
|
|
2514
|
+
(error || editing.error) && React.createElement('p', { role: 'alert' }, t(`connection.error.${error || editing.error}`)),
|
|
2515
|
+
browser && React.createElement(DirectoryBrowser, { api, t, onSelect: choose, onClose: () => setBrowser(false) }))
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2518
|
+
function SettingsHome({ budgetApi, connectionApi, t, active, revision, navigate, children }) {
|
|
2519
|
+
const [budget, setBudget] = useState(null), [connection, setConnection] = useState(null), [overview, setOverview] = useState({ connections: [], sessions: [] })
|
|
2520
|
+
const [busy, setBusy] = useState(false), [error, setError] = useState(false), alive = useRef(false), pending = useRef(false)
|
|
2521
|
+
const load = async () => {
|
|
2522
|
+
if (pending.current) return
|
|
2523
|
+
pending.current = true
|
|
2524
|
+
try {
|
|
2525
|
+
const [b, c, o] = await Promise.all([budgetApi.readConfig(), connectionApi.readConfig(), connectionApi.overview()])
|
|
2526
|
+
if (!b.ok || !c.ok || !o.ok) throw new Error()
|
|
2527
|
+
if (alive.current) { setBudget(b.value); setConnection(c.value); setOverview(o.value); setError(false) }
|
|
2528
|
+
} catch { if (alive.current) setError(true) }
|
|
2529
|
+
finally { pending.current = false }
|
|
2530
|
+
}
|
|
2531
|
+
useEffect(() => { alive.current = true; return () => { alive.current = false } }, [budgetApi, connectionApi])
|
|
2532
|
+
useEffect(() => { if (!active) return; load(); const timer = setInterval(load, 2000); return () => clearInterval(timer) }, [active, revision, budgetApi, connectionApi])
|
|
2533
|
+
const change = async (kind, value) => {
|
|
2534
|
+
if (busy || pending.current) return
|
|
2535
|
+
pending.current = true; setBusy(true)
|
|
2536
|
+
try {
|
|
2537
|
+
let response
|
|
2538
|
+
if (kind === 'protection') response = await budgetApi.saveConfig(JSON.stringify({ ...budget.configuration, deletionProtection: value }), String(budget.revision))
|
|
2539
|
+
else response = await connectionApi.saveConfig(JSON.stringify({ ...connection.configuration, enabled: value }), String(connection.revision))
|
|
2540
|
+
if (!response.ok || !response.value.ok) throw new Error()
|
|
2541
|
+
} catch { if (alive.current) setError(true); return }
|
|
2542
|
+
finally { pending.current = false; if (alive.current) setBusy(false) }
|
|
2543
|
+
if (alive.current) load()
|
|
2544
|
+
}
|
|
2545
|
+
const mode = budget ? budgetDraft(budget.configuration).deletionProtection : ''
|
|
2546
|
+
return React.createElement('section', { className: 'dsh-super-code-home' }, children,
|
|
2547
|
+
React.createElement('div', { className: 'dsh-super-code-settings-actions' }, React.createElement(ActionButton, { type: 'button', onClick: () => navigate('preset') }, t('home.managePreset'))),
|
|
2548
|
+
React.createElement('h3', { className: 'dsh-super-code-home-category' }, t('home.protection')),
|
|
2549
|
+
React.createElement('div', { className: 'dsh-super-code-home-card' },
|
|
2550
|
+
React.createElement('div', { className: 'dsh-super-code-home-heading' }, React.createElement('strong', null, t('delete.title')),
|
|
2551
|
+
React.createElement(ActionButton, { type: 'button', onClick: () => navigate('protection') }, t('home.configure'))),
|
|
2552
|
+
budget && React.createElement(SelectControl, { label: t('delete.mode'), value: mode, choices: ['off', 'relaxed', 'strict'].map(value => [value, t(`delete.${value}`)]), disabled: busy || !budget.writable, onChange: value => change('protection', value) })),
|
|
2553
|
+
React.createElement('h3', { className: 'dsh-super-code-home-category' }, t('home.extensions')),
|
|
2554
|
+
React.createElement('div', { className: 'dsh-super-code-home-card' },
|
|
2555
|
+
React.createElement('div', { className: 'dsh-super-code-home-heading' }, React.createElement('strong', null, t('home.mcp')),
|
|
2556
|
+
React.createElement(ActionButton, { type: 'button', onClick: () => navigate('connection') }, t('home.configure'))),
|
|
2557
|
+
connection && React.createElement(ConnectionSwitch, { checked: connection.configuration.enabled, disabled: busy || !connection.writable,
|
|
2558
|
+
onChange: value => value && !connection.configuration.allowedDirectories.length ? navigate('connection') : change('connection', value) }, t(`connection.state.${connection.state}`)),
|
|
2559
|
+
connection && React.createElement('small', null, t('home.connectionCount', { count: overview.connections.length, max: connection.configuration.maxConnections })),
|
|
2560
|
+
React.createElement('dl', { className: 'dsh-super-code-session-counts' }, ...['connected', 'history'].map(kind => React.createElement('div', { key: kind, 'data-session-count': kind },
|
|
2561
|
+
React.createElement('dt', null, t(kind === 'connected' ? 'home.connectedSessions' : 'home.historySessions')),
|
|
2562
|
+
React.createElement('dd', null, connection ? connectionSessions(overview)[kind].length : '—'))))),
|
|
2563
|
+
error && React.createElement('p', { role: 'alert' }, t('home.error')))
|
|
2564
|
+
}
|
|
2565
|
+
function ConnectionConsent({ api, t }) {
|
|
2566
|
+
const [requests, setRequests] = useState([]), [busy, setBusy] = useState(false), [error, setError] = useState(false)
|
|
2567
|
+
const dialog = useRef(null), alive = useRef(false), pending = useRef(false), resolving = useRef(false), settled = useRef(new Set())
|
|
2568
|
+
const current = requests[0]
|
|
2569
|
+
const load = async () => {
|
|
2570
|
+
if (pending.current) return
|
|
2571
|
+
pending.current = true
|
|
2572
|
+
try { const result = await api.pendingDirectories(); if (alive.current && result.ok) setRequests(result.value.filter(row => !settled.current.has(row.id))) }
|
|
2573
|
+
catch { /* Transport loss never grants consent. */ }
|
|
2574
|
+
finally { pending.current = false }
|
|
2575
|
+
}
|
|
2576
|
+
useEffect(() => { alive.current = true; load(); const timer = setInterval(load, 1000); return () => { alive.current = false; clearInterval(timer) } }, [api])
|
|
2577
|
+
const resolve = async decision => {
|
|
2578
|
+
if (resolving.current || !current) return
|
|
2579
|
+
resolving.current = true; setBusy(true); setError(false)
|
|
2580
|
+
try {
|
|
2581
|
+
const result = await api.resolveDirectory(current.id, decision)
|
|
2582
|
+
if (!result.ok) throw new Error()
|
|
2583
|
+
if (alive.current) { settled.current.add(current.id); setRequests(rows => rows.filter(row => row.id !== current.id)); window.dispatchEvent(new Event('super-code-directories-changed')) }
|
|
2584
|
+
} catch { if (alive.current) setError(true) }
|
|
2585
|
+
finally { resolving.current = false; if (alive.current) setBusy(false) }
|
|
2586
|
+
}
|
|
2587
|
+
useEffect(() => {
|
|
2588
|
+
if (!current || !dialog.current) return
|
|
2589
|
+
setError(false)
|
|
2590
|
+
const element = dialog.current; element.showModal(); element.querySelector('[data-consent-reject]')?.focus()
|
|
2591
|
+
const escape = event => { if (event.key === 'Escape') { event.preventDefault(); event.stopImmediatePropagation(); if (!resolving.current) resolve('rejected') } }
|
|
2592
|
+
window.addEventListener('keydown', escape, true)
|
|
2593
|
+
return () => { window.removeEventListener('keydown', escape, true); if (element.open) element.close() }
|
|
2594
|
+
}, [current?.id])
|
|
2595
|
+
if (!current) return null
|
|
2596
|
+
return React.createElement('dialog', { ref: dialog, className: 'dsh-super-code-confirm', 'aria-labelledby': 'super-code-consent-title',
|
|
2597
|
+
onCancel: event => { event.preventDefault(); event.stopPropagation(); resolve('rejected') } },
|
|
2598
|
+
React.createElement('h3', { id: 'super-code-consent-title' }, t('connection.requestTitle')),
|
|
2599
|
+
React.createElement('p', null, t('connection.requestHint')),
|
|
2600
|
+
React.createElement('dl', { className: 'dsh-super-code-directory-request' },
|
|
2601
|
+
...[['requestClient', current.client], ['requestPath', current.path], ['requestReason', current.reason]].map(([key, value]) => React.createElement('div', { key }, React.createElement('dt', null, t(`connection.${key}`)), React.createElement('dd', null, value)))),
|
|
2602
|
+
error && React.createElement('p', { role: 'alert' }, t('connection.requestError')),
|
|
2603
|
+
React.createElement('div', { className: 'dsh-super-code-confirm-actions' },
|
|
2604
|
+
React.createElement(ActionButton, { type: 'button', disabled: busy, 'data-consent-reject': true, onClick: () => resolve('rejected') }, t('connection.rejectDirectory')),
|
|
2605
|
+
React.createElement(ActionButton, { type: 'button', disabled: busy, className: 'dsh-super-code-primary-button', onClick: () => resolve('approved') }, t('connection.approveDirectory'))))
|
|
2606
|
+
}
|
|
2607
|
+
|
|
2608
|
+
function PresetSettings({ api, budgetApi, connectionApi, openSession = () => {}, t }) {
|
|
2609
|
+
const [tab, setTab] = useState('home')
|
|
2156
2610
|
const [status, setStatus] = useState(null), [customId, setCustomId] = useState(null)
|
|
2157
2611
|
const reinstall = status?.reinstallable ?? status?.state === 'installed'
|
|
2158
2612
|
const name = customId ?? (reinstall ? status.id : 'super-code')
|
|
@@ -2217,18 +2671,22 @@ function PresetSettings({ api, budgetApi, t }) {
|
|
|
2217
2671
|
}
|
|
2218
2672
|
const available = status && ['available', 'installed'].includes(status.state)
|
|
2219
2673
|
const versionState = status?.presetVersion ? status.presetVersion === status.pluginVersion ? 'match' : 'mismatch' : 'unknown'
|
|
2674
|
+
const [overviewRevision, setOverviewRevision] = useState(0)
|
|
2675
|
+
const onSaved = () => setOverviewRevision(value => value + 1)
|
|
2676
|
+
const tabs = ['home', 'preset', 'context', 'protection', 'connection']
|
|
2220
2677
|
return React.createElement('section', { className: 'dsh-super-code-settings' },
|
|
2221
2678
|
React.createElement('div', { className: 'dsh-super-code-settings-tabs', role: 'tablist', 'aria-label': t('settings.tabs'), onKeyDown: event => {
|
|
2222
2679
|
if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) return
|
|
2223
2680
|
event.preventDefault()
|
|
2224
|
-
const selected = event.key === 'Home' ?
|
|
2681
|
+
const selected = event.key === 'Home' ? tabs[0] : event.key === 'End' ? tabs[tabs.length - 1] : tabs[(tabs.indexOf(tab) + (event.key === 'ArrowRight' ? 1 : tabs.length - 1)) % tabs.length]
|
|
2225
2682
|
setTab(selected)
|
|
2226
2683
|
event.currentTarget.querySelector(`#super-code-tab-${selected}`)?.focus()
|
|
2227
|
-
} }, ...
|
|
2684
|
+
} }, ...tabs.map(value => React.createElement('button', { key: value, type: 'button', role: 'tab',
|
|
2228
2685
|
id: `super-code-tab-${value}`, 'aria-selected': tab === value, 'aria-controls': `super-code-panel-${value}`,
|
|
2229
2686
|
tabIndex: tab === value ? 0 : -1, onClick: () => setTab(value) }, t(`settings.${value}Tab`)))),
|
|
2230
2687
|
React.createElement('div', { className: 'dsh-super-code-settings-body' },
|
|
2231
|
-
React.createElement('div', { role: 'tabpanel', id: 'super-code-panel-
|
|
2688
|
+
React.createElement('div', { role: 'tabpanel', id: 'super-code-panel-home', 'aria-labelledby': 'super-code-tab-home', hidden: tab !== 'home' },
|
|
2689
|
+
React.createElement(SettingsHome, { budgetApi, connectionApi, t, active: tab === 'home', revision: overviewRevision, navigate: setTab },
|
|
2232
2690
|
React.createElement('header', { className: 'dsh-super-code-settings-header' },
|
|
2233
2691
|
React.createElement('span', { className: 'dsh-super-code-settings-mark', 'aria-hidden': true }, React.createElement(Glyph, { type: 'code' })),
|
|
2234
2692
|
React.createElement('div', { className: 'dsh-super-code-settings-head-text' },
|
|
@@ -2249,9 +2707,9 @@ function PresetSettings({ api, budgetApi, t }) {
|
|
|
2249
2707
|
React.createElement('div', null, React.createElement('dt', null, t('preset.presetVersion')), React.createElement('dd', null, status.state === 'missing' ? t('preset.versionMissing') : status.presetVersion || t('preset.versionUnknown')))),
|
|
2250
2708
|
status && status.state !== 'missing' && React.createElement('p', { className: 'dsh-super-code-version-hint', role: 'status', 'data-version-state': versionState },
|
|
2251
2709
|
t(versionState === 'match' ? 'preset.versionMatches' : versionState === 'mismatch' ? 'preset.versionMismatch' : 'preset.versionUnknownHint'),
|
|
2252
|
-
versionState === 'mismatch' && ` ${t(reinstall && status.authorable ? 'preset.versionReinstallHint' : 'preset.versionReloadHint')}`)),
|
|
2710
|
+
versionState === 'mismatch' && ` ${t(reinstall && status.authorable ? 'preset.versionReinstallHint' : 'preset.versionReloadHint')}`)))),
|
|
2711
|
+
React.createElement('div', { role: 'tabpanel', id: 'super-code-panel-preset', 'aria-labelledby': 'super-code-tab-preset', hidden: tab !== 'preset' },
|
|
2253
2712
|
status?.authorable && React.createElement('form', { onSubmit: event => { event.preventDefault(); submit() } },
|
|
2254
|
-
React.createElement('h3', null, t('preset.configuration')),
|
|
2255
2713
|
React.createElement('div', { className: 'dsh-super-code-settings-field' },
|
|
2256
2714
|
React.createElement('label', { htmlFor: 'super-code-display-name' }, t('preset.displayName')),
|
|
2257
2715
|
React.createElement('input', { id: 'super-code-display-name', value: displayName, maxLength: 120, required: true, disabled: busy, 'aria-describedby': 'super-code-display-name-hint', onChange: event => setCustomDisplayName(event.target.value) }),
|
|
@@ -2271,7 +2729,11 @@ function PresetSettings({ api, budgetApi, t }) {
|
|
|
2271
2729
|
status?.delivery === 'directory' && !status.authorable && React.createElement('p', { role: 'note' }, t('preset.readonly'))),
|
|
2272
2730
|
error && React.createElement('p', { role: 'alert' }, t(error))),
|
|
2273
2731
|
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' }))
|
|
2732
|
+
React.createElement(BudgetSettings, { api: budgetApi, t, active: tab === 'context', onSaved })),
|
|
2733
|
+
React.createElement('div', { role: 'tabpanel', id: 'super-code-panel-protection', 'aria-labelledby': 'super-code-tab-protection', hidden: tab !== 'protection' },
|
|
2734
|
+
React.createElement(BudgetSettings, { api: budgetApi, t, section: 'protection', active: tab === 'protection', onSaved })),
|
|
2735
|
+
React.createElement('div', { role: 'tabpanel', id: 'super-code-panel-connection', 'aria-labelledby': 'super-code-tab-connection', hidden: tab !== 'connection' },
|
|
2736
|
+
React.createElement(ConnectionSettings, { api: connectionApi, t, active: tab === 'connection', openSession, onSaved }))),
|
|
2275
2737
|
confirmation && React.createElement('dialog', { ref: dialog, className: 'dsh-super-code-confirm',
|
|
2276
2738
|
'aria-labelledby': 'super-code-confirm-title', 'aria-describedby': 'super-code-confirm-hint',
|
|
2277
2739
|
onKeyDown: event => { if (event.key === 'Escape') event.stopPropagation() },
|
|
@@ -2309,20 +2771,34 @@ function apply(ctx) {
|
|
|
2309
2771
|
// $mount creates a lifecycle effect on this same fiber; awaiting it from
|
|
2310
2772
|
// another effect would stall the namespace and the settings contribution.
|
|
2311
2773
|
void ctx.remote.$mount({ package: 'dsh-super-code', descriptors: [
|
|
2312
|
-
...presetRemoteContribution.descriptors, ...memoryRemoteContribution.descriptors, ...budgetRemoteContribution.descriptors,
|
|
2774
|
+
...presetRemoteContribution.descriptors, ...memoryRemoteContribution.descriptors, ...budgetRemoteContribution.descriptors, ...connectionRemoteContribution.descriptors,
|
|
2313
2775
|
] }).catch(error => console.warn('Super Code Remote:', error))
|
|
2314
2776
|
const memoryStates = new WeakMap(), workspaceStates = new WeakMap()
|
|
2777
|
+
const navigation = { openSession: () => {} }
|
|
2778
|
+
ctx.inject(['uiWorkspace'], ready => {
|
|
2779
|
+
navigation.openSession = id => {
|
|
2780
|
+
if (typeof ready.uiWorkspace.openSession === 'function') ready.uiWorkspace.openSession(id)
|
|
2781
|
+
else ready.sessions.open(id)
|
|
2782
|
+
}
|
|
2783
|
+
ready.effect(() => () => { navigation.openSession = () => {} })
|
|
2784
|
+
})
|
|
2785
|
+
ctx.inject(['remote.superCodeConnection'], ready => {
|
|
2786
|
+
ready.slots.inject('shell.overlay', () => ready.slots.register({
|
|
2787
|
+
name: 'shell.overlay', id: 'super-code-directory-consent', order: 180, locale: LOCALE_NS,
|
|
2788
|
+
inject: () => ({ api: ready.remote.superCodeConnection }),
|
|
2789
|
+
}, ConnectionConsent))
|
|
2790
|
+
})
|
|
2315
2791
|
// Locale already requires its host's preference service. Declare that same
|
|
2316
2792
|
// service here: Cordis rejects even optional reads without an injection.
|
|
2317
2793
|
const preferenceService = 'configForms' in ctx ? 'configForms' : 'settingsScope'
|
|
2318
|
-
ctx.inject(['remote.superCodePresets', 'remote.superCodeBudgets', preferenceService], ready => {
|
|
2794
|
+
ctx.inject(['remote.superCodePresets', 'remote.superCodeBudgets', 'remote.superCodeConnection', preferenceService], ready => {
|
|
2319
2795
|
const preferenceScope = preferenceService === 'configForms'
|
|
2320
2796
|
? ready.configForms.get('locale') : ready.settingsScope.bind({ namespace: 'locale' })
|
|
2321
2797
|
ready.effect(() => synchronizePresetDisplay(ready.remote.superCodePresets, locale,
|
|
2322
2798
|
() => window.location.reload(), error => console.warn('Super Code preset display:', error), preferenceScope), 'super-code: preset display')
|
|
2323
2799
|
ready.slots.inject('settings.section', () => ready.slots.register({
|
|
2324
2800
|
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 }),
|
|
2801
|
+
inject: () => ({ api: ready.remote.superCodePresets, budgetApi: ready.remote.superCodeBudgets, connectionApi: ready.remote.superCodeConnection, openSession: id => navigation.openSession(id) }),
|
|
2326
2802
|
}, PresetSettings))
|
|
2327
2803
|
})
|
|
2328
2804
|
ctx.effect(() => {
|
|
@@ -2413,6 +2889,28 @@ function apply(ctx) {
|
|
|
2413
2889
|
.dsh-super-code-budget-group{display:flex;flex-direction:column;gap:16px;min-width:0}
|
|
2414
2890
|
.dsh-super-code-budget-group h3{margin:0;font-size:14px;font-weight:600}
|
|
2415
2891
|
.dsh-super-code-budget-exclusive{border-top:1px solid var(--sc-line);padding-top:24px}
|
|
2892
|
+
|
|
2893
|
+
.dsh-super-code-connection{display:grid;gap:18px}
|
|
2894
|
+
.dsh-super-code-connection form{display:grid;gap:16px}
|
|
2895
|
+
.dsh-super-code-connection-hint{margin:0;color:var(--sc-muted);font-size:12px;line-height:1.6}
|
|
2896
|
+
.dsh-super-code-connection-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px}
|
|
2897
|
+
.dsh-super-code-connection textarea,.dsh-super-code-connection-token{width:100%;box-sizing:border-box;border:1px solid var(--sc-line);border-radius:8px;background:var(--sc-bg);color:var(--sc-text);padding:9px 11px;font:12px/1.6 ui-monospace,monospace;resize:vertical}
|
|
2898
|
+
.dsh-super-code-connection-client{display:grid;gap:12px;border-top:1px solid var(--sc-line);padding-top:18px;min-width:0}
|
|
2899
|
+
.dsh-super-code-connection-client h3{margin:0;font-size:13px}
|
|
2900
|
+
.dsh-super-code-connection-client pre{white-space:pre-wrap;overflow-wrap:anywhere;font:12px/1.7 ui-monospace,monospace;background:var(--sc-surface);padding:12px;border-radius:8px;margin:0}
|
|
2901
|
+
.dsh-super-code-connection small{color:var(--sc-muted);font-size:12px;line-height:1.6}
|
|
2902
|
+
.dsh-super-code-connection .dsh-super-code-settings-actions{flex-wrap:wrap;gap:8px}
|
|
2903
|
+
.dsh-super-code-connection-switch{position:relative;display:inline-flex;align-items:center;gap:10px;cursor:pointer;font-size:13px;font-weight:500;width:fit-content}
|
|
2904
|
+
.dsh-super-code-connection-switch input[role=switch]{position:absolute;opacity:0;width:34px;height:20px;margin:0;padding:0;left:0;top:50%;transform:translateY(-50%);z-index:1;cursor:pointer}
|
|
2905
|
+
.dsh-super-code-connection-track{display:inline-flex;align-items:center;flex:none;width:34px;height:20px;border:1px solid var(--sc-line);border-radius:20px;padding:2px;background:var(--sc-surface);transition:background .15s}
|
|
2906
|
+
.dsh-super-code-connection-track i{display:block;width:14px;height:14px;border-radius:50%;background:var(--sc-muted);transition:transform .15s}
|
|
2907
|
+
.dsh-super-code-connection-switch input:checked+.dsh-super-code-connection-track{background:var(--sc-accent);border-color:var(--sc-accent)}
|
|
2908
|
+
.dsh-super-code-connection-switch input:checked+.dsh-super-code-connection-track i{transform:translateX(14px);background:#fff}
|
|
2909
|
+
.dsh-super-code-connection-switch input:focus-visible+.dsh-super-code-connection-track{outline:2px solid var(--sc-accent);outline-offset:3px}
|
|
2910
|
+
.dsh-super-code-connection-switch input:disabled+.dsh-super-code-connection-track{opacity:.5}
|
|
2911
|
+
.dsh-super-code-connection input[type=number]{appearance:textfield}
|
|
2912
|
+
.dsh-super-code-connection input::-webkit-inner-spin-button{appearance:none}
|
|
2913
|
+
|
|
2416
2914
|
.dsh-super-code-budget-switch{display:flex;align-items:center;gap:10px;font-size:13px;cursor:pointer}
|
|
2417
2915
|
.dsh-super-code-budget-switch input{width:30px;height:17px;flex:none;accent-color:var(--sc-active);cursor:pointer}
|
|
2418
2916
|
.dsh-super-code-budget-default{color:var(--sc-muted);font-weight:400;font-size:12px}
|
|
@@ -2427,6 +2925,60 @@ function apply(ctx) {
|
|
|
2427
2925
|
.dsh-super-code-settings [role=alert],.dsh-super-code-detail [role=alert],.dsh-super-code-tree-error{font-size:13px;color:var(--sc-error);overflow-wrap:anywhere}
|
|
2428
2926
|
@container(max-width:420px){.dsh-super-code-settings-header{padding-bottom:16px;margin-bottom:16px}.dsh-super-code-settings-body{padding:16px}.dsh-super-code-settings-summary{padding:12px}.dsh-super-code-settings-status-row{gap:8px}.dsh-super-code-preset-status{flex-basis:100%}.dsh-super-code-settings-form-actions>small{min-width:0;flex-basis:100%}}
|
|
2429
2927
|
|
|
2928
|
+
.dsh-super-code-session-counts{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px;margin:4px 0 0;padding-top:12px;border-top:1px solid var(--sc-line)}
|
|
2929
|
+
.dsh-super-code-session-counts dt{font-size:12px;color:var(--sc-muted)}
|
|
2930
|
+
.dsh-super-code-session-counts dd{font-size:22px;font-variant-numeric:tabular-nums;font-weight:600;margin:3px 0 0}
|
|
2931
|
+
.dsh-super-code-connection-activity{display:grid;gap:10px;border:1px solid var(--sc-line);border-radius:10px;padding:14px}
|
|
2932
|
+
.dsh-super-code-connection-activity h3{margin:0;font-size:13px;font-weight:600}
|
|
2933
|
+
.dsh-super-code-connection-activity h3:not(:first-child){margin-top:6px}
|
|
2934
|
+
.dsh-super-code-connection-activity h4{margin:0 0 6px;font-size:12px;color:var(--sc-muted);font-weight:500}
|
|
2935
|
+
.dsh-super-code-connection-activity p{margin:0;font-size:12px;color:var(--sc-muted)}
|
|
2936
|
+
.dsh-super-code-connection-activity button{border:1px solid var(--sc-line);border-radius:7px;background:var(--sc-bg);padding:4px 8px;font-size:12px}
|
|
2937
|
+
.dsh-super-code-connection-activity button:hover{background:var(--sc-hover)}
|
|
2938
|
+
/* Settings overview and directory consent use the same host theme aliases. */
|
|
2939
|
+
.dsh-super-code-settings-tabs{gap:20px;overflow-x:auto;white-space:nowrap}
|
|
2940
|
+
.dsh-super-code-home{display:grid;gap:12px}
|
|
2941
|
+
.dsh-super-code-home-category{font-size:11px;letter-spacing:.06em;color:var(--sc-muted);margin:12px 0 0}
|
|
2942
|
+
.dsh-super-code-home-card{display:grid;gap:12px;border:1px solid var(--sc-line);border-radius:10px;padding:14px}
|
|
2943
|
+
.dsh-super-code-home-heading{display:flex;justify-content:space-between;align-items:center;gap:12px}
|
|
2944
|
+
.dsh-super-code-home-heading strong{font-size:13px;font-weight:600}
|
|
2945
|
+
.dsh-super-code-home-card :is(p,small){margin:0;font-size:12px;color:var(--sc-muted)}
|
|
2946
|
+
.dsh-super-code-home-card h4{font-size:12px;font-weight:500;margin:6px 0 0}
|
|
2947
|
+
.dsh-super-code-home-card button,.dsh-super-code-home>.dsh-super-code-settings-actions button{border:1px solid var(--sc-line);border-radius:7px;background:var(--sc-bg);padding:4px 8px;font-size:12px}
|
|
2948
|
+
.dsh-super-code-home-card button:hover{background:var(--sc-hover)}
|
|
2949
|
+
.dsh-super-code-connection-list{list-style:none;padding:0;margin:0;max-height:280px;overflow:auto}
|
|
2950
|
+
.dsh-super-code-connection-list li{display:flex;align-items:center;gap:10px;padding:9px 0;border-bottom:1px solid var(--sc-line)}
|
|
2951
|
+
.dsh-super-code-connection-list li:last-child{border:0}
|
|
2952
|
+
.dsh-super-code-connection-list li>span{display:grid;gap:2px;flex:1;min-width:0;overflow-wrap:anywhere}
|
|
2953
|
+
.dsh-super-code-connection-list strong{font-size:12px;font-weight:500}
|
|
2954
|
+
.dsh-super-code-connection-list code{font:10px/1.5 var(--sc-mono);color:var(--sc-muted)}
|
|
2955
|
+
.dsh-super-code-connection-list button{flex:none}
|
|
2956
|
+
.dsh-super-code-home .dsh-super-code-settings-header{padding-bottom:12px;margin-bottom:0}
|
|
2957
|
+
.dsh-super-code-directory-fieldset{border:0;padding:0;margin:0;min-width:0;display:grid;gap:10px}
|
|
2958
|
+
.dsh-super-code-directory-fieldset legend{font-size:13px;font-weight:500;margin-bottom:10px}
|
|
2959
|
+
.dsh-super-code-directory-list{border:1px solid var(--sc-line);border-radius:9px;max-height:330px;overflow:auto}
|
|
2960
|
+
.dsh-super-code-directory-list>p{margin:0;padding:18px;color:var(--sc-muted);font-size:12px}
|
|
2961
|
+
.dsh-super-code-directory-row{display:flex;align-items:center;gap:8px;padding:10px 12px}
|
|
2962
|
+
.dsh-super-code-directory-row+.dsh-super-code-directory-row{border-top:1px solid var(--sc-line)}
|
|
2963
|
+
.dsh-super-code-directory-row label{display:flex;align-items:center;flex:1;min-width:0;gap:10px;cursor:pointer}
|
|
2964
|
+
.dsh-super-code-directory-row input[type=checkbox]{appearance:none;width:16px;height:16px;flex:none;border:1px solid var(--sc-line);padding:0;border-radius:4px;background:var(--sc-bg);cursor:pointer}
|
|
2965
|
+
.dsh-super-code-directory-row input[type=checkbox]:checked{background:var(--sc-accent);border-color:var(--sc-accent);box-shadow:inset 0 0 0 3px var(--sc-bg)}
|
|
2966
|
+
.dsh-super-code-directory-row input:disabled{opacity:.5;cursor:default}
|
|
2967
|
+
.dsh-super-code-directory-row label>span{display:grid;min-width:0;gap:1px}
|
|
2968
|
+
.dsh-super-code-directory-row strong{font-size:12px;font-weight:500}
|
|
2969
|
+
.dsh-super-code-directory-row code{font:11px/1.6 var(--sc-mono);overflow-wrap:anywhere}
|
|
2970
|
+
.dsh-super-code-directory-row small[data-unavailable=true]{color:var(--sc-warning)}
|
|
2971
|
+
.dsh-super-code-directory-row>button{font-size:11px;border:0;background:none;padding:5px;color:var(--sc-muted)}
|
|
2972
|
+
.dsh-super-code-directory-crumbs{display:flex;flex-wrap:wrap;gap:4px;margin:12px 0}
|
|
2973
|
+
.dsh-super-code-directory-crumbs button{font-size:11px}
|
|
2974
|
+
.dsh-super-code-directory-path{display:block;overflow-wrap:anywhere;font-size:12px;margin-bottom:10px}
|
|
2975
|
+
.dsh-super-code-directory-browser{display:grid;max-height:320px;overflow:auto;gap:2px}
|
|
2976
|
+
.dsh-super-code-directory-browser button{display:flex;align-items:center;justify-content:flex-start;gap:8px;border:0;text-align:left;overflow-wrap:anywhere}
|
|
2977
|
+
.dsh-super-code-directory-request{display:grid;gap:12px;padding:14px;border:1px solid var(--sc-line);border-radius:9px;background:var(--sc-surface)}
|
|
2978
|
+
.dsh-super-code-directory-request dt{font-size:11px;color:var(--sc-muted);margin-bottom:3px}
|
|
2979
|
+
.dsh-super-code-directory-request dd{font-size:13px;line-height:1.6;overflow-wrap:anywhere;white-space:pre-wrap;margin:0}
|
|
2980
|
+
.dsh-super-code-directory-request>div:nth-child(2) dd{font-family:var(--sc-mono);font-size:12px}
|
|
2981
|
+
|
|
2430
2982
|
/* Confirmation. */
|
|
2431
2983
|
.dsh-super-code-confirm{width:min(520px,calc(100vw - 32px));max-height:calc(100dvh - 48px);overflow:auto;border:1px solid var(--sc-line);border-radius:16px;padding:24px;background:var(--sc-bg);box-shadow:0 12px 48px #0003}
|
|
2432
2984
|
.dsh-super-code-confirm::backdrop{background:#10182066}
|
|
@@ -2854,5 +3406,5 @@ function apply(ctx) {
|
|
|
2854
3406
|
ctx.effect(() => disposeMemoryType, 'super-code: memory library tab')
|
|
2855
3407
|
}
|
|
2856
3408
|
|
|
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 }
|
|
3409
|
+
return { inject, apply, taskFeedbackRows, feedbackStatus, feedbackListItems, feedbackListLayout, feedbackListIndex, feedbackListWindow, feedbackListAnchor, connectionSessions, createConnectionAutosave, connectionDraft, connectionConfiguration, budgetDraft, budgetConfiguration, synchronizePresetDisplay, agentSessionState, buildAgentView, visibleAgentRows, agentTreeDisplay, agentTreeTotals, agentTreeLayout, agentExecutionStatus, agentDetailAddress, agentDetailTarget, agentPath, detailRecord, createAgentInspector, agentHistoryRange, agentHistoryRows, agentHistoryWindow, memoryLibraryAddress, memoryLibraryTarget, memoryLibraryNavigation, memoryLibraryWindow, createMemoryEditor }
|
|
2858
3410
|
} })
|