openclacky 1.5.13 → 1.5.15

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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +96 -0
  3. data/lib/clacky/agent/chunk_index.rb +83 -0
  4. data/lib/clacky/agent/history_navigation.rb +239 -0
  5. data/lib/clacky/agent/session_serializer.rb +53 -98
  6. data/lib/clacky/agent.rb +477 -297
  7. data/lib/clacky/agent_config.rb +5 -3
  8. data/lib/clacky/billing/billing_store.rb +2 -2
  9. data/lib/clacky/billing/platform_billing.rb +7 -0
  10. data/lib/clacky/brand_config.rb +27 -19
  11. data/lib/clacky/cli.rb +92 -12
  12. data/lib/clacky/cli_guidance.rb +48 -0
  13. data/lib/clacky/client.rb +2 -2
  14. data/lib/clacky/default_extensions/ext-studio/agents/ext-developer/system_prompt.md +70 -134
  15. data/lib/clacky/default_extensions/ext-studio/api/handler.rb +4 -1
  16. data/lib/clacky/default_extensions/ext-studio/panels/studio/view.js +307 -98
  17. data/lib/clacky/default_extensions/ext-studio/skills/ext-develop/SKILL.md +179 -577
  18. data/lib/clacky/default_extensions/git/panels/git/view.js +34 -14
  19. data/lib/clacky/default_extensions/preview/ext.yml +20 -0
  20. data/lib/clacky/default_extensions/preview/panels/preview/view.js +475 -0
  21. data/lib/clacky/default_extensions/time_machine/panels/time_machine/view.js +1 -10
  22. data/lib/clacky/extension/api_extension.rb +18 -6
  23. data/lib/clacky/extension/verifier.rb +1 -1
  24. data/lib/clacky/json_ui_controller.rb +4 -2
  25. data/lib/clacky/media/openai_compat.rb +70 -30
  26. data/lib/clacky/message_format/bedrock.rb +6 -1
  27. data/lib/clacky/message_format/open_ai.rb +22 -5
  28. data/lib/clacky/message_format/open_ai_responses.rb +7 -3
  29. data/lib/clacky/plain_ui_controller.rb +1 -1
  30. data/lib/clacky/prompts/base.md +1 -1
  31. data/lib/clacky/providers.rb +44 -13
  32. data/lib/clacky/rich_ui/components/composer_guidance.rb +49 -0
  33. data/lib/clacky/rich_ui/rich_ui_controller.rb +24 -4
  34. data/lib/clacky/rich_ui/shell/rich_agent_shell.rb +13 -0
  35. data/lib/clacky/search_config.rb +3 -3
  36. data/lib/clacky/server/channel/channel_manager.rb +25 -7
  37. data/lib/clacky/server/channel/channel_ui_controller.rb +1 -1
  38. data/lib/clacky/server/dir_picker.rb +67 -4
  39. data/lib/clacky/server/git_panel.rb +10 -2
  40. data/lib/clacky/server/http_server.rb +391 -75
  41. data/lib/clacky/server/preview.rb +351 -0
  42. data/lib/clacky/server/session_registry.rb +4 -0
  43. data/lib/clacky/server/web_ui_controller.rb +10 -5
  44. data/lib/clacky/session_manager.rb +52 -1
  45. data/lib/clacky/skill.rb +48 -0
  46. data/lib/clacky/tools/browser.rb +176 -19
  47. data/lib/clacky/tools/web_search.rb +91 -8
  48. data/lib/clacky/ui2/components/command_suggestions.rb +3 -1
  49. data/lib/clacky/ui2/components/input_area.rb +37 -4
  50. data/lib/clacky/ui2/components/modal_component.rb +35 -6
  51. data/lib/clacky/ui2/screen_buffer.rb +1 -0
  52. data/lib/clacky/ui2/ui_controller.rb +72 -12
  53. data/lib/clacky/ui_interface.rb +1 -1
  54. data/lib/clacky/utils/file_processor.rb +19 -7
  55. data/lib/clacky/utils/mac_app_detector.rb +186 -0
  56. data/lib/clacky/utils/model_pricing.rb +118 -68
  57. data/lib/clacky/utils/windows_app_detector.rb +334 -0
  58. data/lib/clacky/version.rb +1 -1
  59. data/lib/clacky/web/app.css +998 -168
  60. data/lib/clacky/web/app.js +19 -0
  61. data/lib/clacky/web/components/chat-navigator.js +489 -202
  62. data/lib/clacky/web/components/code-editor.js +191 -5
  63. data/lib/clacky/web/components/composer.js +3 -1
  64. data/lib/clacky/web/components/datepicker.js +19 -19
  65. data/lib/clacky/web/components/mentions.js +11 -14
  66. data/lib/clacky/web/components/model-picker.js +66 -23
  67. data/lib/clacky/web/components/quote-select.js +341 -0
  68. data/lib/clacky/web/core/aside.js +157 -8
  69. data/lib/clacky/web/core/ext.js +16 -0
  70. data/lib/clacky/web/features/billing/view.js +75 -21
  71. data/lib/clacky/web/features/extensions/store.js +4 -1
  72. data/lib/clacky/web/features/model-tester/store.js +37 -4
  73. data/lib/clacky/web/features/skills/store.js +18 -1
  74. data/lib/clacky/web/features/skills/view.js +128 -7
  75. data/lib/clacky/web/features/workspace/store.js +80 -7
  76. data/lib/clacky/web/features/workspace/view.js +474 -43
  77. data/lib/clacky/web/i18n.js +158 -23
  78. data/lib/clacky/web/index.html +57 -29
  79. data/lib/clacky/web/sessions.js +517 -127
  80. data/lib/clacky/web/settings.js +155 -6
  81. data/lib/clacky/web/utils.js +34 -0
  82. data/lib/clacky/web/vendor/codemirror/codemirror.min.js +24 -19
  83. data/lib/clacky/web/vendor/codemirror/entry.js +130 -0
  84. data/lib/clacky/web/vendor/codemirror/package.json +29 -0
  85. data/lib/clacky/web/ws-dispatcher.js +161 -4
  86. data/lib/clacky.rb +2 -0
  87. metadata +13 -1
