@draht/web-ui 2026.3.14 → 2026.3.25

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 (187) hide show
  1. package/README.md +13 -13
  2. package/dist/ChatPanel.d.ts +1 -0
  3. package/dist/ChatPanel.d.ts.map +1 -1
  4. package/dist/ChatPanel.js +12 -11
  5. package/dist/ChatPanel.js.map +1 -1
  6. package/dist/components/AgentInterface.d.ts +1 -0
  7. package/dist/components/AgentInterface.d.ts.map +1 -1
  8. package/dist/components/AgentInterface.js +50 -48
  9. package/dist/components/AgentInterface.js.map +1 -1
  10. package/dist/components/AttachmentTile.d.ts.map +1 -1
  11. package/dist/components/AttachmentTile.js +7 -6
  12. package/dist/components/AttachmentTile.js.map +1 -1
  13. package/dist/components/ConsoleBlock.d.ts.map +1 -1
  14. package/dist/components/ConsoleBlock.js +6 -3
  15. package/dist/components/ConsoleBlock.js.map +1 -1
  16. package/dist/components/CustomProviderCard.d.ts.map +1 -1
  17. package/dist/components/CustomProviderCard.js +4 -6
  18. package/dist/components/CustomProviderCard.js.map +1 -1
  19. package/dist/components/ExpandableSection.d.ts.map +1 -1
  20. package/dist/components/ExpandableSection.js +6 -4
  21. package/dist/components/ExpandableSection.js.map +1 -1
  22. package/dist/components/Input.js.map +1 -1
  23. package/dist/components/MessageEditor.d.ts.map +1 -1
  24. package/dist/components/MessageEditor.js +124 -126
  25. package/dist/components/MessageEditor.js.map +1 -1
  26. package/dist/components/MessageList.d.ts.map +1 -1
  27. package/dist/components/MessageList.js +6 -5
  28. package/dist/components/MessageList.js.map +1 -1
  29. package/dist/components/Messages.d.ts.map +1 -1
  30. package/dist/components/Messages.js +16 -18
  31. package/dist/components/Messages.js.map +1 -1
  32. package/dist/components/ProviderKeyInput.d.ts.map +1 -1
  33. package/dist/components/ProviderKeyInput.js +10 -7
  34. package/dist/components/ProviderKeyInput.js.map +1 -1
  35. package/dist/components/SandboxedIframe.d.ts.map +1 -1
  36. package/dist/components/SandboxedIframe.js +0 -7
  37. package/dist/components/SandboxedIframe.js.map +1 -1
  38. package/dist/components/StreamingMessageContainer.d.ts.map +1 -1
  39. package/dist/components/StreamingMessageContainer.js +9 -9
  40. package/dist/components/StreamingMessageContainer.js.map +1 -1
  41. package/dist/components/ThinkingBlock.d.ts.map +1 -1
  42. package/dist/components/ThinkingBlock.js +5 -3
  43. package/dist/components/ThinkingBlock.js.map +1 -1
  44. package/dist/components/message-renderer-registry.js.map +1 -1
  45. package/dist/components/sandbox/ArtifactsRuntimeProvider.d.ts.map +1 -1
  46. package/dist/components/sandbox/ArtifactsRuntimeProvider.js +0 -3
  47. package/dist/components/sandbox/ArtifactsRuntimeProvider.js.map +1 -1
  48. package/dist/components/sandbox/AttachmentsRuntimeProvider.d.ts.map +1 -1
  49. package/dist/components/sandbox/AttachmentsRuntimeProvider.js +0 -1
  50. package/dist/components/sandbox/AttachmentsRuntimeProvider.js.map +1 -1
  51. package/dist/components/sandbox/ConsoleRuntimeProvider.d.ts.map +1 -1
  52. package/dist/components/sandbox/ConsoleRuntimeProvider.js +5 -3
  53. package/dist/components/sandbox/ConsoleRuntimeProvider.js.map +1 -1
  54. package/dist/components/sandbox/FileDownloadRuntimeProvider.d.ts.map +1 -1
  55. package/dist/components/sandbox/FileDownloadRuntimeProvider.js +3 -1
  56. package/dist/components/sandbox/FileDownloadRuntimeProvider.js.map +1 -1
  57. package/dist/components/sandbox/RuntimeMessageBridge.d.ts.map +1 -1
  58. package/dist/components/sandbox/RuntimeMessageBridge.js.map +1 -1
  59. package/dist/components/sandbox/RuntimeMessageRouter.d.ts.map +1 -1
  60. package/dist/components/sandbox/RuntimeMessageRouter.js +5 -3
  61. package/dist/components/sandbox/RuntimeMessageRouter.js.map +1 -1
  62. package/dist/dialogs/ApiKeyPromptDialog.d.ts.map +1 -1
  63. package/dist/dialogs/ApiKeyPromptDialog.js +10 -8
  64. package/dist/dialogs/ApiKeyPromptDialog.js.map +1 -1
  65. package/dist/dialogs/AttachmentOverlay.d.ts.map +1 -1
  66. package/dist/dialogs/AttachmentOverlay.js +31 -31
  67. package/dist/dialogs/AttachmentOverlay.js.map +1 -1
  68. package/dist/dialogs/CustomProviderDialog.d.ts.map +1 -1
  69. package/dist/dialogs/CustomProviderDialog.js +12 -12
  70. package/dist/dialogs/CustomProviderDialog.js.map +1 -1
  71. package/dist/dialogs/ModelSelector.d.ts +2 -1
  72. package/dist/dialogs/ModelSelector.d.ts.map +1 -1
  73. package/dist/dialogs/ModelSelector.js +84 -36
  74. package/dist/dialogs/ModelSelector.js.map +1 -1
  75. package/dist/dialogs/PersistentStorageDialog.d.ts.map +1 -1
  76. package/dist/dialogs/PersistentStorageDialog.js +10 -7
  77. package/dist/dialogs/PersistentStorageDialog.js.map +1 -1
  78. package/dist/dialogs/ProvidersModelsTab.d.ts.map +1 -1
  79. package/dist/dialogs/ProvidersModelsTab.js +5 -2
  80. package/dist/dialogs/ProvidersModelsTab.js.map +1 -1
  81. package/dist/dialogs/SessionListDialog.d.ts.map +1 -1
  82. package/dist/dialogs/SessionListDialog.js +13 -11
  83. package/dist/dialogs/SessionListDialog.js.map +1 -1
  84. package/dist/dialogs/SettingsDialog.d.ts +2 -1
  85. package/dist/dialogs/SettingsDialog.d.ts.map +1 -1
  86. package/dist/dialogs/SettingsDialog.js +18 -9
  87. package/dist/dialogs/SettingsDialog.js.map +1 -1
  88. package/dist/index.d.ts +1 -0
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/index.js +1 -0
  91. package/dist/index.js.map +1 -1
  92. package/dist/prompts/prompts.d.ts.map +1 -1
  93. package/dist/storage/app-storage.d.ts.map +1 -1
  94. package/dist/storage/app-storage.js +0 -5
  95. package/dist/storage/app-storage.js.map +1 -1
  96. package/dist/storage/backends/indexeddb-storage-backend.d.ts.map +1 -1
  97. package/dist/storage/backends/indexeddb-storage-backend.js +1 -2
  98. package/dist/storage/backends/indexeddb-storage-backend.js.map +1 -1
  99. package/dist/storage/store.d.ts.map +1 -1
  100. package/dist/storage/store.js +3 -1
  101. package/dist/storage/store.js.map +1 -1
  102. package/dist/storage/stores/custom-providers-store.d.ts.map +1 -1
  103. package/dist/storage/stores/custom-providers-store.js.map +1 -1
  104. package/dist/storage/stores/provider-keys-store.d.ts.map +1 -1
  105. package/dist/storage/stores/provider-keys-store.js.map +1 -1
  106. package/dist/storage/stores/sessions-store.d.ts.map +1 -1
  107. package/dist/storage/stores/sessions-store.js.map +1 -1
  108. package/dist/storage/stores/settings-store.d.ts.map +1 -1
  109. package/dist/storage/stores/settings-store.js.map +1 -1
  110. package/dist/tools/artifacts/ArtifactElement.d.ts.map +1 -1
  111. package/dist/tools/artifacts/ArtifactElement.js +4 -1
  112. package/dist/tools/artifacts/ArtifactElement.js.map +1 -1
  113. package/dist/tools/artifacts/ArtifactPill.js.map +1 -1
  114. package/dist/tools/artifacts/Console.d.ts.map +1 -1
  115. package/dist/tools/artifacts/Console.js +7 -4
  116. package/dist/tools/artifacts/Console.js.map +1 -1
  117. package/dist/tools/artifacts/DocxArtifact.d.ts.map +1 -1
  118. package/dist/tools/artifacts/DocxArtifact.js +5 -2
  119. package/dist/tools/artifacts/DocxArtifact.js.map +1 -1
  120. package/dist/tools/artifacts/ExcelArtifact.d.ts.map +1 -1
  121. package/dist/tools/artifacts/ExcelArtifact.js +5 -2
  122. package/dist/tools/artifacts/ExcelArtifact.js.map +1 -1
  123. package/dist/tools/artifacts/GenericArtifact.d.ts.map +1 -1
  124. package/dist/tools/artifacts/GenericArtifact.js +4 -1
  125. package/dist/tools/artifacts/GenericArtifact.js.map +1 -1
  126. package/dist/tools/artifacts/HtmlArtifact.d.ts.map +1 -1
  127. package/dist/tools/artifacts/HtmlArtifact.js +11 -9
  128. package/dist/tools/artifacts/HtmlArtifact.js.map +1 -1
  129. package/dist/tools/artifacts/ImageArtifact.d.ts.map +1 -1
  130. package/dist/tools/artifacts/ImageArtifact.js +4 -1
  131. package/dist/tools/artifacts/ImageArtifact.js.map +1 -1
  132. package/dist/tools/artifacts/MarkdownArtifact.d.ts.map +1 -1
  133. package/dist/tools/artifacts/MarkdownArtifact.js +6 -3
  134. package/dist/tools/artifacts/MarkdownArtifact.js.map +1 -1
  135. package/dist/tools/artifacts/PdfArtifact.d.ts.map +1 -1
  136. package/dist/tools/artifacts/PdfArtifact.js +6 -3
  137. package/dist/tools/artifacts/PdfArtifact.js.map +1 -1
  138. package/dist/tools/artifacts/SvgArtifact.d.ts.map +1 -1
  139. package/dist/tools/artifacts/SvgArtifact.js +6 -3
  140. package/dist/tools/artifacts/SvgArtifact.js.map +1 -1
  141. package/dist/tools/artifacts/TextArtifact.d.ts.map +1 -1
  142. package/dist/tools/artifacts/TextArtifact.js +5 -2
  143. package/dist/tools/artifacts/TextArtifact.js.map +1 -1
  144. package/dist/tools/artifacts/artifacts-tool-renderer.d.ts.map +1 -1
  145. package/dist/tools/artifacts/artifacts-tool-renderer.js +0 -1
  146. package/dist/tools/artifacts/artifacts-tool-renderer.js.map +1 -1
  147. package/dist/tools/artifacts/artifacts.d.ts.map +1 -1
  148. package/dist/tools/artifacts/artifacts.js +12 -17
  149. package/dist/tools/artifacts/artifacts.js.map +1 -1
  150. package/dist/tools/extract-document.d.ts +1 -1
  151. package/dist/tools/extract-document.d.ts.map +1 -1
  152. package/dist/tools/extract-document.js.map +1 -1
  153. package/dist/tools/index.js.map +1 -1
  154. package/dist/tools/javascript-repl.d.ts +2 -2
  155. package/dist/tools/javascript-repl.d.ts.map +1 -1
  156. package/dist/tools/javascript-repl.js.map +1 -1
  157. package/dist/tools/renderer-registry.js.map +1 -1
  158. package/dist/tools/renderers/BashRenderer.d.ts.map +1 -1
  159. package/dist/tools/renderers/BashRenderer.js.map +1 -1
  160. package/dist/tools/renderers/CalculateRenderer.d.ts.map +1 -1
  161. package/dist/tools/renderers/CalculateRenderer.js.map +1 -1
  162. package/dist/tools/renderers/DefaultRenderer.d.ts.map +1 -1
  163. package/dist/tools/renderers/DefaultRenderer.js.map +1 -1
  164. package/dist/tools/renderers/GetCurrentTimeRenderer.d.ts.map +1 -1
  165. package/dist/tools/renderers/GetCurrentTimeRenderer.js.map +1 -1
  166. package/dist/utils/attachment-utils.js.map +1 -1
  167. package/dist/utils/auth-token.js.map +1 -1
  168. package/dist/utils/format.js.map +1 -1
  169. package/dist/utils/i18n.d.ts +14 -14
  170. package/dist/utils/i18n.d.ts.map +1 -1
  171. package/dist/utils/i18n.js.map +1 -1
  172. package/dist/utils/model-discovery.js.map +1 -1
  173. package/dist/utils/proxy-utils.d.ts +1 -1
  174. package/dist/utils/proxy-utils.d.ts.map +1 -1
  175. package/dist/utils/proxy-utils.js +5 -1
  176. package/dist/utils/proxy-utils.js.map +1 -1
  177. package/dist/utils/test-sessions.d.ts +47 -47
  178. package/dist/utils/test-sessions.js.map +1 -1
  179. package/package.json +5 -5
  180. package/src/ChatPanel.ts +2 -0
  181. package/src/components/AgentInterface.ts +15 -2
  182. package/src/components/MessageEditor.ts +3 -1
  183. package/src/components/ProviderKeyInput.ts +1 -1
  184. package/src/dialogs/ModelSelector.ts +72 -18
  185. package/src/dialogs/SettingsDialog.ts +5 -1
  186. package/src/index.ts +1 -0
  187. package/src/utils/proxy-utils.ts +6 -1
