@mengruo/dsh-vision-toolkit 0.1.5 → 0.1.6-beta.0

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.
Files changed (48) hide show
  1. package/assets/1.mp4 +0 -0
  2. package/assets/skill/SKILL.md +37 -5
  3. package/docs/plan-per-tool-visibility.md +82 -0
  4. package/lib/client.js +53 -3
  5. package/lib/client.js.map +1 -1
  6. package/lib/config.js +17 -0
  7. package/lib/config.js.map +1 -1
  8. package/lib/exposure.js +35 -8
  9. package/lib/exposure.js.map +1 -1
  10. package/lib/index.js +1 -1
  11. package/lib/index.js.map +1 -1
  12. package/lib/paths.js +9 -0
  13. package/lib/paths.js.map +1 -1
  14. package/lib/runtime.js +175 -1
  15. package/lib/runtime.js.map +1 -1
  16. package/lib/tools.js +116 -4
  17. package/lib/tools.js.map +1 -1
  18. package/lib/types/client/index.d.ts +24 -1
  19. package/lib/types/client/index.d.ts.map +1 -1
  20. package/lib/types/config.d.ts +26 -0
  21. package/lib/types/config.d.ts.map +1 -1
  22. package/lib/types/exposure.d.ts +14 -2
  23. package/lib/types/exposure.d.ts.map +1 -1
  24. package/lib/types/index.d.ts.map +1 -1
  25. package/lib/types/paths.d.ts +7 -0
  26. package/lib/types/paths.d.ts.map +1 -1
  27. package/lib/types/runtime.d.ts +38 -0
  28. package/lib/types/runtime.d.ts.map +1 -1
  29. package/lib/types/tools.d.ts +22 -1
  30. package/lib/types/tools.d.ts.map +1 -1
  31. package/lib/types/video.d.ts +78 -0
  32. package/lib/types/video.d.ts.map +1 -0
  33. package/lib/types/web.d.ts +1 -0
  34. package/lib/types/web.d.ts.map +1 -1
  35. package/lib/video.js +169 -0
  36. package/lib/video.js.map +1 -0
  37. package/lib/web.js +47 -4
  38. package/lib/web.js.map +1 -1
  39. package/package.json +2 -1
  40. package/src/client/index.tsx +72 -1
  41. package/src/config.ts +43 -0
  42. package/src/exposure.ts +34 -9
  43. package/src/index.ts +10 -5
  44. package/src/paths.ts +11 -0
  45. package/src/runtime.ts +196 -0
  46. package/src/tools.ts +144 -3
  47. package/src/video.ts +222 -0
  48. package/src/web.ts +53 -5
package/assets/1.mp4 ADDED
Binary file
@@ -1,15 +1,25 @@
1
1
  # vision-skills
2
2
 
3
- Ten native DSH tools give a text-only agent eyes. Use these structured tools
3
+ Native DSH tools give a text-only agent eyes. Use these structured tools
4
4
  directly; do not shell out to the bundled Python scripts or reproduce their
5
5
  implementation. Vision API credentials and model settings are managed by the
6
6
  plugin, so tool calls do not receive credentials.
7
7
 
8
8
  The visual execution schemas are mounted only for the current Agent after this
9
9
  Skill is loaded. A normal `skill` call activates them for the next model step.
10
- If this content arrived through a direct `/vision-skills` invocation and the
11
- visual tools are still absent, call `vision_toolkit_activate` once. Do not call
12
- that bootstrap when the visual tools are already present.
10
+ Which tools actually mount is governed by the plugin's **tool-visibility**
11
+ snapshot in Settings (local / on-line / video buckets); each bucket's tools
12
+ appear only when that bucket is enabled. If this content arrived through a
13
+ direct `/vision-skills` invocation and a listed tool is still absent, call
14
+ `vision_toolkit_activate` once **only when the user explicitly asks you to
15
+ refresh or list the current vision tools** — doing so re-reads the latest
16
+ Settings snapshot and returns the tool names currently mounted. Do not call
17
+ that bootstrap just to discover the tool list on ordinary image work.
18
+
19
+ Concurrency applies only to on-line vision calls (glance, ground, detect,
20
+ long-screenshot OCR, video understanding); the local-processing tools (trace,
21
+ crop, pixel diff, foreground, colors, HTML screenshot, video info) run
22
+ immediately and are not throttled by it.
13
23
 
14
24
  Pick the tool by the question you are answering:
15
25
 
@@ -239,6 +249,27 @@ significant colours and their shares. Candidate mode scores each supplied
239
249
  value against the pixels and returns the winner. Take the value from here,
240
250
  never from `vision_glance` prose.
241
251
 
252
+ ## Vision tools (video) — info is local, understanding is on-line
253
+
254
+ Both video tools live under the plugin's **video tool bucket** in Settings
255
+ (the video-understanding one is experimental and off by default). When their
256
+ bucket is enabled:
257
+
258
+ ```json
259
+ {"video":"clip.mp4"}
260
+ {"video":"clip.mp4","prompt":"what happens in this video?","fps":2}
261
+ ```
262
+
263
+ - `vision_video_info` — local ffprobe probe of a video file's metadata
264
+ (format, duration, resolution, frame rate, codecs). No API call, no
265
+ credential, never throttled by concurrency. This is in the **local** bucket,
266
+ not the video bucket.
267
+ - `vision_video_understand` — uploads the video to object storage and sends it
268
+ plus your prompt to a configured vision service. It requires a video-capable
269
+ vision service; if none is enabled, the call returns a
270
+ "video understanding unavailable" config error — do not retry it with a
271
+ different wording. This is in the **video** bucket.
272
+
242
273
  ## Prefer a durable path; platform temp paths are supported
243
274
 
244
275
  Use workspace storage when the image or a derived artifact must remain
@@ -318,7 +349,8 @@ resource base and load only the relevant file.
318
349
  - Only PNG / JPEG / GIF / WebP images are supported.
319
350
  - `vision_html_screenshot` accepts local `.html` / `.htm` files only, not URLs
320
351
  or data URIs.
321
- - If a visual tool is absent after Skill activation, report that the plugin
352
+ - If a visual tool is absent after Skill activation, first check whether its
353
+ tool-visibility bucket is disabled in Settings; report that the plugin
322
354
  runtime is unavailable instead of improvising a shell replacement.
323
355
  - If a tool fails, relay its stable error faithfully and fix the identified
324
356
  path, limit, Credential, runtime, or service condition. Never fabricate image