@@ -20,7 +20,7 @@ const I18n = (() => {
20
20
  "sidebar.chat": "Chat",
21
21
  "sidebar.projects": "Projects",
22
22
  "sidebar.config": "Config",
23
- "sidebar.tasks": "Task Management",
23
+ "sidebar.tasks": "Scheduled Tasks",
24
24
  "sidebar.skills": "Skill Management",
25
25
  "sidebar.channels": "Channel Management",
26
26
  "sidebar.mcp": "MCP Servers",
@@ -96,7 +96,24 @@ const I18n = (() => {
96
96
  "chat.done": "Done — {{n}} iteration(s), {{cost}}",
97
97
  "chat.done.duration": " · {{duration}}s",
98
98
  "chat.done.cache": "Cache hit {{rate}}% ({{hits}}/{{total}}) · {{tokens}} tokens reused",
99
- "chat.interrupted": "Interrupted.",
99
+ "chat.input.behaviorTitle": "Messages while working",
100
+ "chat.input.interruptDescription": "Stop the current step and accept your message.",
101
+ "chat.input.steerDescription": "Accept your message after the current step.",
102
+ "chat.input.interruptMode": "Interrupt and run",
103
+ "chat.input.steerMode": "Guide current task",
104
+ "chat.input.queued": "Queued task",
105
+ "chat.input.queueMode": "Queue task",
106
+ "chat.input.queueDescription": "Run after the current task completes.",
107
+ "chat.input.guidancePending": "Waiting for current task to receive guidance",
108
+ "chat.input.guidanceClosed": "The current task is not accepting guidance",
109
+ "chat.input.guidanceRejected": "The task is no longer accepting guidance. Your message remains queued.",
110
+ "chat.input.stoppingDelayed": "The current task is still stopping. Your message remains queued; try again once it stops.",
111
+ "chat.input.stopping": "Stopping current task…",
112
+ "chat.input.editQueued": "Edit",
113
+ "chat.input.saveQueued": "Save",
114
+ "chat.input.sendQueued": "Interrupt and run",
115
+ "chat.input.removeQueued": "Remove",
116
+ "chat.interrupted": "Generation stopped",
100
117
  "chat.feedback_hint": "Or type your own answer below ↓",
101
118
  "chat.feedback_submit": "Submit",
102
119
  "chat.feedback_other": "Or type your own answer",
@@ -106,6 +123,10 @@ const I18n = (() => {
106
123
  "chat.feedback_next": "Next",
107
124
  "chat.newMessageHint": "New messages ↓",
108
125
  "chat.retry": "Retry",
126
+ "chat.quote.btn": "Quote",
127
+ "chat.quote.roleUser": "You",
128
+ "chat.quote.roleAssistant": "Assistant",
129
+ "chat.quote.sig": "{{role}} · #{{index}}",
109
130
  "chat.resetSession": "Reset session",
110
131
  "chat.resetSessionConfirm": "Reset will start a brand-new session. The current conversation history stays in your sidebar but will no longer be active. Continue?",
111
132
  "chat.copy": "Copy",
@@ -127,7 +148,12 @@ const I18n = (() => {
127
148
  "chat.empty.tip2": "Summarize and organize information",
128
149
  "chat.empty.tip3": "Upload an image or file for me to analyze",
129
150
  "chat.nav.title": "Messages",
151
+ "chat.nav.round": "Message {{number}}",
130
152
  "chat.nav.empty": "(empty)",
153
+ "chat.nav.aiReply": "AI reply",
154
+ "chat.nav.noReply": "No final reply yet",
155
+ "chat.nav.retry": "Navigation could not load. Click to retry.",
156
+ "chat.nav.latest": "Back to latest messages ↓",
131
157
 
132
158
  // ── Session list ──
133
159
  "sessions.empty": "No sessions yet",
@@ -180,6 +206,9 @@ const I18n = (() => {
180
206
  "sib.dir.drives": "Drives",
181
207
  "sib.dir.places.drive": "{{letter}}:",
182
208
  "workspace.title": "Files",
209
+ "workspace.toggleTree": "Toggle file tree",
210
+ "workspace.backToFiles": "Back to files",
211
+ "workspace.splitPreview": "Split preview",
183
212
  "workspace.expand": "Open workspace",
184
213
  "workspace.collapse": "Collapse workspace",
185
214
  "workspace.refresh": "Refresh",
@@ -191,11 +220,21 @@ const I18n = (() => {
191
220
  "workspace.downloadFailed": "Download failed",
192
221
  "workspace.previewFailed": "Failed to preview file",
193
222
  "workspace.previewUnsupported": "Cannot preview this file type",
223
+ "workspace.fallbackHint": "This file type can't be previewed here.",
224
+ "workspace.fallbackSubHint": "Please open this file in another application to view it.",
225
+ "workspace.tableTruncated": "Showing the first %{rows} rows only. Use \"Open\" to view the full file.",
226
+ "workspace.open": "Open",
227
+ "workspace.openDefault": "Open with default app",
228
+ "workspace.openWith": "Open with {{app}}",
229
+ "workspace.openWithFailed": "Failed to open file",
230
+ "workspace.openedInSystem": "Opened it in your default application.",
194
231
  "workspace.download": "Download",
195
232
  "workspace.copyPath": "Copy Path",
196
233
  "workspace.copyRelPath": "Copy Relative Path",
197
234
  "aside.collapse": "Collapse panel",
198
235
  "aside.expand": "Open panel",
236
+ "aside.fullscreen": "Fullscreen preview",
237
+ "aside.exitFullscreen": "Exit fullscreen",
199
238
  "workspace.revealInFinder": "Reveal in Finder",
200
239
  "workspace.revealFailed": "Failed to reveal file",
201
240
  "workspace.closeTab": "Close",
@@ -227,6 +266,7 @@ const I18n = (() => {
227
266
  "changes.select.count": "{{n}}/{{total}} selected",
228
267
  "changes.save.btnN": "Save {{n}} selected file(s)",
229
268
  "changes.save.promptMsg": "Describe this snapshot",
269
+ "changes.backToList": "Back to changes",
230
270
  "changes.diff.loading": "Loading diff…",
231
271
  "changes.diff.fail": "Failed to load diff",
232
272
  "changes.diff.same": "No content change in this file.",
@@ -237,7 +277,19 @@ const I18n = (() => {
237
277
  "changes.restore.diffBtn": "Discard changes",
238
278
  "changes.restore.doing": "Reverting…",
239
279
  "changes.restore.done": "Reverted: {{path}}",
240
- "changes.restore.failed": "Revert failed", "tm.tab": "History",
280
+ "changes.restore.failed": "Revert failed",
281
+ "preview.tab": "Preview",
282
+ "preview.empty.title": "Start browsing",
283
+ "preview.empty.sub": "Enter a local file path or localhost address to open the page",
284
+ "preview.address": "Enter a local file path or localhost address",
285
+ "preview.notFound": "File not found",
286
+ "preview.unreachable": "Cannot reach the dev server",
287
+ "preview.openExternal": "Open in new tab",
288
+ "preview.reload": "Reload",
289
+ "preview.back": "Back",
290
+ "preview.forward": "Forward",
291
+ "preview.go": "Go",
292
+ "tm.tab": "History",
241
293
  "sib.model.tooltip": "Click to switch model",
242
294
  "sib.model.tooltip.busy": "Model switching is disabled while the agent is responding",
243
295
  "sib.variant.header": "Quick switch",
@@ -582,6 +634,15 @@ const I18n = (() => {
582
634
  "skills.upload.publishTip": "Publish to cloud",
583
635
  "skills.btn.refresh": "Refresh",
584
636
  "skills.editFail": "Failed to load skill",
637
+ "skills.edit.title": "Edit skill",
638
+ "skills.edit.name": "Name",
639
+ "skills.edit.nameHint": "English slug used as the /command name",
640
+ "skills.edit.nameZh": "Chinese name",
641
+ "skills.edit.description": "Description",
642
+ "skills.edit.descriptionZh": "Chinese description",
643
+ "skills.edit.body": "Content (Markdown)",
644
+ "skills.edit.editMarkdown": "Edit Markdown",
645
+ "skills.edit.saveFail": "Save failed",
585
646
 
586
647
  // ── Channels panel ──
587
648
  "channels.title": "Channels",
@@ -783,6 +844,7 @@ const I18n = (() => {
783
844
  "settings.media.baseUrl.required": "Base URL required",
784
845
  "settings.models.field.quicksetup": "Quick Setup",
785
846
  "settings.models.field.model": "Model",
847
+ "settings.models.field.modelPlaceholder": "Type or pick a model",
786
848
  "settings.models.field.baseurl": "Base URL",
787
849
  "settings.models.field.apikey": "API Key",
788
850
  "settings.models.field.apiFormat": "API Format",
@@ -836,6 +898,10 @@ const I18n = (() => {
836
898
  "settings.models.provider.custom": "Custom",
837
899
  "settings.models.unnamed": "Unnamed Model",
838
900
  "settings.models.noModels": "No preset models available",
901
+ "settings.models.loadingModels": "Loading models…",
902
+ "settings.models.loadEmpty": "Endpoint returned no models",
903
+ "settings.models.loadFailed": "Couldn't load models: {{msg}}",
904
+ "settings.models.fetchedFrom": "Fetched from {{endpoint}} · {{count}} models",
839
905
  "settings.models.test.ok": "✓ Connected",
840
906
  "settings.models.test.fail": "✗ Test fail: {{msg}}",
841
907
  "settings.models.badge.model": "Model {{n}}",
@@ -867,9 +933,9 @@ const I18n = (() => {
867
933
  "settings.network.cleared": "Cleared — direct connection",
868
934
  "settings.network.invalidUrl": "Invalid URL — use http://host:port or http://user:pass@host:port",
869
935
  "settings.search.title": "Web Search",
870
- "settings.search.desc": "Free built-in search (DuckDuckGo / Bing) works out of the box. Connect an API provider below for higher-quality results — falls back to built-in if unavailable.",
936
+ "settings.search.desc": "Free built-in search (Parallel / DuckDuckGo / Bing) works out of the box. Select an API provider below to use it instead.",
871
937
  "settings.search.provider": "Provider",
872
- "settings.search.builtin": "Built-in (DuckDuckGo / Bing)",
938
+ "settings.search.builtin": "Built-in (Parallel / DuckDuckGo / Bing)",
873
939
  "settings.search.key": "API Key",
874
940
  "settings.search.save": "Save",
875
941
  "settings.search.saved": "Saved",
@@ -1194,7 +1260,7 @@ const I18n = (() => {
1194
1260
  "sidebar.chat": "会话",
1195
1261
  "sidebar.projects": "项目",
1196
1262
  "sidebar.config": "配置",
1197
- "sidebar.tasks": "任务管理",
1263
+ "sidebar.tasks": "定时任务",
1198
1264
  "sidebar.skills": "技能管理",
1199
1265
  "sidebar.channels": "频道管理",
1200
1266
  "sidebar.mcp": "MCP 工具",
@@ -1269,7 +1335,24 @@ const I18n = (() => {
1269
1335
  "chat.done": "完成 — {{n}} 步,{{cost}}",
1270
1336
  "chat.done.duration": " · {{duration}}s",
1271
1337
  "chat.done.cache": "缓存命中 {{rate}}% ({{hits}}/{{total}}) · 复用 {{tokens}} tokens",
1272
- "chat.interrupted": "已中断。",
1338
+ "chat.input.behaviorTitle": "AI 工作时发送消息",
1339
+ "chat.input.interruptDescription": "立即停止当前执行,接收新信息",
1340
+ "chat.input.steerDescription": "当前步骤结束后,接收新信息",
1341
+ "chat.input.interruptMode": "打断并执行",
1342
+ "chat.input.steerMode": "引导当前任务",
1343
+ "chat.input.queued": "待执行",
1344
+ "chat.input.queueMode": "排队执行",
1345
+ "chat.input.queueDescription": "当前任务完整结束后,按顺序执行",
1346
+ "chat.input.guidancePending": "等待当前任务接收引导",
1347
+ "chat.input.guidanceClosed": "当前任务已不再接收引导",
1348
+ "chat.input.guidanceRejected": "当前任务已不再接收引导,消息仍保留在队列中。",
1349
+ "chat.input.stoppingDelayed": "当前任务尚未停止,消息已保留在队列中,请停止后重试。",
1350
+ "chat.input.stopping": "正在停止当前任务…",
1351
+ "chat.input.editQueued": "编辑",
1352
+ "chat.input.saveQueued": "保存",
1353
+ "chat.input.sendQueued": "打断并执行",
1354
+ "chat.input.removeQueued": "撤回",
1355
+ "chat.interrupted": "已停止生成",
1273
1356
  "chat.feedback_hint": "或在下方输入框自由作答 ↓",
1274
1357
  "chat.feedback_submit": "提交",
1275
1358
  "chat.feedback_other": "或自己填写",
@@ -1279,6 +1362,10 @@ const I18n = (() => {
1279
1362
  "chat.feedback_next": "下一题",
1280
1363
  "chat.newMessageHint": "有新消息 ↓",
1281
1364
  "chat.retry": "重试",
1365
+ "chat.quote.btn": "引用",
1366
+ "chat.quote.roleUser": "我",
1367
+ "chat.quote.roleAssistant": "助手",
1368
+ "chat.quote.sig": "{{role}} · 第{{index}}条",
1282
1369
  "chat.copy": "复制",
1283
1370
  "chat.todo.title": "任务清单",
1284
1371
  "chat.todo.spawn": "开干",
@@ -1298,7 +1385,12 @@ const I18n = (() => {
1298
1385
  "chat.empty.tip2": "归纳总结、整理信息",
1299
1386
  "chat.empty.tip3": "上传图片或文件,让我帮你分析",
1300
1387
  "chat.nav.title": "消息导航",
1388
+ "chat.nav.round": "第 {{number}} 条消息",
1301
1389
  "chat.nav.empty": "(空)",
1390
+ "chat.nav.aiReply": "AI 回复",
1391
+ "chat.nav.noReply": "暂无最终回复",
1392
+ "chat.nav.retry": "导航加载失败,点击重试",
1393
+ "chat.nav.latest": "回到最新消息 ↓",
1302
1394
 
1303
1395
  // ── Session list ──
1304
1396
  "sessions.empty": "暂无会话",
@@ -1351,6 +1443,9 @@ const I18n = (() => {
1351
1443
  "sib.dir.drives": "驱动器",
1352
1444
  "sib.dir.places.drive": "{{letter}}盘",
1353
1445
  "workspace.title": "文件",
1446
+ "workspace.toggleTree": "切换目录树",
1447
+ "workspace.backToFiles": "返回文件列表",
1448
+ "workspace.splitPreview": "并排预览",
1354
1449
  "workspace.expand": "打开工作区",
1355
1450
  "workspace.collapse": "收起工作区",
1356
1451
  "workspace.refresh": "刷新",
@@ -1362,11 +1457,21 @@ const I18n = (() => {
1362
1457
  "workspace.downloadFailed": "下载失败",
1363
1458
  "workspace.previewFailed": "文件预览失败",
1364
1459
  "workspace.previewUnsupported": "无法预览此类型文件",
1460
+ "workspace.fallbackHint": "该格式暂不支持在线预览",
1461
+ "workspace.fallbackSubHint": "请在其他应用中打开此文件查看",
1462
+ "workspace.tableTruncated": "仅显示前 %{rows} 行,点击「打开」查看完整文件",
1463
+ "workspace.open": "打开",
1464
+ "workspace.openDefault": "用默认应用打开",
1465
+ "workspace.openWith": "用 {{app}} 打开",
1466
+ "workspace.openedInSystem": "已用系统默认程序打开",
1467
+ "workspace.openWithFailed": "打开文件失败",
1365
1468
  "workspace.download": "下载",
1366
1469
  "workspace.copyPath": "复制路径",
1367
1470
  "workspace.copyRelPath": "复制相对路径",
1368
1471
  "aside.collapse": "收起面板",
1369
1472
  "aside.expand": "打开面板",
1473
+ "aside.fullscreen": "全屏预览",
1474
+ "aside.exitFullscreen": "退出全屏",
1370
1475
  "workspace.revealInFinder": "打开所在文件夹",
1371
1476
  "workspace.revealFailed": "无法打开文件位置",
1372
1477
  "workspace.closeTab": "关闭",
@@ -1398,6 +1503,7 @@ const I18n = (() => {
1398
1503
  "changes.select.count": "已选 {{n}}/{{total}}",
1399
1504
  "changes.save.btnN": "存档选中的 {{n}} 个文件",
1400
1505
  "changes.save.promptMsg": "给这个版本写句说明",
1506
+ "changes.backToList": "返回改动列表",
1401
1507
  "changes.diff.loading": "正在读取差异…",
1402
1508
  "changes.diff.fail": "读取差异失败",
1403
1509
  "changes.diff.same": "这个文件的内容没有变化。",
@@ -1409,6 +1515,17 @@ const I18n = (() => {
1409
1515
  "changes.restore.doing": "正在撤销…",
1410
1516
  "changes.restore.done": "已撤销:{{path}}",
1411
1517
  "changes.restore.failed": "撤销失败",
1518
+ "preview.tab": "预览",
1519
+ "preview.empty.title": "开始浏览",
1520
+ "preview.empty.sub": "输入本地文件路径或 localhost 地址以打开页面",
1521
+ "preview.address": "输入本地文件路径或 localhost 地址",
1522
+ "preview.notFound": "文件不存在",
1523
+ "preview.unreachable": "无法连接到开发服务器",
1524
+ "preview.openExternal": "在新标签页打开",
1525
+ "preview.reload": "刷新",
1526
+ "preview.back": "后退",
1527
+ "preview.forward": "前进",
1528
+ "preview.go": "前往",
1412
1529
  "tm.tab": "时光机",
1413
1530
  "sib.model.tooltip": "点击切换模型",
1414
1531
  "sib.model.tooltip.busy": "Agent 回复中,暂时无法切换模型",
@@ -1525,7 +1642,7 @@ const I18n = (() => {
1525
1642
  "upgrade.tooltip.done": "重启成功",
1526
1643
 
1527
1644
  // ── Tasks panel ──
1528
- "tasks.title": "任务管理",
1645
+ "tasks.title": "定时任务",
1529
1646
  "tasks.subtitle": "管理和调度助手的自动化任务",
1530
1647
  "tasks.btn.create": "创建任务",
1531
1648
  "tasks.btn.createTask": "创建任务",
@@ -1753,6 +1870,15 @@ const I18n = (() => {
1753
1870
  "skills.upload.publishTip": "发布到云端",
1754
1871
  "skills.btn.refresh": "刷新",
1755
1872
  "skills.editFail": "加载技能失败",
1873
+ "skills.edit.title": "编辑技能",
1874
+ "skills.edit.name": "名称",
1875
+ "skills.edit.nameHint": "英文标识,同时作为 /命令 名称",
1876
+ "skills.edit.nameZh": "中文名称",
1877
+ "skills.edit.description": "英文描述",
1878
+ "skills.edit.descriptionZh": "中文描述",
1879
+ "skills.edit.body": "内容(Markdown)",
1880
+ "skills.edit.editMarkdown": "编辑 Markdown",
1881
+ "skills.edit.saveFail": "保存失败",
1756
1882
 
1757
1883
  // ── Channels panel ──
1758
1884
  "channels.title": "频道管理",
@@ -1911,7 +2037,7 @@ const I18n = (() => {
1911
2037
  "settings.models.loading": "加载模型中…",
1912
2038
  "settings.models.error": "加载失败:{{msg}}",
1913
2039
  "settings.models.empty": "暂未配置模型,点击「+ 添加模型」添加。",
1914
- "settings.models.search.placeholder": "搜索模型 / 服务商 / 备注 / Base URL",
2040
+ "settings.models.search.placeholder": "搜索模型 / 服务商 / 备注 / 接口地址",
1915
2041
  "settings.models.search.noResults": "没有匹配的模型。",
1916
2042
  "settings.models.filter.all": "全部服务商",
1917
2043
  "settings.models.badge.default": "默认",
@@ -1930,8 +2056,8 @@ const I18n = (() => {
1930
2056
  "settings.media.source.auto": "自动",
1931
2057
  "settings.media.source.custom": "自定义",
1932
2058
  "settings.media.field.model": "模型",
1933
- "settings.media.field.baseUrl": "Base URL",
1934
- "settings.media.field.apiKey": "API Key",
2059
+ "settings.media.field.baseUrl": "接口地址",
2060
+ "settings.media.field.apiKey": "API 密钥",
1935
2061
  "settings.media.field.provider":"服务商",
1936
2062
  "settings.media.off.hint": "已关闭。",
1937
2063
  "settings.media.auto.followsDefault": "该服务商已支持,已自动启用",
@@ -1948,10 +2074,10 @@ const I18n = (() => {
1948
2074
  "settings.media.action.saved": "已保存",
1949
2075
  "settings.media.action.test": "测试",
1950
2076
  "settings.media.testing": "测试连接中…",
1951
- "settings.media.apiKey.placeholder": "请输入 API Key",
1952
- "settings.media.apiKey.required": "请填写 API Key",
2077
+ "settings.media.apiKey.placeholder": "请输入 API 密钥",
2078
+ "settings.media.apiKey.required": "请填写 API 密钥",
1953
2079
  "settings.media.model.required": "请填写模型名称",
1954
- "settings.media.baseUrl.required": "请填写 Base URL",
2080
+ "settings.media.baseUrl.required": "请填写接口地址",
1955
2081
  "settings.media.output_dir.desc": "生成的图片、视频、音频文件保存位置(可选)",
1956
2082
  "settings.media.output_dir.browse": "选择目录…",
1957
2083
  "settings.media.output_dir.picker": "选择媒体输出目录",
@@ -1960,9 +2086,10 @@ const I18n = (() => {
1960
2086
  "settings.media.output_dir.cleared": "已清除",
1961
2087
  "settings.media.output_dir.invalid": "目录无效",
1962
2088
  "settings.models.field.quicksetup": "快速配置",
1963
- "settings.models.field.model": "Model",
1964
- "settings.models.field.baseurl": "Base URL",
1965
- "settings.models.field.apikey": "API Key",
2089
+ "settings.models.field.model": "模型",
2090
+ "settings.models.field.modelPlaceholder": "输入或选择模型",
2091
+ "settings.models.field.baseurl": "接口地址",
2092
+ "settings.models.field.apikey": "API 密钥",
1966
2093
  "settings.models.field.apiFormat": "API 协议",
1967
2094
  "settings.models.apiFormat.auto": "自动(根据服务商和模型检测)",
1968
2095
  "settings.models.apiFormat.anthropicMessages": "Anthropic Messages API",
@@ -2014,6 +2141,10 @@ const I18n = (() => {
2014
2141
  "settings.models.provider.custom": "自定义",
2015
2142
  "settings.models.unnamed": "未命名模型",
2016
2143
  "settings.models.noModels": "暂无预设模型",
2144
+ "settings.models.loadingModels": "正在加载模型…",
2145
+ "settings.models.loadEmpty": "该接口未返回模型",
2146
+ "settings.models.loadFailed": "加载模型失败:{{msg}}",
2147
+ "settings.models.fetchedFrom": "已从 {{endpoint}} 获取 · {{count}} 个模型",
2017
2148
  "settings.models.test.ok": "✓ 连接成功",
2018
2149
  "settings.models.test.fail": "✗ 测试失败:{{msg}}",
2019
2150
  "settings.models.badge.model": "模型 {{n}}",
@@ -2045,9 +2176,9 @@ const I18n = (() => {
2045
2176
  "settings.network.cleared": "已清除 — 直连",
2046
2177
  "settings.network.invalidUrl": "地址格式不正确,请使用 http://host:port 或 http://user:pass@host:port",
2047
2178
  "settings.search.title": "网页搜索",
2048
- "settings.search.desc": "内置免费搜索(DuckDuckGo / Bing)开箱即用。在下方接入 API 服务商可获得更高质量的搜索结果,不可用时自动回退到内置引擎。",
2179
+ "settings.search.desc": "内置免费搜索(Parallel / DuckDuckGo / Bing)开箱即用;也可在下方接入 API 服务商作为替代。",
2049
2180
  "settings.search.provider": "服务商",
2050
- "settings.search.builtin": "内置(DuckDuckGo / Bing)",
2181
+ "settings.search.builtin": "内置(Parallel / DuckDuckGo / Bing)",
2051
2182
  "settings.search.key": "API 密钥",
2052
2183
  "settings.search.save": "保存",
2053
2184
  "settings.search.saved": "已保存",
@@ -2153,8 +2284,8 @@ const I18n = (() => {
2153
2284
  "onboard.key.provider": "服务商",
2154
2285
  "onboard.key.provider.placeholder": "— 选择服务商 —",
2155
2286
  "onboard.key.model": "模型",
2156
- "onboard.key.baseurl": "Base URL",
2157
- "onboard.key.apikey": "API Key",
2287
+ "onboard.key.baseurl": "接口地址",
2288
+ "onboard.key.apikey": "API 密钥",
2158
2289
  "onboard.key.getApiKey": "如何获取 →",
2159
2290
  "onboard.key.docsGuide.question": "不会配置 AI 模型?",
2160
2291
  "onboard.key.docsGuide.cta": "查看指南 →",
@@ -2176,7 +2307,7 @@ const I18n = (() => {
2176
2307
  "onboard.device.success.model.label": "试用模型:",
2177
2308
  "onboard.device.success.note": "试用期内仅可使用 Gemini 3.8 Flash —— 任意金额充值后,同一 Key 立即解锁全部前沿模型。",
2178
2309
  "onboard.device.success.btn": "开始使用",
2179
- "onboard.manual.toggle": "使用自己的 API Key 手动配置",
2310
+ "onboard.manual.toggle": "使用自己的 API 密钥手动配置",
2180
2311
  "onboard.provider.custom": "自定义",
2181
2312
  "provider.recommended": "推荐",
2182
2313
  "provider.name.volcengine_ark": "火山方舟(豆包)",
@@ -2396,7 +2527,11 @@ const I18n = (() => {
2396
2527
  const dict = TRANSLATIONS[_lang] || TRANSLATIONS[DEFAULT_LANG];
2397
2528
  let str = dict[key] ?? TRANSLATIONS[DEFAULT_LANG][key] ?? key;
2398
2529
  Object.entries(vars).forEach(([k, v]) => {
2399
- str = str.replaceAll(`{{${k}}}`, v);
2530
+ // Function replacement: unlike the string form it never interprets `$`
2531
+ // patterns (`$&`, `$$`…) inside the value — interpolated values can
2532
+ // carry remote-origin text (e.g. model-list errors) that must appear
2533
+ // verbatim.
2534
+ str = str.replaceAll(`{{${k}}}`, () => v);
2400
2535
  });
2401
2536
  return str;
2402
2537
  }
@@ -234,7 +234,7 @@
234
234
  <polyline points="12 6 12 12 16 14"/>
235
235
  </svg>
236
236
  <div class="task-info">
237
- <span class="task-name" id="tasks-sidebar-label" data-i18n="sidebar.tasks">Task Management</span>
237
+ <span class="task-name" id="tasks-sidebar-label" data-i18n="sidebar.tasks">Scheduled Tasks</span>
238
238
  </div>
239
239
  </div>
240
240
  </div>
@@ -248,24 +248,24 @@
248
248
  </div>
249
249
  </div>
250
250
  </div>
251
- <div id="extensions-sidebar-item" class="task-item task-item-summary">
251
+ <div id="channels-sidebar-item" class="task-item task-item-summary">
252
252
  <div class="task-row">
253
253
  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="task-icon">
254
- <path d="M19.439 7.85c-.049.322.059.648.289.878l1.568 1.568c.47.47.706 1.087.706 1.704s-.235 1.233-.706 1.704l-1.611 1.611a.98.98 0 0 1-.837.276c-.47-.07-.802-.48-.968-.925a2.501 2.501 0 1 0-3.214 3.214c.446.166.855.497.925.968a.979.979 0 0 1-.276.837l-1.61 1.61a2.404 2.404 0 0 1-1.705.707 2.402 2.402 0 0 1-1.704-.706l-1.568-1.568a1.026 1.026 0 0 0-.877-.29c-.493.074-.84.504-1.02.968a2.5 2.5 0 1 1-3.237-3.237c.464-.18.894-.527.967-1.02a1.026 1.026 0 0 0-.289-.877l-1.568-1.568A2.402 2.402 0 0 1 1.998 12c0-.617.236-1.234.706-1.704L4.23 8.77c.24-.24.581-.353.917-.303.515.077.877.528 1.073 1.01a2.5 2.5 0 1 0 3.259-3.259c-.482-.196-.933-.558-1.01-1.073-.05-.336.062-.676.303-.917l1.525-1.525A2.402 2.402 0 0 1 12 1.998c.617 0 1.234.236 1.704.706l1.568 1.568c.23.23.556.338.877.29.493-.074.84-.504 1.02-.968a2.5 2.5 0 1 1 3.237 3.237c-.464.18-.894.527-.967 1.02Z"/>
254
+ <rect x="4" y="2" width="16" height="20" rx="2"/>
255
+ <path d="M12 18h.01"/>
255
256
  </svg>
256
257
  <div class="task-info">
257
- <span class="task-name" id="extensions-sidebar-label" data-i18n="sidebar.extensions">Extension Management</span>
258
+ <span class="task-name" id="channels-sidebar-label" data-i18n="sidebar.channels">Channel Management</span>
258
259
  </div>
259
260
  </div>
260
261
  </div>
261
- <div id="channels-sidebar-item" class="task-item task-item-summary">
262
+ <div id="extensions-sidebar-item" class="task-item task-item-summary">
262
263
  <div class="task-row">
263
264
  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="task-icon">
264
- <rect x="4" y="2" width="16" height="20" rx="2"/>
265
- <path d="M12 18h.01"/>
265
+ <path d="M19.439 7.85c-.049.322.059.648.289.878l1.568 1.568c.47.47.706 1.087.706 1.704s-.235 1.233-.706 1.704l-1.611 1.611a.98.98 0 0 1-.837.276c-.47-.07-.802-.48-.968-.925a2.501 2.501 0 1 0-3.214 3.214c.446.166.855.497.925.968a.979.979 0 0 1-.276.837l-1.61 1.61a2.404 2.404 0 0 1-1.705.707 2.402 2.402 0 0 1-1.704-.706l-1.568-1.568a1.026 1.026 0 0 0-.877-.29c-.493.074-.84.504-1.02.968a2.5 2.5 0 1 1-3.237-3.237c.464-.18.894-.527.967-1.02a1.026 1.026 0 0 0-.289-.877l-1.568-1.568A2.402 2.402 0 0 1 1.998 12c0-.617.236-1.234.706-1.704L4.23 8.77c.24-.24.581-.353.917-.303.515.077.877.528 1.073 1.01a2.5 2.5 0 1 0 3.259-3.259c-.482-.196-.933-.558-1.01-1.073-.05-.336.062-.676.303-.917l1.525-1.525A2.402 2.402 0 0 1 12 1.998c.617 0 1.234.236 1.704.706l1.568 1.568c.23.23.556.338.877.29.493-.074.84-.504 1.02-.968a2.5 2.5 0 1 1 3.237 3.237c-.464.18-.894.527-.967 1.02Z"/>
266
266
  </svg>
267
267
  <div class="task-info">
268
- <span class="task-name" id="channels-sidebar-label" data-i18n="sidebar.channels">Channel Management</span>
268
+ <span class="task-name" id="extensions-sidebar-label" data-i18n="sidebar.extensions">Extension Management</span>
269
269
  </div>
270
270
  </div>
271
271
  </div>
@@ -486,7 +486,7 @@
486
486
  <div id="sib-actions-dropdown" class="sib-actions-dropdown" style="display:none" role="menu"></div>
487
487
  </span>
488
488
  <span class="sib-sep sib-sep-after-id">│</span>
489
- <span id="sib-dir" data-i18n-title="sib.dir.tooltip" title="Click to change directory"></span>
489
+ <span id="sib-dir" data-i18n-title="sib.dir.tooltip" title="Click to change directory"><span id="sib-dir-text"></span><span class="sib-caret" aria-hidden="true"><svg viewBox="0 0 16 16" width="9" height="9" aria-hidden="true"><path d="M4 6.5L8 10.5L12 6.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span></span>
490
490
  <span class="sib-sep sib-sep-after-dir">│</span>
491
491
  <span id="sib-model-wrap">
492
492
  <span id="sib-model" class="sib-model-clickable" title="Click to switch model"></span>
@@ -495,7 +495,7 @@
495
495
  </span>
496
496
  <span class="sib-sep sib-sep-after-model">│</span>
497
497
  <span id="sib-reasoning-wrap">
498
- <span id="sib-reasoning" class="sib-reasoning-clickable" data-i18n-title="sib.reasoning.tooltip" title="Click to change reasoning effort"></span>
498
+ <span id="sib-reasoning" class="sib-reasoning-clickable" data-i18n-title="sib.reasoning.tooltip" title="Click to change reasoning effort"><span id="sib-reasoning-text"></span><span class="sib-caret" aria-hidden="true"><svg viewBox="0 0 16 16" width="9" height="9" aria-hidden="true"><path d="M4 6.5L8 10.5L12 6.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span></span>
499
499
  <div id="sib-reasoning-dropdown" class="sib-reasoning-dropdown" style="display:none" role="menu"></div>
500
500
  </span>
501
501
  <span class="sib-sep sib-sep-after-reasoning">│</span>
@@ -537,6 +537,7 @@
537
537
  <div id="skill-autocomplete-list"></div>
538
538
  </div>
539
539
  <!-- Image preview strip (hidden when empty) -->
540
+ <div id="input-queue" aria-live="polite" hidden></div>
540
541
  <div id="image-preview-strip" style="display:none"></div>
541
542
  <!-- @ mention menu (Files & Folders / Past Chats) -->
542
543
  <div id="mention-autocomplete" class="mention-autocomplete" style="display:none" role="listbox" aria-label="Mention"></div>
@@ -557,6 +558,24 @@
557
558
  aria-multiline="true"
558
559
  data-i18n-placeholder="chat.input.placeholder"
559
560
  data-placeholder="Message… (Enter to send, Shift-Enter for newline)"></div>
561
+ <input id="input-behavior" type="hidden" value="queue">
562
+ <div id="input-behavior-control" hidden>
563
+ <button id="input-behavior-toggle" type="button" aria-haspopup="menu" aria-expanded="false" aria-controls="input-behavior-menu" data-i18n-title="chat.input.behaviorTitle" title="Messages while working">
564
+ <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7h9"/><path d="M18 7h2"/><path d="M15.5 4.5v5"/><path d="M4 17h4"/><path d="M13 17h7"/><path d="M10.5 14.5v5"/></svg>
565
+ </button>
566
+ <div id="input-behavior-menu" role="menu" data-i18n-aria-label="chat.input.behaviorTitle" aria-label="Messages while working" hidden>
567
+ <div class="input-behavior-heading" data-i18n="chat.input.behaviorTitle">Messages while working</div>
568
+ <button type="button" role="menuitemradio" aria-checked="true" data-input-behavior="queue">
569
+ <span class="input-behavior-check" aria-hidden="true">✓</span><span><strong data-i18n="chat.input.queueMode">Queue task</strong><small data-i18n="chat.input.queueDescription">Run after the current task completes.</small></span>
570
+ </button>
571
+ <button type="button" role="menuitemradio" aria-checked="false" data-input-behavior="interrupt">
572
+ <span class="input-behavior-check" aria-hidden="true">✓</span><span><strong data-i18n="chat.input.interruptMode">Interrupt and add</strong><small data-i18n="chat.input.interruptDescription">Stop the current step and accept your message.</small></span>
573
+ </button>
574
+ <button type="button" role="menuitemradio" aria-checked="false" data-input-behavior="steer">
575
+ <span class="input-behavior-check" aria-hidden="true">✓</span><span><strong data-i18n="chat.input.steerMode">Guide current task</strong><small data-i18n="chat.input.steerDescription">Accept your message after the current step.</small></span>
576
+ </button>
577
+ </div>
578
+ </div>
560
579
  <button id="btn-send" title="Send message"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg></button>
561
580
  <button id="btn-interrupt" style="display:none" title="Stop"><svg viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="6" width="12" height="12" rx="1.5"/></svg></button>
562
581
  </div>
@@ -570,9 +589,17 @@
570
589
  sits outside the slot container so re-renders never clobber it. -->
571
590
  <div id="session-aside" class="session-aside">
572
591
  <div id="session-aside-resize" class="session-aside-resize"></div>
573
- <button id="btn-aside-collapse" class="session-aside-collapse" data-i18n-title="aside.collapse" title="Collapse">
574
- <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M9 3v18"/><path d="m14 9 3 3-3 3"/></svg>
575
- </button>
592
+ <!-- Host-owned actions docked at the right end of the tab bar (the bar
593
+ itself is rendered by Clacky.ext inside the slot below). -->
594
+ <div class="aside-tabbar-actions">
595
+ <button id="btn-aside-fullscreen" class="session-aside-fullscreen" data-i18n-title="aside.fullscreen" title="Fullscreen">
596
+ <svg class="icon-fs-enter" xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v3"/><path d="M21 8V5a2 2 0 0 0-2-2h-3"/><path d="M3 16v3a2 2 0 0 0 2 2h3"/><path d="M16 21h3a2 2 0 0 0 2-2v-3"/></svg>
597
+ <svg class="icon-fs-exit" xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3v3a2 2 0 0 1-2 2H3"/><path d="M21 8h-3a2 2 0 0 1-2-2V3"/><path d="M3 16h3a2 2 0 0 1 2 2v3"/><path d="M16 21v-3a2 2 0 0 1 2-2h3"/></svg>
598
+ </button>
599
+ <button id="btn-aside-collapse" class="session-aside-collapse" data-i18n-title="aside.collapse" title="Collapse">
600
+ <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M9 3v18"/><path d="m14 9 3 3-3 3"/></svg>
601
+ </button>
602
+ </div>
576
603
  <div id="ext-slot-session-aside" class="session-aside-slot" data-slot="session.aside"></div>
577
604
  </div>
578
605
 
@@ -1156,19 +1183,19 @@
1156
1183
  <span data-i18n="settings.search.title">Web Search</span>
1157
1184
  </div>
1158
1185
  <div class="settings-network">
1159
- <p class="settings-network-desc" data-i18n="settings.search.desc">By default web_search scrapes DuckDuckGo and Bing, which needs no key but returns lower-quality results. Select a search provider to use an API instead — it falls back to the built-in engines if the provider fails.</p>
1186
+ <p class="settings-network-desc" data-i18n="settings.search.desc">Free built-in search uses Parallel, DuckDuckGo, and Bing with no key required. Select a search provider to use a configured API instead.</p>
1160
1187
  <div class="settings-network-url">
1161
1188
  <label class="settings-network-url-label" for="settings-search-provider-trigger" data-i18n="settings.search.provider">Provider</label>
1162
1189
  <div class="settings-network-url-row">
1163
1190
  <div class="custom-select-wrapper" id="settings-search-provider-wrapper">
1164
1191
  <div class="custom-select-trigger" tabindex="0" id="settings-search-provider-trigger">
1165
- <span class="custom-select-value placeholder" id="settings-search-provider-value" data-i18n="settings.search.builtin">Built-in (DuckDuckGo / Bing)</span>
1192
+ <span class="custom-select-value placeholder" id="settings-search-provider-value" data-i18n="settings.search.builtin">Built-in (Parallel / DuckDuckGo / Bing)</span>
1166
1193
  <svg class="custom-select-arrow" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1167
1194
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
1168
1195
  </svg>
1169
1196
  </div>
1170
1197
  <div class="custom-select-dropdown" id="settings-search-provider-dropdown">
1171
- <div class="custom-select-option" data-value="" data-i18n="settings.search.builtin">Built-in (DuckDuckGo / Bing)</div>
1198
+ <div class="custom-select-option" data-value="" data-i18n="settings.search.builtin">Built-in (Parallel / DuckDuckGo / Bing)</div>
1172
1199
  </div>
1173
1200
  </div>
1174
1201
  <button type="button" id="btn-save-search" class="btn-settings-action" data-i18n="settings.search.save">Save</button>
@@ -1417,19 +1444,6 @@
1417
1444
  <div id="model-modal-promo-hint" class="provider-promo-hint" data-index="modal"></div>
1418
1445
  </label>
1419
1446
 
1420
- <label class="model-field">
1421
- <span class="field-label" data-i18n="settings.models.field.model">Model</span>
1422
- <div class="model-name-combobox" id="model-modal-model-combobox">
1423
- <input type="text" id="model-modal-model" class="field-input model-name-input" placeholder="gpt-4o" autocomplete="off">
1424
- <button class="model-name-dropdown-btn" type="button" id="model-modal-model-dropdown-btn">
1425
- <svg width="12" height="12" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1426
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
1427
- </svg>
1428
- </button>
1429
- <div class="model-name-dropdown" id="model-modal-model-dropdown" style="display:none"></div>
1430
- </div>
1431
- </label>
1432
-
1433
1447
  <label class="model-field">
1434
1448
  <span class="field-label" data-i18n="settings.models.field.baseurl">Base URL</span>
1435
1449
  <div class="base-url-combobox" id="model-modal-baseurl-combobox">
@@ -1458,6 +1472,19 @@
1458
1472
  </div>
1459
1473
  </label>
1460
1474
 
1475
+ <label class="model-field">
1476
+ <span class="field-label" data-i18n="settings.models.field.model">Model</span>
1477
+ <div class="model-name-combobox" id="model-modal-model-combobox">
1478
+ <input type="text" id="model-modal-model" class="field-input model-name-input" data-i18n-placeholder="settings.models.field.modelPlaceholder" placeholder="Type or pick a model" autocomplete="off">
1479
+ <button class="model-name-dropdown-btn" type="button" id="model-modal-model-dropdown-btn">
1480
+ <svg width="12" height="12" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1481
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
1482
+ </svg>
1483
+ </button>
1484
+ <div class="model-name-dropdown" id="model-modal-model-dropdown" style="display:none"></div>
1485
+ </div>
1486
+ </label>
1487
+
1461
1488
  <label class="model-field" id="model-modal-apiformat-field">
1462
1489
  <span class="field-label" data-i18n="settings.models.field.apiFormat">API Format</span>
1463
1490
  <div class="custom-select-wrapper" id="model-modal-api-format-wrapper">
@@ -1743,6 +1770,7 @@
1743
1770
  <script src="/ws-dispatcher.js"></script>
1744
1771
  <script src="/components/custom-select.js"></script>
1745
1772
  <script src="/sessions.js"></script>
1773
+ <script src="/components/quote-select.js"></script>
1746
1774
  <script src="/projects.js"></script>
1747
1775
  <script src="/features/workspace/store.js"></script>
1748
1776
  <script src="/features/workspace/view.js"></script>