@@ -15,6 +15,37 @@ import { formatModelCost } from "../utils/format.js";
15
15
  import { i18n } from "../utils/i18n.js";
16
16
  import { discoverModels } from "../utils/model-discovery.js";
17
17
 
18
+ /**
19
+ * Score a query against a text using subsequence matching.
20
+ * All query characters must appear in order in the text.
21
+ * Higher score = tighter match (fewer gaps between matched characters).
22
+ * Returns 0 if no match.
23
+ */
24
+ function subsequenceScore(query: string, text: string): number {
25
+ let qi = 0;
26
+ let ti = 0;
27
+ let gaps = 0;
28
+ let lastMatchIndex = -1;
29
+
30
+ while (qi < query.length && ti < text.length) {
31
+ if (query[qi] === text[ti]) {
32
+ if (lastMatchIndex >= 0) {
33
+ gaps += ti - lastMatchIndex - 1;
34
+ }
35
+ lastMatchIndex = ti;
36
+ qi++;
37
+ }
38
+ ti++;
39
+ }
40
+
41
+ // All query chars must match
42
+ if (qi < query.length) return 0;
43
+
44
+ // Score: longer query match = better, fewer gaps = better
45
+ // Normalize so exact substring gets highest score
46
+ return query.length / (query.length + gaps);
47
+ }
48
+
18
49
  @customElement("agent-model-selector")