@@ -0,0 +1,82 @@
1
+ # 按工具组可见性开关(toolVisibility)实现规格
2
+
3
+ ## 目标
4
+
5
+ 在 `vision-toolkit` 配置下新增一个粗粒度可见性开关组,按"会话头快照"生效:
6
+
7
+ - 开 → 该组工具在**下一个 agent 对话**(下次激活)可见;
8
+ - 关 → 不可见;已激活 agent 维持激活时的旧快照,不实时增删(激活时截取快照语义)。
9
+ - 保留全局兜底 `vision_toolkit_activate`,但语义改为"显式刷新/重载视觉工具集",AI 只在用户要求时调用。
10
+
11
+ ## 三个开关与工具分桶
12
+
13
+ 新配置段(全局一份,旧配置自动兼容,缺省即默认值):
14
+
15
+ ```yaml
16
+ vision-toolkit:
17
+ toolVisibility:
18
+ local: true # 本地处理工具,默认开
19
+ online: true # 联机图片工具 + concurrency,默认开
20
+ video: false # 视频理解工具,默认关(UI 标注「测试」)
21
+ ```
22
+
23
+ | 桶 | 开关字段 | 工具 | 走 concurrency? |
24
+ |----|---------|------|----------------|
25
+ | **local** | `toolVisibility.local` | `vision_trace`、`vision_crop`、`vision_pixel_diff`、`vision_extract_foreground`、`vision_dominant_colors`、`vision_html_screenshot`、`vision_video_info` | 否(本地处理,无并联限制) |
26
+ | **online** | `toolVisibility.online` | `vision_glance`、`vision_ground`、`vision_detect`、`vision_long_screenshot_ocr`、`vision_concurrency` | 是(受 session 并发限流) |
27
+ | **video** | `toolVisibility.video` | `vision_video_understand` | 是(走后端能力) |
28
+ | 兜底 | 永远可用 | `vision_toolkit_activate` | — |
29
+
30
+ > 说明:
31
+ > - `vision_concurrency` 归 `online` 桶,且其语义改为"当前**联机服务**的可用并发配额",本地工具不受并行限制。工具描述 / 返回需随之更新。
32
+ > - 上游"那 10 个工具"被拆进 local 与 online 两桶,二者默认均开,构成完整上游集 + `video_info`。
33
+ > - 默认全开(local+online),video 默认关。
34
+
35
+ ## 可见性判定
36
+
37
+ 对某个工具 `T`:**激活 agent 作用域工具集时**,根据当时读到的 `toolVisibility` 快照,逐个决定是否注册该工具的定义。
38
+
39
+ - `local` 桶工具 → 若 `toolVisibility.local === true`。
40
+ - `online` 桶工具 → 若 `toolVisibility.online === true`。
41
+ - `video` 桶(`vision_video_understand`) → 若 `toolVisibility.video === true`;**不再**受 `videoSupportEnabled` 门控决定"是否暴露"。
42
+ - 激活后不再变动;后续设置改动只影响下一个 agent 的激活。
43
+
44
+ ## 视频工具与后端能力的解耦
45
+
46
+ `videoSupportEnabled`(provider 的 `videoSupport` 标记)**不再**决定 `vision_video_understand` 是否出现在工具列表。
47
+
48
+ - 工具可见性:仅由 `toolVisibility.video` 决定。
49
+ - 工具成功率:调用 `videoUnderstand` 时,若无任何具备视频能力的可用 provider/模型,**返回明确的"视频理解不可用"错误**(沿用 `config` 类错误,文案如 `no enabled vision service has video support; enable it in Settings first`),而不是在暴露阶段就隐藏。
50
+
51
+ ## `vision_toolkit_activate` 兜底改造
52
+
53
+ - **始终存在、不分桶**。
54
+ - **描述改写**:从"激活 vison 工具"改为"**当用户显式要求刷新/重载当前会话的视觉工具时调用**"。要求模型仅在用户显式触发时使用,不作为普通激活路径。
55
+ - 调用后按**当时**的 `toolVisibility` 快照重新生成工具集(重载),并返回当前实际可用的工具清单,让 AI 自查。
56
+ - 保持"激活后从模型视野收起"的既有行为,但语义对齐上述描述。
57
+
58
+ ## 配置 / UI
59
+
60
+ - `src/config.ts`:新增 `toolVisibility` 字段(`local`/`online`/`video` 三个 bool),schema 默认 `{local:true, online:true, video:false}`,迁移兼容旧配置。
61
+ - `src/client/index.tsx`:视觉工具高级设置区新增三个开关,label:
62
+ - 本地工具(默认开)
63
+ - 联机图片工具(默认开)
64
+ - 视频工具(默认关,文案标注「(测试)」)
65
+ - `src/runtime.ts`:`videoSupportEnabled` 语义保持"存在 video-capable provider";`videoUnderstand` 在无视频 provider 时返回"视频理解不可用"。
66
+
67
+ ## 涉及文件
68
+
69
+ - `src/config.ts`(schema / defaults / 迁移)
70
+ - `src/index.ts`(将 toolVisibility 快照传入 exposure 生成逻辑)
71
+ - `src/exposure.ts`(激活时按快照生成对应桶子集;重载语义;activate 描述改写)
72
+ - `src/tools.ts`(按桶把工具定义分组;concurrency 描述/语义调整;video 工具与 capability 解耦)
73
+ - `src/runtime.ts`(`videoUnderstand` 无能力即报错)
74
+ - `src/client/index.tsx`(高级设置 UI:三个开关 + 文案)
75
+ - `assets/skill/SKILL.md`(描述补充:按桶可用性、concurrency 语义、activate 的新语义)
76
+ - `docs/plan-per-tool-visibility.md`(本文件)
77
+
78
+ ## 待实现时再确认的细点(标记,非阻塞)
79
+
80
+ - `vision_video_info` 归 local 桶;如需并入 video 桶再调整。
81
+ - `vision_concurrency` 返回改写为"联机并发可用额度 + 本地无限制"的说明文案。
82
+ - 空桶(如 local 关但 online 开)时无工具可注册,exposure 需正确处理"只有兜底 activate"的情况。
package/lib/client.js CHANGED
@@ -132,6 +132,8 @@ const en = {
132
132
  streamHint: 'Request a streamed (SSE) completion instead of one JSON response. Use it for endpoints with weak non-streaming support or that time out on long outputs.',
133
133
  uploadViaUrl: 'Transfer images via URL',
134
134
  uploadViaUrlHint: 'Upload each image to the configured object storage and send the model a URL instead of base64. Requires object storage (below); region crops fall back to base64.',
135
+ videoSupport: 'Enable video support',
136
+ videoSupportHint: 'OpenAI-compatible video understanding. This setting is hidden for Anthropic providers and is currently a placeholder with no effect yet.',
135
137
  language: 'Output language',
136
138
  limits: 'Timeout and concurrency',
137
139
  timeout: 'Request timeout (ms)',
@@ -186,8 +188,10 @@ const en = {
186
188
  runHealth: 'Run health check',
187
189
  testConnection: 'Test API connection',
188
190
  testModel: 'Test vision model',
191
+ testVideo: 'Test video call',
189
192
  testing: 'Checking…',
190
193
  testingModel: 'Testing model…',
194
+ testingVideo: 'Testing video…',
191
195
  connectionHint: 'The API connection test only queries GET /models. The vision model test sends the bundled diagnostic image and verifies one real multimodal request.',
192
196
  saveBeforeTesting: 'Save service changes before testing the connection.',
193
197
  advanced: 'Advanced settings',
@@ -196,6 +200,14 @@ const en = {
196
200
  hiddenVariants: 'Transparent variant routing',
197
201
  hiddenVariantsLabel: 'Keep the original model names and enable images automatically',
198
202
  hiddenVariantsHint: 'Text-only models keep one model-selector entry with the original name while the session runs on the image-capable variant. Pasted images, image history, and the built-in read_image tool keep working; disable to restore the explicit (Vision Toolkit) entries.',
203
+ toolVisibility: 'Tool visibility',
204
+ toolVisibilityHint: 'Which groups of vision tools an Agent sees. Applies to the next Agent conversation: an already-active Agent keeps its activation-time set until it is reloaded.',
205
+ toolVisibilityLocal: 'Local tools',
206
+ toolVisibilityLocalHint: 'trace / crop / pixel diff / foreground / colors / HTML screenshot / video info. Local processing, no on-line parallel limits.',
207
+ toolVisibilityOnline: 'Online image tools',
208
+ toolVisibilityOnlineHint: 'glance / ground / detect / long-screenshot OCR, plus the concurrency/status probe. On-line service calls share the session concurrency budget.',
209
+ toolVisibilityVideo: 'Video tools',
210
+ toolVisibilityVideoHint: 'video understanding (experimental). Calling it without an enabled video-capable service returns "video understanding unavailable".',
199
211
  pluginVersion: 'Plugin',
200
212
  upstreamVersion: 'Upstream',
201
213
  activeGeneration: 'Runtime generation',
@@ -364,6 +376,8 @@ const zh = {
364
376
  streamHint: '以流式(SSE)方式请求补全,而非一次性返回 JSON。适合对非流式支持不佳、长输出易连接超时的端点。',
365
377
  uploadViaUrl: '通过 URL 传输图片',
366
378
  uploadViaUrlHint: '把每张图片上传到下方配置的对象存储,将 URL 传给模型而不是 base64。需要先配置对象存储;使用 region 裁剪时回退为 base64。',
379
+ videoSupport: '启用视频支持',
380
+ videoSupportHint: 'OpenAI 兼容接口的视频理解能力。Anthropic 服务不显示此项;当前为占位设置,尚未生效。',
367
381
  language: '结果语言',
368
382
  limits: '超时与并发限制',
369
383
  timeout: '单次请求超时(毫秒)',
@@ -418,8 +432,10 @@ const zh = {
418
432
  runHealth: '检查本地环境',
419
433
  testConnection: '测试 API 连接',
420
434
  testModel: '测试视觉模型',
435
+ testVideo: '测试视频调用',
421
436
  testing: '检查中…',
422
437
  testingModel: '正在测试模型…',
438
+ testingVideo: '正在测试视频…',
423
439
  connectionHint: '“测试 API 连接”只请求 GET /models;“测试视觉模型”会发送插件自带的诊断图片,验证一次真实多模态调用。',
424
440
  saveBeforeTesting: '修改服务配置后,请先保存,再执行 API 或视觉模型测试。',
425
441
  advanced: '高级设置',
@@ -428,6 +444,14 @@ const zh = {
428
444
  hiddenVariants: '透明变体路由',
429
445
  hiddenVariantsLabel: '保留原模型名并自动启用图片能力',
430
446
  hiddenVariantsHint: '文本模型在模型列表中只显示原名称,会话实际运行在支持图片的变体路由上:粘贴图片、历史图片和内置 read_image 工具均可正常使用。关闭后恢复显示显式的(Vision Toolkit)条目。',
447
+ toolVisibility: '工具可见性',
448
+ toolVisibilityHint: 'Agent 可见的视觉工具分组。仅在下一个 agent 对话中生效:已激活的 agent 保持激活时的工具集,直到重载。',
449
+ toolVisibilityLocal: '本地工具',
450
+ toolVisibilityLocalHint: 'trace / crop / 像素对比 / 前景提取 / 取色 / HTML 截图 / 视频信息。本地处理,不受联机并发限制。',
451
+ toolVisibilityOnline: '联机图片工具',
452
+ toolVisibilityOnlineHint: 'glance / ground / detect / 长截图 OCR,以及并发/状态查询。联机调用共享会话并发额度。',
453
+ toolVisibilityVideo: '视频工具',
454
+ toolVisibilityVideoHint: '视频理解(测试)。调用时若没有已启用且支持视频的服务,将返回“视频理解不可用”。',
431
455
  pluginVersion: '插件版本',
432
456
  upstreamVersion: '工具包版本',
433
457
  activeGeneration: '本次运行已应用',
@@ -928,6 +952,20 @@ class VisionSettingsController {
928
952
  this.set({ ...this.state, action: undefined, error: error instanceof Error ? error.message : String(error) });
929
953
  }
930
954
  }
955
+ async runVideoTest(providerIndex) {
956
+ this.set({ ...this.state, action: 'test-video', error: undefined, message: undefined });
957
+ try {
958
+ const result = await apiRequest({
959
+ method: 'POST',
960
+ headers: { 'Content-Type': 'application/json' },
961
+ body: JSON.stringify({ action: 'test-video', ...(providerIndex === undefined ? {} : { providerIndex }) }),
962
+ });
963
+ this.set({ ...this.state, action: undefined, videoTest: result });
964
+ }
965
+ catch (error) {
966
+ this.set({ ...this.state, action: undefined, error: error instanceof Error ? error.message : String(error) });
967
+ }
968
+ }
931
969
  async checkUpdate() {
932
970
  this.set({ ...this.state, action: 'check-update', error: undefined, message: undefined });
933
971
  try {
@@ -994,6 +1032,7 @@ function emptyProviderDraft(defaults) {
994
1032
  userAgent: DEFAULT_USER_AGENT,
995
1033
  stream: false,
996
1034
  uploadViaUrl: false,
1035
+ videoSupport: false,
997
1036
  t1Seconds: '90',
998
1037
  t2Seconds: '90',
999
1038
  maxImageBytes: String(defaults.maxImageBytes),
@@ -1017,6 +1056,7 @@ function providerDraftOf(value, fallback, defaults) {
1017
1056
  userAgent: source?.userAgent ?? DEFAULT_USER_AGENT,
1018
1057
  stream: source?.stream === true,
1019
1058
  uploadViaUrl: source?.uploadViaUrl === true,
1059
+ videoSupport: source?.videoSupport === true,
1020
1060
  t1Seconds: String(source?.t1Seconds ?? 90),
1021
1061
  t2Seconds: String(source?.t2Seconds ?? 90),
1022
1062
  maxImageBytes: String(source?.maxImageBytes ?? defaults.maxImageBytes),
@@ -1056,6 +1096,9 @@ function draftOf(value) {
1056
1096
  variantEnabled: value.imageInputVariants?.enabled ?? true,
1057
1097
  variantProviders: (value.imageInputVariants?.providers ?? []).join('\n'),
1058
1098
  variantAutoSwitch: value.imageInputVariants?.autoSwitch ?? true,
1099
+ toolVisibilityLocal: value.toolVisibility?.local ?? true,
1100
+ toolVisibilityOnline: value.toolVisibility?.online ?? true,
1101
+ toolVisibilityVideo: value.toolVisibility?.video ?? false,
1059
1102
  };
1060
1103
  }
1061
1104
  function positiveInteger(raw, label, t) {
@@ -1089,6 +1132,7 @@ function valueOf(draft, t) {
1089
1132
  ...(provider.userAgent.trim() === DEFAULT_USER_AGENT ? {} : { userAgent: provider.userAgent.trim() }),
1090
1133
  ...(provider.stream ? { stream: true } : {}),
1091
1134
  ...(provider.uploadViaUrl ? { uploadViaUrl: true } : {}),
1135
+ ...(provider.videoSupport ? { videoSupport: true } : {}),
1092
1136
  ...(provider.t1Seconds.trim().length === 0 ? {} : { t1Seconds: positiveInteger(provider.t1Seconds, t('t1'), t) }),
1093
1137
  ...(provider.t2Seconds.trim().length === 0 ? {} : { t2Seconds: positiveInteger(provider.t2Seconds, t('t2'), t) }),
1094
1138
  ...(provider.maxImageBytes.trim().length === 0 ? {} : { maxImageBytes: positiveInteger(provider.maxImageBytes, t('maxBytes'), t) }),
@@ -1107,6 +1151,7 @@ function valueOf(draft, t) {
1107
1151
  userAgent: primary?.userAgent.trim() || DEFAULT_USER_AGENT,
1108
1152
  stream: primary?.stream === true,
1109
1153
  uploadViaUrl: primary?.uploadViaUrl === true,
1154
+ videoSupport: primary?.videoSupport === true,
1110
1155
  },
1111
1156
  providers,
1112
1157
  language: draft.language,
@@ -1141,6 +1186,11 @@ function valueOf(draft, t) {
1141
1186
  ...(draft.variantAutoSwitch ? {} : { autoSwitch: false }),
1142
1187
  hidden: draft.hiddenVariants,
1143
1188
  },
1189
+ toolVisibility: {
1190
+ local: draft.toolVisibilityLocal,
1191
+ online: draft.toolVisibilityOnline,
1192
+ video: draft.toolVisibilityVideo,
1193
+ },
1144
1194
  };
1145
1195
  }
1146
1196
  function settingsDraftChanged(draft, saved, t) {
@@ -1452,17 +1502,17 @@ function LoadedSettings({ controller, t }) {
1452
1502
  void controller.applyUpdate(latestVersion);
1453
1503
  };
1454
1504
  const providerHealth = state.providerHealth?.[selectedIndex];
1455
- return ((0, jsx_runtime_1.jsxs)("div", { className: "dvt-settings", children: [(0, jsx_runtime_1.jsxs)("div", { className: "dvt-save-dock", "data-dirty": updateHasUnsavedChanges ? 'true' : undefined, children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "dvt-save-dock-reload", "aria-label": t('reload'), title: t('reload'), disabled: busy, onClick: () => { void controller.load(); }, children: (0, jsx_runtime_1.jsx)(ReloadIcon, {}) }), updateHasUnsavedChanges ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { className: "dvt-save-dock-text", children: t('unsavedChanges') }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "outline", disabled: busy, onClick: discard, children: t('discardChanges') }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "primary", disabled: !canSave || busy, onClick: save, children: state.action === 'save' ? t('saving') : t('save') })] })) : ((0, jsx_runtime_1.jsx)("span", { className: "dvt-save-dock-text", children: t('noUnsavedChanges') }))] }), (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert notice", children: t('externalNotice') }), !snapshot.writable ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert warning", children: t('readOnly') }) : null, draftError === undefined ? null : (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert error", children: draftError }), state.error === undefined ? null : (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert error", children: state.error }), state.message === 'saved' ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert success", children: t('saved') }) : null, state.message === 'restarting' && state.restart !== undefined ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert success", children: t('restarting', { version: state.restart.toVersion }) }) : null, state.message === 'manual-restart-required' && state.restart !== undefined ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert success", children: t('manualRestartRequired', { version: state.restart.toVersion }) }) : null, snapshot.runtime.lastError === undefined ? null : (0, jsx_runtime_1.jsxs)("div", { className: "dvt-alert error", children: [(0, jsx_runtime_1.jsx)("strong", { children: runtimeErrorTitle }), (0, jsx_runtime_1.jsx)("span", { children: snapshot.runtime.lastError })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel dvt-essential", children: [(0, jsx_runtime_1.jsx)("div", { className: "dvt-panel-title", children: (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { children: t('providers') }), (0, jsx_runtime_1.jsx)("p", { children: t('providerHint') })] }) }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-provider-select", children: [(0, jsx_runtime_1.jsx)("select", { "aria-label": t('providers'), disabled: !snapshot.writable || busy, value: selectedIndex, onChange: (event) => { setSelectedIndex(Number(event.target.value)); }, children: draft.providers.map((provider, index) => ((0, jsx_runtime_1.jsxs)("option", { value: index, children: [index + 1, ". ", provider.name.trim() || provider.model.trim() || t('provider')] }, index))) }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "outline", disabled: !snapshot.writable || busy || draft.providers.length >= 32, onClick: addProvider, children: t('addProvider') }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "outline", disabled: !snapshot.writable || busy || draft.providers.length <= 1, onClick: () => { removeProvider(selectedIndex); }, children: t('removeProvider') })] }), selectedProvider === undefined ? null : ((0, jsx_runtime_1.jsxs)("div", { className: "dvt-provider-card", children: [(0, jsx_runtime_1.jsxs)("div", { className: "dvt-provider-head", children: [(0, jsx_runtime_1.jsxs)("label", { className: "dvt-check", children: [(0, jsx_runtime_1.jsx)("input", { type: "checkbox", checked: selectedProvider.enabled, disabled: !snapshot.writable || busy, onChange: (event) => { updateProvider(selectedIndex, 'enabled', event.target.checked); } }), (0, jsx_runtime_1.jsx)("span", { children: t('enabled') })] }), (0, jsx_runtime_1.jsx)("span", { className: "dvt-provider-priority", children: t('priority', { index: selectedIndex + 1 }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-form-grid", children: [(0, jsx_runtime_1.jsx)(Field, { label: t('name'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('name'), value: selectedProvider.name, onChange: (event) => { updateProvider(selectedIndex, 'name', event.target.value); }, placeholder: selectedProvider.model || t('model') }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('model'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('model'), disabled: !snapshot.writable || busy, value: selectedProvider.model, onChange: (event) => { updateProvider(selectedIndex, 'model', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('baseUrl'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('baseUrl'), disabled: !snapshot.writable || busy, value: selectedProvider.baseUrl, onChange: (event) => { updateProvider(selectedIndex, 'baseUrl', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('protocol'), children: (0, jsx_runtime_1.jsxs)("select", { "aria-label": t('protocol'), disabled: !snapshot.writable || busy, value: selectedProvider.protocol, onChange: (event) => { updateProvider(selectedIndex, 'protocol', event.target.value); }, children: [(0, jsx_runtime_1.jsx)("option", { value: "openai", children: "OpenAI Chat Completions" }), (0, jsx_runtime_1.jsx)("option", { value: "anthropic", children: "Anthropic Messages" })] }) }), selectedProvider.protocol === 'anthropic' ? (0, jsx_runtime_1.jsx)(Field, { label: t('anthropicThinking'), hint: t('anthropicThinkingHint'), children: (0, jsx_runtime_1.jsxs)("select", { "aria-label": t('anthropicThinking'), value: selectedProvider.anthropicThinking, onChange: (event) => { updateProvider(selectedIndex, 'anthropicThinking', event.target.value); }, children: [(0, jsx_runtime_1.jsx)("option", { value: "omit", children: "omit (widest compatibility)" }), (0, jsx_runtime_1.jsx)("option", { value: "disabled", children: "disabled (model support required)" }), (0, jsx_runtime_1.jsx)("option", { value: "adaptive", children: "adaptive (model support required)" })] }) }) : null, (0, jsx_runtime_1.jsx)(Field, { label: t('userAgent'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('userAgent'), value: selectedProvider.userAgent, onChange: (event) => { updateProvider(selectedIndex, 'userAgent', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('stream'), hint: t('streamHint'), children: (0, jsx_runtime_1.jsxs)("select", { "aria-label": t('stream'), disabled: !snapshot.writable || busy, value: selectedProvider.stream ? 'enabled' : 'disabled', onChange: (event) => { updateProvider(selectedIndex, 'stream', event.target.value === 'enabled'); }, children: [(0, jsx_runtime_1.jsx)("option", { value: "disabled", children: t('disabled') }), (0, jsx_runtime_1.jsx)("option", { value: "enabled", children: t('enabled') })] }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('uploadViaUrl'), hint: t('uploadViaUrlHint'), children: (0, jsx_runtime_1.jsxs)("select", { "aria-label": t('uploadViaUrl'), disabled: !snapshot.writable || busy, value: selectedProvider.uploadViaUrl ? 'enabled' : 'disabled', onChange: (event) => { updateProvider(selectedIndex, 'uploadViaUrl', event.target.value === 'enabled'); }, children: [(0, jsx_runtime_1.jsx)("option", { value: "disabled", children: t('disabled') }), (0, jsx_runtime_1.jsx)("option", { value: "enabled", children: t('enabled') })] }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('t1'), hint: t('t1Hint'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('t1'), inputMode: "numeric", value: selectedProvider.t1Seconds, onChange: (event) => { updateProvider(selectedIndex, 't1Seconds', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('t2'), hint: t('t2Hint'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('t2'), inputMode: "numeric", value: selectedProvider.t2Seconds, onChange: (event) => { updateProvider(selectedIndex, 't2Seconds', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('maxBytes'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('maxBytes'), inputMode: "numeric", value: selectedProvider.maxImageBytes, onChange: (event) => { updateProvider(selectedIndex, 'maxImageBytes', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('maxPixels'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('maxPixels'), inputMode: "numeric", value: selectedProvider.maxImagePixels, onChange: (event) => { updateProvider(selectedIndex, 'maxImagePixels', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('concurrency'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('concurrency'), inputMode: "numeric", value: selectedProvider.concurrency, onChange: (event) => { updateProvider(selectedIndex, 'concurrency', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('attempts'), hint: t('attemptsHint'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('attempts'), inputMode: "numeric", value: selectedProvider.attempts, onChange: (event) => { updateProvider(selectedIndex, 'attempts', event.target.value); } }) })] }), (0, jsx_runtime_1.jsx)(Field, { label: t('apiKey'), hint: selectedBuiltInFree ? t('apiKeyBuiltInFree') : selectedKeyLocked ? t('apiKeyLocked') : t('apiKeyHint'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('apiKey'), type: "password", autoComplete: "new-password", disabled: busy || selectedKeyLocked || selectedBuiltInFree, placeholder: selectedCredential?.configured ? t('apiKeyPlaceholderConfigured') : t('apiKeyPlaceholderMissing'), value: selectedKey, onChange: (event) => { setKeys(current => ({ ...current, [selectedProvider.credential]: event.target.value })); setDraftError(undefined); } }) }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-provider-tests", children: [(0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "outline", disabled: busy || !snapshot.runtime.ready, onClick: () => { void controller.runHealth('connection', selectedIndex); }, children: state.action === 'connection' ? t('testing') : t('testConnection') }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "primary", disabled: busy || !snapshot.runtime.ready, onClick: () => { void controller.runHealth('model', selectedIndex); }, children: state.action === 'model' ? t('testingModel') : t('testModel') })] }), providerHealth === undefined ? null : (0, jsx_runtime_1.jsx)("div", { className: "dvt-provider-test-result", children: ['service', 'model'].map(name => {
1505
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "dvt-settings", children: [(0, jsx_runtime_1.jsxs)("div", { className: "dvt-save-dock", "data-dirty": updateHasUnsavedChanges ? 'true' : undefined, children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "dvt-save-dock-reload", "aria-label": t('reload'), title: t('reload'), disabled: busy, onClick: () => { void controller.load(); }, children: (0, jsx_runtime_1.jsx)(ReloadIcon, {}) }), updateHasUnsavedChanges ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { className: "dvt-save-dock-text", children: t('unsavedChanges') }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "outline", disabled: busy, onClick: discard, children: t('discardChanges') }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "primary", disabled: !canSave || busy, onClick: save, children: state.action === 'save' ? t('saving') : t('save') })] })) : ((0, jsx_runtime_1.jsx)("span", { className: "dvt-save-dock-text", children: t('noUnsavedChanges') }))] }), (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert notice", children: t('externalNotice') }), !snapshot.writable ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert warning", children: t('readOnly') }) : null, draftError === undefined ? null : (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert error", children: draftError }), state.error === undefined ? null : (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert error", children: state.error }), state.message === 'saved' ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert success", children: t('saved') }) : null, state.message === 'restarting' && state.restart !== undefined ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert success", children: t('restarting', { version: state.restart.toVersion }) }) : null, state.message === 'manual-restart-required' && state.restart !== undefined ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert success", children: t('manualRestartRequired', { version: state.restart.toVersion }) }) : null, snapshot.runtime.lastError === undefined ? null : (0, jsx_runtime_1.jsxs)("div", { className: "dvt-alert error", children: [(0, jsx_runtime_1.jsx)("strong", { children: runtimeErrorTitle }), (0, jsx_runtime_1.jsx)("span", { children: snapshot.runtime.lastError })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel dvt-essential", children: [(0, jsx_runtime_1.jsx)("div", { className: "dvt-panel-title", children: (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { children: t('providers') }), (0, jsx_runtime_1.jsx)("p", { children: t('providerHint') })] }) }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-provider-select", children: [(0, jsx_runtime_1.jsx)("select", { "aria-label": t('providers'), disabled: !snapshot.writable || busy, value: selectedIndex, onChange: (event) => { setSelectedIndex(Number(event.target.value)); }, children: draft.providers.map((provider, index) => ((0, jsx_runtime_1.jsxs)("option", { value: index, children: [index + 1, ". ", provider.name.trim() || provider.model.trim() || t('provider')] }, index))) }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "outline", disabled: !snapshot.writable || busy || draft.providers.length >= 32, onClick: addProvider, children: t('addProvider') }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "outline", disabled: !snapshot.writable || busy || draft.providers.length <= 1, onClick: () => { removeProvider(selectedIndex); }, children: t('removeProvider') })] }), selectedProvider === undefined ? null : ((0, jsx_runtime_1.jsxs)("div", { className: "dvt-provider-card", children: [(0, jsx_runtime_1.jsxs)("div", { className: "dvt-provider-head", children: [(0, jsx_runtime_1.jsxs)("label", { className: "dvt-check", children: [(0, jsx_runtime_1.jsx)("input", { type: "checkbox", checked: selectedProvider.enabled, disabled: !snapshot.writable || busy, onChange: (event) => { updateProvider(selectedIndex, 'enabled', event.target.checked); } }), (0, jsx_runtime_1.jsx)("span", { children: t('enabled') })] }), (0, jsx_runtime_1.jsx)("span", { className: "dvt-provider-priority", children: t('priority', { index: selectedIndex + 1 }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-form-grid", children: [(0, jsx_runtime_1.jsx)(Field, { label: t('name'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('name'), value: selectedProvider.name, onChange: (event) => { updateProvider(selectedIndex, 'name', event.target.value); }, placeholder: selectedProvider.model || t('model') }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('model'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('model'), disabled: !snapshot.writable || busy, value: selectedProvider.model, onChange: (event) => { updateProvider(selectedIndex, 'model', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('baseUrl'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('baseUrl'), disabled: !snapshot.writable || busy, value: selectedProvider.baseUrl, onChange: (event) => { updateProvider(selectedIndex, 'baseUrl', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('protocol'), children: (0, jsx_runtime_1.jsxs)("select", { "aria-label": t('protocol'), disabled: !snapshot.writable || busy, value: selectedProvider.protocol, onChange: (event) => { updateProvider(selectedIndex, 'protocol', event.target.value); }, children: [(0, jsx_runtime_1.jsx)("option", { value: "openai", children: "OpenAI Chat Completions" }), (0, jsx_runtime_1.jsx)("option", { value: "anthropic", children: "Anthropic Messages" })] }) }), selectedProvider.protocol === 'anthropic' ? (0, jsx_runtime_1.jsx)(Field, { label: t('anthropicThinking'), hint: t('anthropicThinkingHint'), children: (0, jsx_runtime_1.jsxs)("select", { "aria-label": t('anthropicThinking'), value: selectedProvider.anthropicThinking, onChange: (event) => { updateProvider(selectedIndex, 'anthropicThinking', event.target.value); }, children: [(0, jsx_runtime_1.jsx)("option", { value: "omit", children: "omit (widest compatibility)" }), (0, jsx_runtime_1.jsx)("option", { value: "disabled", children: "disabled (model support required)" }), (0, jsx_runtime_1.jsx)("option", { value: "adaptive", children: "adaptive (model support required)" })] }) }) : null, (0, jsx_runtime_1.jsx)(Field, { label: t('userAgent'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('userAgent'), value: selectedProvider.userAgent, onChange: (event) => { updateProvider(selectedIndex, 'userAgent', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('stream'), hint: t('streamHint'), children: (0, jsx_runtime_1.jsxs)("select", { "aria-label": t('stream'), disabled: !snapshot.writable || busy, value: selectedProvider.stream ? 'enabled' : 'disabled', onChange: (event) => { updateProvider(selectedIndex, 'stream', event.target.value === 'enabled'); }, children: [(0, jsx_runtime_1.jsx)("option", { value: "disabled", children: t('disabled') }), (0, jsx_runtime_1.jsx)("option", { value: "enabled", children: t('enabled') })] }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('uploadViaUrl'), hint: t('uploadViaUrlHint'), children: (0, jsx_runtime_1.jsxs)("select", { "aria-label": t('uploadViaUrl'), disabled: !snapshot.writable || busy, value: selectedProvider.uploadViaUrl ? 'enabled' : 'disabled', onChange: (event) => { updateProvider(selectedIndex, 'uploadViaUrl', event.target.value === 'enabled'); }, children: [(0, jsx_runtime_1.jsx)("option", { value: "disabled", children: t('disabled') }), (0, jsx_runtime_1.jsx)("option", { value: "enabled", children: t('enabled') })] }) }), selectedProvider.protocol === 'openai' ? (0, jsx_runtime_1.jsx)(Field, { label: t('videoSupport'), hint: t('videoSupportHint'), children: (0, jsx_runtime_1.jsxs)("select", { "aria-label": t('videoSupport'), disabled: !snapshot.writable || busy, value: selectedProvider.videoSupport ? 'enabled' : 'disabled', onChange: (event) => { updateProvider(selectedIndex, 'videoSupport', event.target.value === 'enabled'); }, children: [(0, jsx_runtime_1.jsx)("option", { value: "disabled", children: t('disabled') }), (0, jsx_runtime_1.jsx)("option", { value: "enabled", children: t('enabled') })] }) }) : null, (0, jsx_runtime_1.jsx)(Field, { label: t('t1'), hint: t('t1Hint'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('t1'), inputMode: "numeric", value: selectedProvider.t1Seconds, onChange: (event) => { updateProvider(selectedIndex, 't1Seconds', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('t2'), hint: t('t2Hint'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('t2'), inputMode: "numeric", value: selectedProvider.t2Seconds, onChange: (event) => { updateProvider(selectedIndex, 't2Seconds', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('maxBytes'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('maxBytes'), inputMode: "numeric", value: selectedProvider.maxImageBytes, onChange: (event) => { updateProvider(selectedIndex, 'maxImageBytes', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('maxPixels'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('maxPixels'), inputMode: "numeric", value: selectedProvider.maxImagePixels, onChange: (event) => { updateProvider(selectedIndex, 'maxImagePixels', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('concurrency'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('concurrency'), inputMode: "numeric", value: selectedProvider.concurrency, onChange: (event) => { updateProvider(selectedIndex, 'concurrency', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('attempts'), hint: t('attemptsHint'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('attempts'), inputMode: "numeric", value: selectedProvider.attempts, onChange: (event) => { updateProvider(selectedIndex, 'attempts', event.target.value); } }) })] }), (0, jsx_runtime_1.jsx)(Field, { label: t('apiKey'), hint: selectedBuiltInFree ? t('apiKeyBuiltInFree') : selectedKeyLocked ? t('apiKeyLocked') : t('apiKeyHint'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('apiKey'), type: "password", autoComplete: "new-password", disabled: busy || selectedKeyLocked || selectedBuiltInFree, placeholder: selectedCredential?.configured ? t('apiKeyPlaceholderConfigured') : t('apiKeyPlaceholderMissing'), value: selectedKey, onChange: (event) => { setKeys(current => ({ ...current, [selectedProvider.credential]: event.target.value })); setDraftError(undefined); } }) }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-provider-tests", children: [(0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "outline", disabled: busy || !snapshot.runtime.ready, onClick: () => { void controller.runHealth('connection', selectedIndex); }, children: state.action === 'connection' ? t('testing') : t('testConnection') }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "primary", disabled: busy || !snapshot.runtime.ready, onClick: () => { void controller.runHealth('model', selectedIndex); }, children: state.action === 'model' ? t('testingModel') : t('testModel') }), selectedProvider.protocol === 'openai' ? (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "primary", disabled: busy || !snapshot.runtime.ready, onClick: () => { void controller.runVideoTest(selectedIndex); }, children: state.action === 'test-video' ? t('testingVideo') : t('testVideo') }) : null] }), providerHealth === undefined ? null : (0, jsx_runtime_1.jsx)("div", { className: "dvt-provider-test-result", children: ['service', 'model'].map(name => {
1456
1506
  const check = providerHealth.checks[name];
1457
1507
  if (check === undefined)
1458
1508
  return null;
1459
1509
  return (0, jsx_runtime_1.jsxs)("div", { "data-status": check.status, children: [(0, jsx_runtime_1.jsx)("span", { children: t(HEALTH_NAME_KEYS[name] ?? 'health') }), (0, jsx_runtime_1.jsx)("strong", { children: t(HEALTH_STATUS_KEYS[check.status]) }), (0, jsx_runtime_1.jsx)("p", { children: healthDetail(name, check.detail, t) })] }, name);
1460
- }) })] }))] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsx)("div", { className: "dvt-panel-title", children: (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { children: t('prioritySort') }), (0, jsx_runtime_1.jsx)("p", { children: t('prioritySortHint') })] }) }), (0, jsx_runtime_1.jsx)("div", { className: "dvt-sorter", children: draft.providers.map((provider, index) => ((0, jsx_runtime_1.jsxs)("div", { className: "dvt-sorter-row", children: [(0, jsx_runtime_1.jsx)("span", { className: "dvt-sorter-index", children: index + 1 }), (0, jsx_runtime_1.jsx)("span", { className: "dvt-sorter-name", children: provider.name.trim() || provider.model.trim() || t('provider') }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-provider-actions", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": t('moveUp'), title: t('moveUp'), disabled: !snapshot.writable || busy || index === 0, onClick: () => { moveProvider(index, -1); }, children: "\u2191" }), (0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": t('moveDown'), title: t('moveDown'), disabled: !snapshot.writable || busy || index === draft.providers.length - 1, onClick: () => { moveProvider(index, 1); }, children: "\u2193" })] })] }, index))) })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel dvt-update-panel", children: [(0, jsx_runtime_1.jsxs)("div", { className: "dvt-panel-title", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { children: t('runtimeCheck') }), (0, jsx_runtime_1.jsx)("p", { children: t('runtimeCheckHint') })] }), (0, jsx_runtime_1.jsx)("div", { className: "dvt-actions", children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "outline", disabled: busy || !snapshot.runtime.ready, onClick: () => { void controller.runHealth('health'); }, children: state.action === 'health' ? t('testing') : t('runHealth') }) })] }), state.health === undefined ? (0, jsx_runtime_1.jsx)("p", { className: "dvt-muted", children: t('notTested') }) : (0, jsx_runtime_1.jsx)("div", { className: "dvt-health-grid", children: ['python', 'dependencies', 'chrome'].map(name => {
1510
+ }) }), state.videoTest === undefined ? null : (0, jsx_runtime_1.jsx)("div", { className: "dvt-provider-test-result", children: (0, jsx_runtime_1.jsxs)("div", { "data-status": "ok", children: [(0, jsx_runtime_1.jsx)("span", { children: t('videoSupport') }), (0, jsx_runtime_1.jsx)("strong", { children: t('statusOk') }), (0, jsx_runtime_1.jsx)("p", { children: state.videoTest.detail })] }) })] }))] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsx)("div", { className: "dvt-panel-title", children: (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { children: t('prioritySort') }), (0, jsx_runtime_1.jsx)("p", { children: t('prioritySortHint') })] }) }), (0, jsx_runtime_1.jsx)("div", { className: "dvt-sorter", children: draft.providers.map((provider, index) => ((0, jsx_runtime_1.jsxs)("div", { className: "dvt-sorter-row", children: [(0, jsx_runtime_1.jsx)("span", { className: "dvt-sorter-index", children: index + 1 }), (0, jsx_runtime_1.jsx)("span", { className: "dvt-sorter-name", children: provider.name.trim() || provider.model.trim() || t('provider') }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-provider-actions", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": t('moveUp'), title: t('moveUp'), disabled: !snapshot.writable || busy || index === 0, onClick: () => { moveProvider(index, -1); }, children: "\u2191" }), (0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": t('moveDown'), title: t('moveDown'), disabled: !snapshot.writable || busy || index === draft.providers.length - 1, onClick: () => { moveProvider(index, 1); }, children: "\u2193" })] })] }, index))) })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel dvt-update-panel", children: [(0, jsx_runtime_1.jsxs)("div", { className: "dvt-panel-title", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { children: t('runtimeCheck') }), (0, jsx_runtime_1.jsx)("p", { children: t('runtimeCheckHint') })] }), (0, jsx_runtime_1.jsx)("div", { className: "dvt-actions", children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "outline", disabled: busy || !snapshot.runtime.ready, onClick: () => { void controller.runHealth('health'); }, children: state.action === 'health' ? t('testing') : t('runHealth') }) })] }), state.health === undefined ? (0, jsx_runtime_1.jsx)("p", { className: "dvt-muted", children: t('notTested') }) : (0, jsx_runtime_1.jsx)("div", { className: "dvt-health-grid", children: ['python', 'dependencies', 'chrome'].map(name => {
1461
1511
  const check = state.health?.checks[name];
1462
1512
  if (check === undefined)
1463
1513
  return null;
1464
1514
  return (0, jsx_runtime_1.jsxs)("div", { "data-status": check.status, children: [(0, jsx_runtime_1.jsx)("span", { children: t(HEALTH_NAME_KEYS[name] ?? 'health') }), (0, jsx_runtime_1.jsx)("strong", { children: t(HEALTH_STATUS_KEYS[check.status]) }), (0, jsx_runtime_1.jsx)("p", { children: healthDetail(name, check.detail, t) })] }, name);
1465
- }) }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-panel-title", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { children: t('updates') }), (0, jsx_runtime_1.jsx)("p", { children: t('updatesHint') })] }), (0, jsx_runtime_1.jsx)("span", { className: `dvt-badge ${pluginUpdate?.updateAvailable ? 'warning' : pluginUpdate !== undefined && pluginUpdate.supported ? 'ok' : ''}`, children: pluginUpdate?.updateAvailable ? t('updateAvailable') : pluginUpdate !== undefined && pluginUpdate.supported ? t('upToDate') : t('pluginVersion') })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-update-grid", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { children: t('updateInstalled') }), (0, jsx_runtime_1.jsx)("strong", { children: snapshot.release.pluginVersion })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { children: t('updateLatest') }), (0, jsx_runtime_1.jsx)("strong", { children: latestVersion ?? '—' })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { children: t('updateProfile') }), (0, jsx_runtime_1.jsx)("strong", { children: updateCapability.profile ?? '—' })] })] }), !updateCapability.supported ? (0, jsx_runtime_1.jsxs)("div", { className: "dvt-alert warning", children: [(0, jsx_runtime_1.jsx)("strong", { children: t('updateUnsupported') }), (0, jsx_runtime_1.jsx)("span", { children: updateReason })] }) : null, updateCapability.supported && updateHasUnsavedChanges ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert warning", children: t('updateSaveFirst') }) : null, pluginUpdate?.supported && pluginUpdate.updateAvailable && latestVersion !== undefined ? (0, jsx_runtime_1.jsx)("p", { className: "dvt-muted", children: t('updateAvailableDetail', { version: latestVersion }) }) : null, pluginUpdate?.supported && !pluginUpdate.updateAvailable && latestVersion !== undefined ? (0, jsx_runtime_1.jsx)("p", { className: "dvt-muted", children: t('upToDateDetail', { version: latestVersion }) }) : null, (0, jsx_runtime_1.jsx)("p", { className: "dvt-muted", children: t('manualUpdateHint') }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-manual-update", children: [(0, jsx_runtime_1.jsx)("code", { children: manualUpdateCommand }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "outline", onClick: copyManualUpdate, children: copiedCommand ? t('copied') : t('copy') })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-actions", children: [(0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { variant: "outline", disabled: busy || !updateCheckSupported || state.restart !== undefined, onClick: () => { void controller.checkUpdate(); }, children: state.action === 'check-update' ? t('checkingUpdate') : t('checkUpdate') }), pluginUpdate?.supported && pluginUpdate.updateAvailable && latestVersion !== undefined ? (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { variant: "primary", disabled: busy || state.restart !== undefined || updateHasUnsavedChanges, onClick: applyUpdate, children: state.action === 'apply-update' ? t('updatingPlugin') : t('updateNow') }) : null] })] }), (0, jsx_runtime_1.jsxs)("details", { className: "dvt-advanced", children: [(0, jsx_runtime_1.jsxs)("summary", { children: [(0, jsx_runtime_1.jsxs)("span", { children: [(0, jsx_runtime_1.jsx)("strong", { children: t('advanced') }), (0, jsx_runtime_1.jsx)("small", { children: t('advancedHint') })] }), (0, jsx_runtime_1.jsx)("span", { className: "dvt-details-chevron", "aria-hidden": "true", children: "\u2304" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-advanced-body", children: [(0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsx)("div", { className: "dvt-panel-title", children: (0, jsx_runtime_1.jsx)("h3", { children: t('language') }) }), (0, jsx_runtime_1.jsx)("div", { className: "dvt-form-grid", children: (0, jsx_runtime_1.jsx)(Field, { label: t('language'), children: (0, jsx_runtime_1.jsxs)("select", { value: draft.language, onChange: (event) => { update('language', event.target.value); }, children: [(0, jsx_runtime_1.jsx)("option", { value: "zh", children: "\u4E2D\u6587" }), (0, jsx_runtime_1.jsx)("option", { value: "en", children: "English" })] }) }) })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsx)("div", { className: "dvt-panel-title", children: (0, jsx_runtime_1.jsx)("h3", { children: t('limits') }) }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-form-grid", children: [(0, jsx_runtime_1.jsx)(Field, { label: t('hardTimeout'), hint: t('hardTimeoutHint'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('hardTimeout'), inputMode: "numeric", value: draft.hardTimeoutSeconds, onChange: (event) => { update('hardTimeoutSeconds', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('sessionConcurrency'), hint: t('sessionConcurrencyHint'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('sessionConcurrency'), inputMode: "numeric", value: draft.sessionMaxConcurrency, onChange: (event) => { update('sessionMaxConcurrency', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('minAvailable'), hint: t('minAvailableHint'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('minAvailable'), inputMode: "numeric", value: draft.minAvailableSeconds, onChange: (event) => { update('minAvailableSeconds', event.target.value); } }) })] })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsx)("div", { className: "dvt-panel-title", children: (0, jsx_runtime_1.jsx)("h3", { children: t('imageInput') }) }), (0, jsx_runtime_1.jsxs)("label", { className: "dvt-check", children: [(0, jsx_runtime_1.jsx)("input", { type: "checkbox", checked: draft.hiddenVariants, disabled: !snapshot.writable || busy, onChange: (event) => { update('hiddenVariants', event.target.checked); } }), (0, jsx_runtime_1.jsx)("span", { children: t('hiddenVariantsLabel') }), (0, jsx_runtime_1.jsx)("small", { children: t('hiddenVariantsHint') })] })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsx)("div", { className: "dvt-panel-title", children: (0, jsx_runtime_1.jsx)("h3", { children: t('storage') }) }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-form-grid", children: [(0, jsx_runtime_1.jsx)(Field, { label: t('storageDir'), hint: t('storageDirHint'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('storageDir'), placeholder: "/tmp/dsh-vision-toolkit", value: draft.storageDir, onChange: (event) => { update('storageDir', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('allowedDirs'), hint: t('allowedDirsHint'), children: (0, jsx_runtime_1.jsx)("textarea", { rows: 3, value: draft.allowedDirs, onChange: (event) => { update('allowedDirs', event.target.value); } }) })] })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsxs)("div", { className: "dvt-panel-title", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { children: t('objectStorage') }), (0, jsx_runtime_1.jsx)("p", { children: t('objectStorageHint') })] }), (0, jsx_runtime_1.jsx)("div", { className: "dvt-actions", children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "outline", disabled: busy || !snapshot.runtime.ready, onClick: () => { void controller.testStorage(); }, children: state.action === 'test-storage' ? t('testingStorage') : t('testStorage') }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-form-grid", children: [(0, jsx_runtime_1.jsx)(Field, { label: t('storageEndpoint'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('storageEndpoint'), disabled: !snapshot.writable || busy, value: draft.storageEndpoint, onChange: (event) => { update('storageEndpoint', event.target.value); }, placeholder: "https://<account>.r2.cloudflarestorage.com" }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('storageBucket'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('storageBucket'), disabled: !snapshot.writable || busy, value: draft.storageBucket, onChange: (event) => { update('storageBucket', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('storageAccessKeyId'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('storageAccessKeyId'), type: "password", autoComplete: "new-password", disabled: busy || !snapshot.writable, placeholder: objectStorageCredential.configured ? t('apiKeyPlaceholderConfigured') : t('apiKeyPlaceholderMissing'), value: storageKeys.accessKeyId, onChange: (event) => { setStorageKeys(current => ({ ...current, accessKeyId: event.target.value })); setDraftError(undefined); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('storageSecretAccessKey'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('storageSecretAccessKey'), type: "password", autoComplete: "new-password", disabled: busy || !snapshot.writable, placeholder: objectStorageCredential.configured ? t('apiKeyPlaceholderConfigured') : t('apiKeyPlaceholderMissing'), value: storageKeys.secretAccessKey, onChange: (event) => { setStorageKeys(current => ({ ...current, secretAccessKey: event.target.value })); setDraftError(undefined); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('storagePublicBase'), hint: t('storagePublicBaseHint'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('storagePublicBase'), disabled: !snapshot.writable || busy, value: draft.storagePublicBase, onChange: (event) => { update('storagePublicBase', event.target.value); }, placeholder: "https://cdn.example.com" }) })] }), state.storageTest === undefined ? null : (0, jsx_runtime_1.jsx)("div", { className: "dvt-provider-test-result", children: (0, jsx_runtime_1.jsxs)("div", { "data-status": "ok", children: [(0, jsx_runtime_1.jsx)("span", { children: t('objectStorage') }), (0, jsx_runtime_1.jsx)("strong", { children: t('statusOk') }), (0, jsx_runtime_1.jsx)("p", { children: state.storageTest.detail })] }) })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsxs)("div", { className: "dvt-panel-title", children: [(0, jsx_runtime_1.jsx)("h3", { children: t('runtime') }), (0, jsx_runtime_1.jsx)("span", { className: `dvt-badge ${snapshot.runtime.ready ? 'ok' : 'error'}`, children: snapshot.runtime.ready ? snapshot.runtime.upstream?.source === 'managed' ? t('runtimeManaged') : snapshot.runtime.upstream?.source === 'external' ? t('runtimeExternal') : t('runtimeReady') : t('runtimeUnavailable') })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-form-grid", children: [(0, jsx_runtime_1.jsx)(Field, { label: t('runtimeMode'), children: (0, jsx_runtime_1.jsxs)("select", { value: draft.runtimeMode, onChange: (event) => { update('runtimeMode', event.target.value); }, children: [(0, jsx_runtime_1.jsx)("option", { value: "managed", children: t('runtimeManaged') }), (0, jsx_runtime_1.jsx)("option", { value: "external", children: t('runtimeExternal') })] }) }), draft.runtimeMode === 'external' ? (0, jsx_runtime_1.jsx)(Field, { label: t('toolkitPath'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { value: draft.toolkitPath, onChange: (event) => { update('toolkitPath', event.target.value); } }) }) : null, (0, jsx_runtime_1.jsx)(Field, { label: t('python'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { placeholder: "python3", value: draft.python, onChange: (event) => { update('python', event.target.value); } }) })] }), snapshot.runtime.upstream === undefined ? null : (0, jsx_runtime_1.jsxs)("div", { className: "dvt-runtime-facts", children: [(0, jsx_runtime_1.jsx)("code", { children: snapshot.runtime.upstream.path }), (0, jsx_runtime_1.jsxs)("code", { children: [snapshot.runtime.upstream.python, " \u00B7 ", snapshot.runtime.upstream.pythonVersion] }), (0, jsx_runtime_1.jsx)("code", { children: snapshot.runtime.upstream.runtimeHome })] })] })] })] }), (0, jsx_runtime_1.jsxs)("footer", { className: "dvt-settings-footer", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { className: "dvt-kicker", children: t('pluginKind') }), (0, jsx_runtime_1.jsx)("h2", { children: t('settingsTitle') }), (0, jsx_runtime_1.jsx)("p", { children: t('settingsIntro') })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-release", children: [(0, jsx_runtime_1.jsxs)("span", { children: [t('pluginVersion'), " ", (0, jsx_runtime_1.jsx)("strong", { children: snapshot.release.pluginVersion })] }), (0, jsx_runtime_1.jsxs)("span", { children: [t('upstreamVersion'), " ", (0, jsx_runtime_1.jsx)("strong", { children: snapshot.release.upstreamVersion })] }), (0, jsx_runtime_1.jsxs)("span", { children: [t('activeGeneration'), " ", (0, jsx_runtime_1.jsx)("strong", { children: t('activeGenerationValue', { generation: snapshot.runtime.generation }) })] })] })] })] }));
1515
+ }) }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-panel-title", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { children: t('updates') }), (0, jsx_runtime_1.jsx)("p", { children: t('updatesHint') })] }), (0, jsx_runtime_1.jsx)("span", { className: `dvt-badge ${pluginUpdate?.updateAvailable ? 'warning' : pluginUpdate !== undefined && pluginUpdate.supported ? 'ok' : ''}`, children: pluginUpdate?.updateAvailable ? t('updateAvailable') : pluginUpdate !== undefined && pluginUpdate.supported ? t('upToDate') : t('pluginVersion') })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-update-grid", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { children: t('updateInstalled') }), (0, jsx_runtime_1.jsx)("strong", { children: snapshot.release.pluginVersion })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { children: t('updateLatest') }), (0, jsx_runtime_1.jsx)("strong", { children: latestVersion ?? '—' })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { children: t('updateProfile') }), (0, jsx_runtime_1.jsx)("strong", { children: updateCapability.profile ?? '—' })] })] }), !updateCapability.supported ? (0, jsx_runtime_1.jsxs)("div", { className: "dvt-alert warning", children: [(0, jsx_runtime_1.jsx)("strong", { children: t('updateUnsupported') }), (0, jsx_runtime_1.jsx)("span", { children: updateReason })] }) : null, updateCapability.supported && updateHasUnsavedChanges ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert warning", children: t('updateSaveFirst') }) : null, pluginUpdate?.supported && pluginUpdate.updateAvailable && latestVersion !== undefined ? (0, jsx_runtime_1.jsx)("p", { className: "dvt-muted", children: t('updateAvailableDetail', { version: latestVersion }) }) : null, pluginUpdate?.supported && !pluginUpdate.updateAvailable && latestVersion !== undefined ? (0, jsx_runtime_1.jsx)("p", { className: "dvt-muted", children: t('upToDateDetail', { version: latestVersion }) }) : null, (0, jsx_runtime_1.jsx)("p", { className: "dvt-muted", children: t('manualUpdateHint') }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-manual-update", children: [(0, jsx_runtime_1.jsx)("code", { children: manualUpdateCommand }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "outline", onClick: copyManualUpdate, children: copiedCommand ? t('copied') : t('copy') })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-actions", children: [(0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { variant: "outline", disabled: busy || !updateCheckSupported || state.restart !== undefined, onClick: () => { void controller.checkUpdate(); }, children: state.action === 'check-update' ? t('checkingUpdate') : t('checkUpdate') }), pluginUpdate?.supported && pluginUpdate.updateAvailable && latestVersion !== undefined ? (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { variant: "primary", disabled: busy || state.restart !== undefined || updateHasUnsavedChanges, onClick: applyUpdate, children: state.action === 'apply-update' ? t('updatingPlugin') : t('updateNow') }) : null] })] }), (0, jsx_runtime_1.jsxs)("details", { className: "dvt-advanced", children: [(0, jsx_runtime_1.jsxs)("summary", { children: [(0, jsx_runtime_1.jsxs)("span", { children: [(0, jsx_runtime_1.jsx)("strong", { children: t('advanced') }), (0, jsx_runtime_1.jsx)("small", { children: t('advancedHint') })] }), (0, jsx_runtime_1.jsx)("span", { className: "dvt-details-chevron", "aria-hidden": "true", children: "\u2304" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-advanced-body", children: [(0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsx)("div", { className: "dvt-panel-title", children: (0, jsx_runtime_1.jsx)("h3", { children: t('language') }) }), (0, jsx_runtime_1.jsx)("div", { className: "dvt-form-grid", children: (0, jsx_runtime_1.jsx)(Field, { label: t('language'), children: (0, jsx_runtime_1.jsxs)("select", { value: draft.language, onChange: (event) => { update('language', event.target.value); }, children: [(0, jsx_runtime_1.jsx)("option", { value: "zh", children: "\u4E2D\u6587" }), (0, jsx_runtime_1.jsx)("option", { value: "en", children: "English" })] }) }) })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsx)("div", { className: "dvt-panel-title", children: (0, jsx_runtime_1.jsx)("h3", { children: t('limits') }) }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-form-grid", children: [(0, jsx_runtime_1.jsx)(Field, { label: t('hardTimeout'), hint: t('hardTimeoutHint'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('hardTimeout'), inputMode: "numeric", value: draft.hardTimeoutSeconds, onChange: (event) => { update('hardTimeoutSeconds', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('sessionConcurrency'), hint: t('sessionConcurrencyHint'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('sessionConcurrency'), inputMode: "numeric", value: draft.sessionMaxConcurrency, onChange: (event) => { update('sessionMaxConcurrency', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('minAvailable'), hint: t('minAvailableHint'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('minAvailable'), inputMode: "numeric", value: draft.minAvailableSeconds, onChange: (event) => { update('minAvailableSeconds', event.target.value); } }) })] })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsx)("div", { className: "dvt-panel-title", children: (0, jsx_runtime_1.jsx)("h3", { children: t('imageInput') }) }), (0, jsx_runtime_1.jsxs)("label", { className: "dvt-check", children: [(0, jsx_runtime_1.jsx)("input", { type: "checkbox", checked: draft.hiddenVariants, disabled: !snapshot.writable || busy, onChange: (event) => { update('hiddenVariants', event.target.checked); } }), (0, jsx_runtime_1.jsx)("span", { children: t('hiddenVariantsLabel') }), (0, jsx_runtime_1.jsx)("small", { children: t('hiddenVariantsHint') })] })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsx)("div", { className: "dvt-panel-title", children: (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { children: t('toolVisibility') }), (0, jsx_runtime_1.jsx)("p", { children: t('toolVisibilityHint') })] }) }), (0, jsx_runtime_1.jsxs)("label", { className: "dvt-check", children: [(0, jsx_runtime_1.jsx)("input", { type: "checkbox", checked: draft.toolVisibilityLocal, disabled: !snapshot.writable || busy, onChange: (event) => { update('toolVisibilityLocal', event.target.checked); } }), (0, jsx_runtime_1.jsx)("span", { children: t('toolVisibilityLocal') }), (0, jsx_runtime_1.jsx)("small", { children: t('toolVisibilityLocalHint') })] }), (0, jsx_runtime_1.jsxs)("label", { className: "dvt-check", children: [(0, jsx_runtime_1.jsx)("input", { type: "checkbox", checked: draft.toolVisibilityOnline, disabled: !snapshot.writable || busy, onChange: (event) => { update('toolVisibilityOnline', event.target.checked); } }), (0, jsx_runtime_1.jsx)("span", { children: t('toolVisibilityOnline') }), (0, jsx_runtime_1.jsx)("small", { children: t('toolVisibilityOnlineHint') })] }), (0, jsx_runtime_1.jsxs)("label", { className: "dvt-check", children: [(0, jsx_runtime_1.jsx)("input", { type: "checkbox", checked: draft.toolVisibilityVideo, disabled: !snapshot.writable || busy, onChange: (event) => { update('toolVisibilityVideo', event.target.checked); } }), (0, jsx_runtime_1.jsxs)("span", { children: [t('toolVisibilityVideo'), " ", (0, jsx_runtime_1.jsx)("em", { children: "\uFF08\u6D4B\u8BD5\uFF09" })] }), (0, jsx_runtime_1.jsx)("small", { children: t('toolVisibilityVideoHint') })] })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsx)("div", { className: "dvt-panel-title", children: (0, jsx_runtime_1.jsx)("h3", { children: t('storage') }) }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-form-grid", children: [(0, jsx_runtime_1.jsx)(Field, { label: t('storageDir'), hint: t('storageDirHint'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('storageDir'), placeholder: "/tmp/dsh-vision-toolkit", value: draft.storageDir, onChange: (event) => { update('storageDir', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('allowedDirs'), hint: t('allowedDirsHint'), children: (0, jsx_runtime_1.jsx)("textarea", { rows: 3, value: draft.allowedDirs, onChange: (event) => { update('allowedDirs', event.target.value); } }) })] })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsxs)("div", { className: "dvt-panel-title", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { children: t('objectStorage') }), (0, jsx_runtime_1.jsx)("p", { children: t('objectStorageHint') })] }), (0, jsx_runtime_1.jsx)("div", { className: "dvt-actions", children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "outline", disabled: busy || !snapshot.runtime.ready, onClick: () => { void controller.testStorage(); }, children: state.action === 'test-storage' ? t('testingStorage') : t('testStorage') }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-form-grid", children: [(0, jsx_runtime_1.jsx)(Field, { label: t('storageEndpoint'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('storageEndpoint'), disabled: !snapshot.writable || busy, value: draft.storageEndpoint, onChange: (event) => { update('storageEndpoint', event.target.value); }, placeholder: "https://<account>.r2.cloudflarestorage.com" }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('storageBucket'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('storageBucket'), disabled: !snapshot.writable || busy, value: draft.storageBucket, onChange: (event) => { update('storageBucket', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('storageAccessKeyId'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('storageAccessKeyId'), type: "password", autoComplete: "new-password", disabled: busy || !snapshot.writable, placeholder: objectStorageCredential.configured ? t('apiKeyPlaceholderConfigured') : t('apiKeyPlaceholderMissing'), value: storageKeys.accessKeyId, onChange: (event) => { setStorageKeys(current => ({ ...current, accessKeyId: event.target.value })); setDraftError(undefined); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('storageSecretAccessKey'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('storageSecretAccessKey'), type: "password", autoComplete: "new-password", disabled: busy || !snapshot.writable, placeholder: objectStorageCredential.configured ? t('apiKeyPlaceholderConfigured') : t('apiKeyPlaceholderMissing'), value: storageKeys.secretAccessKey, onChange: (event) => { setStorageKeys(current => ({ ...current, secretAccessKey: event.target.value })); setDraftError(undefined); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('storagePublicBase'), hint: t('storagePublicBaseHint'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('storagePublicBase'), disabled: !snapshot.writable || busy, value: draft.storagePublicBase, onChange: (event) => { update('storagePublicBase', event.target.value); }, placeholder: "https://cdn.example.com" }) })] }), state.storageTest === undefined ? null : (0, jsx_runtime_1.jsx)("div", { className: "dvt-provider-test-result", children: (0, jsx_runtime_1.jsxs)("div", { "data-status": "ok", children: [(0, jsx_runtime_1.jsx)("span", { children: t('objectStorage') }), (0, jsx_runtime_1.jsx)("strong", { children: t('statusOk') }), (0, jsx_runtime_1.jsx)("p", { children: state.storageTest.detail })] }) })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsxs)("div", { className: "dvt-panel-title", children: [(0, jsx_runtime_1.jsx)("h3", { children: t('runtime') }), (0, jsx_runtime_1.jsx)("span", { className: `dvt-badge ${snapshot.runtime.ready ? 'ok' : 'error'}`, children: snapshot.runtime.ready ? snapshot.runtime.upstream?.source === 'managed' ? t('runtimeManaged') : snapshot.runtime.upstream?.source === 'external' ? t('runtimeExternal') : t('runtimeReady') : t('runtimeUnavailable') })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-form-grid", children: [(0, jsx_runtime_1.jsx)(Field, { label: t('runtimeMode'), children: (0, jsx_runtime_1.jsxs)("select", { value: draft.runtimeMode, onChange: (event) => { update('runtimeMode', event.target.value); }, children: [(0, jsx_runtime_1.jsx)("option", { value: "managed", children: t('runtimeManaged') }), (0, jsx_runtime_1.jsx)("option", { value: "external", children: t('runtimeExternal') })] }) }), draft.runtimeMode === 'external' ? (0, jsx_runtime_1.jsx)(Field, { label: t('toolkitPath'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { value: draft.toolkitPath, onChange: (event) => { update('toolkitPath', event.target.value); } }) }) : null, (0, jsx_runtime_1.jsx)(Field, { label: t('python'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { placeholder: "python3", value: draft.python, onChange: (event) => { update('python', event.target.value); } }) })] }), snapshot.runtime.upstream === undefined ? null : (0, jsx_runtime_1.jsxs)("div", { className: "dvt-runtime-facts", children: [(0, jsx_runtime_1.jsx)("code", { children: snapshot.runtime.upstream.path }), (0, jsx_runtime_1.jsxs)("code", { children: [snapshot.runtime.upstream.python, " \u00B7 ", snapshot.runtime.upstream.pythonVersion] }), (0, jsx_runtime_1.jsx)("code", { children: snapshot.runtime.upstream.runtimeHome })] })] })] })] }), (0, jsx_runtime_1.jsxs)("footer", { className: "dvt-settings-footer", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { className: "dvt-kicker", children: t('pluginKind') }), (0, jsx_runtime_1.jsx)("h2", { children: t('settingsTitle') }), (0, jsx_runtime_1.jsx)("p", { children: t('settingsIntro') })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-release", children: [(0, jsx_runtime_1.jsxs)("span", { children: [t('pluginVersion'), " ", (0, jsx_runtime_1.jsx)("strong", { children: snapshot.release.pluginVersion })] }), (0, jsx_runtime_1.jsxs)("span", { children: [t('upstreamVersion'), " ", (0, jsx_runtime_1.jsx)("strong", { children: snapshot.release.upstreamVersion })] }), (0, jsx_runtime_1.jsxs)("span", { children: [t('activeGeneration'), " ", (0, jsx_runtime_1.jsx)("strong", { children: t('activeGenerationValue', { generation: snapshot.runtime.generation }) })] })] })] })] }));
1466
1516
  }
1467
1517
  const CSS = `
1468
1518
  .dvt-tool{margin:4px 0;border:1px solid var(--dsw-alias-border-l1);border-radius:12px;background:var(--dsw-alias-bg-layer-1);overflow:hidden;box-shadow:var(--dsw-shadow-lv1)}