19
50
  export class ModelSelector extends DialogBase {
20
51
  @state() currentModel: Model<any> | null = null;
@@ -27,16 +58,24 @@ export class ModelSelector extends DialogBase {
27
58
  @state() private customProviderModels: Model<any>[] = [];
28
59
 
29
60
  private onSelectCallback?: (model: Model<any>) => void;
61
+ private allowedProviders?: Set<string>;
30
62
  private scrollContainerRef = createRef<HTMLDivElement>();
31
63
  private searchInputRef = createRef<HTMLInputElement>();
32
64
  private lastMousePosition = { x: 0, y: 0 };
33
65
 
34
66
  protected override modalWidth = "min(400px, 90vw)";
35
67
 
36
- static async open(currentModel: Model<any> | null, onSelect: (model: Model<any>) => void) {
68
+ static async open(
69
+ currentModel: Model<any> | null,
70
+ onSelect: (model: Model<any>) => void,
71
+ allowedProviders?: string[],
72
+ ) {
37
73
  const selector = new ModelSelector();
38
74
  selector.currentModel = currentModel;
39
75
  selector.onSelectCallback = onSelect;
76
+ if (allowedProviders) {
77
+ selector.allowedProviders = new Set(allowedProviders);
78
+ }
40
79
  selector.open();
41
80
  selector.loadCustomProviders();
42
81
  }
@@ -173,19 +212,30 @@ export class ModelSelector extends DialogBase {
173
212
  allModels.push({ provider: model.provider, id: model.id, model });
174
213
  }
175
214
 
215
+ // Filter by allowed providers if set
216
+ if (this.allowedProviders) {
217
+ const allowed = this.allowedProviders;
218
+ allModels.splice(0, allModels.length, ...allModels.filter(({ provider }) => allowed.has(provider)));
219
+ }
220
+
176
221
  // Filter models based on search and capability filters
177
222
  let filteredModels = allModels;
178
223
 
179
- // Apply search filter
224
+ // Apply search filter (subsequence match: characters must appear in order)
180
225
  if (this.searchQuery) {
181
- filteredModels = filteredModels.filter(({ provider, id, model }) => {
182
- const searchTokens = this.searchQuery
183
- .toLowerCase()
184
- .split(/\s+/)
185
- .filter((t) => t);
186
- const searchText = `${provider} ${id} ${model.name}`.toLowerCase();
187
- return searchTokens.every((token) => searchText.includes(token));
188
- });
226
+ const query = this.searchQuery.toLowerCase().replace(/\s+/g, "");
227
+ if (query) {
228
+ const scored: Array<{ item: (typeof allModels)[0]; score: number }> = [];
229
+ for (const entry of filteredModels) {
230
+ const searchText = `${entry.provider} ${entry.id} ${entry.model.name}`.toLowerCase();
231
+ const score = subsequenceScore(query, searchText);
232
+ if (score > 0) {
233
+ scored.push({ item: entry, score });
234
+ }
235
+ }
236
+ scored.sort((a, b) => b.score - a.score);
237
+ filteredModels = scored.map((s) => s.item);
238
+ }
189
239
  }
190
240
 
191
241
  // Apply capability filters
@@ -196,14 +246,18 @@ export class ModelSelector extends DialogBase {
196
246
  filteredModels = filteredModels.filter(({ model }) => model.input.includes("image"));
197
247
  }
198
248
 
199
- // Sort: current model first, then by provider
200
- filteredModels.sort((a, b) => {
201
- const aIsCurrent = modelsAreEqual(this.currentModel, a.model);
202
- const bIsCurrent = modelsAreEqual(this.currentModel, b.model);
203
- if (aIsCurrent && !bIsCurrent) return -1;
204
- if (!aIsCurrent && bIsCurrent) return 1;
205
- return a.provider.localeCompare(b.provider);
206
- });
249
+ // Sort: when not searching, current model first then by provider.
250
+ // When searching, preserve the score-based order from above,
251
+ // but still float the current model to the top.
252
+ if (!this.searchQuery) {
253
+ filteredModels.sort((a, b) => {
254
+ const aIsCurrent = modelsAreEqual(this.currentModel, a.model);
255
+ const bIsCurrent = modelsAreEqual(this.currentModel, b.model);
256
+ if (aIsCurrent && !bIsCurrent) return -1;
257
+ if (!aIsCurrent && bIsCurrent) return 1;
258
+ return a.provider.localeCompare(b.provider);
259
+ });
260
+ }
207
261
 
208
262
  return filteredModels;
209
263
  }
@@ -122,9 +122,12 @@ export class SettingsDialog extends LitElement {
122
122
  return this;
123
123
  }
124
124
 
125
- static async open(tabs: SettingsTab[]) {
125
+ private onCloseCallback?: () => void;
126
+
127
+ static async open(tabs: SettingsTab[], onClose?: () => void) {
126
128
  const dialog = new SettingsDialog();
127
129
  dialog.tabs = tabs;
130
+ dialog.onCloseCallback = onClose;
128
131
  dialog.isOpen = true;
129
132
  document.body.appendChild(dialog);
130
133
  }
@@ -173,6 +176,7 @@ export class SettingsDialog extends LitElement {
173
176
  onClose: () => {
174
177
  this.isOpen = false;
175
178
  this.remove();
179
+ this.onCloseCallback?.();
176
180
  },
177
181
  width: "min(1000px, 90vw)",
178
182
  height: "min(800px, 90vh)",
package/src/index.ts CHANGED
@@ -55,6 +55,7 @@ export type { SandboxRuntimeProvider } from "./components/sandbox/SandboxRuntime
55
55
  export { ThinkingBlock } from "./components/ThinkingBlock.js";
56
56
  export { ApiKeyPromptDialog } from "./dialogs/ApiKeyPromptDialog.js";
57
57
  export { AttachmentOverlay } from "./dialogs/AttachmentOverlay.js";
58
+ export { CustomProviderDialog } from "./dialogs/CustomProviderDialog.js";
58
59
  // Dialogs
59
60
  export { ModelSelector } from "./dialogs/ModelSelector.js";
60
61
  export { PersistentStorageDialog } from "./dialogs/PersistentStorageDialog.js";
@@ -25,7 +25,11 @@ export function shouldUseProxyForProvider(provider: string, apiKey: string): boo
25
25
  case "anthropic":
26
26
  // Anthropic OAuth tokens (sk-ant-oat-*) require proxy
27
27
  // Regular API keys (sk-ant-api-*) do NOT require proxy
28
- return apiKey.startsWith("sk-ant-oat");
28
+ return apiKey.startsWith("sk-ant-oat") || apiKey.startsWith("{");
29
+
30
+ case "openai-codex":
31
+ // Codex uses chatgpt.com/backend-api which has no CORS
32
+ return true;
29
33
 
30
34
  // These providers work without proxy
31
35
  case "openai":
@@ -36,6 +40,7 @@ export function shouldUseProxyForProvider(provider: string, apiKey: string): boo
36
40
  case "xai":
37
41
  case "ollama":
38
42
  case "lmstudio":
43
+ case "github-copilot":
39
44
  return false;
40
45
 
41
46
  // Unknown providers - assume no proxy needed