@mariozechner/pi-web-ui 0.5.48 → 0.7.1

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 (222) hide show
  1. package/dist/ChatPanel.d.ts +1 -0
  2. package/dist/ChatPanel.d.ts.map +1 -1
  3. package/dist/ChatPanel.js +3 -2
  4. package/dist/ChatPanel.js.map +1 -1
  5. package/dist/agent/transports/ProviderTransport.d.ts +1 -1
  6. package/dist/agent/transports/ProviderTransport.d.ts.map +1 -1
  7. package/dist/agent/transports/ProviderTransport.js +5 -10
  8. package/dist/agent/transports/ProviderTransport.js.map +1 -1
  9. package/dist/app.css +4188 -2
  10. package/dist/components/AgentInterface.d.ts +1 -0
  11. package/dist/components/AgentInterface.d.ts.map +1 -1
  12. package/dist/components/AgentInterface.js +13 -3
  13. package/dist/components/AgentInterface.js.map +1 -1
  14. package/dist/components/AttachmentTile.d.ts.map +1 -1
  15. package/dist/components/AttachmentTile.js +2 -1
  16. package/dist/components/AttachmentTile.js.map +1 -1
  17. package/dist/components/ConsoleBlock.d.ts.map +1 -1
  18. package/dist/components/ConsoleBlock.js +2 -1
  19. package/dist/components/ConsoleBlock.js.map +1 -1
  20. package/dist/components/CustomProviderCard.d.ts +17 -0
  21. package/dist/components/CustomProviderCard.d.ts.map +1 -0
  22. package/dist/components/CustomProviderCard.js +110 -0
  23. package/dist/components/CustomProviderCard.js.map +1 -0
  24. package/dist/components/Input.d.ts +2 -2
  25. package/dist/components/Input.d.ts.map +1 -1
  26. package/dist/components/Input.js +2 -1
  27. package/dist/components/Input.js.map +1 -1
  28. package/dist/components/MessageEditor.d.ts +1 -3
  29. package/dist/components/MessageEditor.d.ts.map +1 -1
  30. package/dist/components/MessageEditor.js +6 -31
  31. package/dist/components/MessageEditor.js.map +1 -1
  32. package/dist/components/MessageList.d.ts +1 -0
  33. package/dist/components/MessageList.d.ts.map +1 -1
  34. package/dist/components/MessageList.js +6 -3
  35. package/dist/components/MessageList.js.map +1 -1
  36. package/dist/components/Messages.d.ts +2 -0
  37. package/dist/components/Messages.d.ts.map +1 -1
  38. package/dist/components/Messages.js +25 -14
  39. package/dist/components/Messages.js.map +1 -1
  40. package/dist/components/ProviderKeyInput.d.ts +1 -1
  41. package/dist/components/ProviderKeyInput.d.ts.map +1 -1
  42. package/dist/components/ProviderKeyInput.js +22 -36
  43. package/dist/components/ProviderKeyInput.js.map +1 -1
  44. package/dist/components/StreamingMessageContainer.d.ts +1 -0
  45. package/dist/components/StreamingMessageContainer.d.ts.map +1 -1
  46. package/dist/components/StreamingMessageContainer.js +5 -2
  47. package/dist/components/StreamingMessageContainer.js.map +1 -1
  48. package/dist/components/ThinkingBlock.d.ts +11 -0
  49. package/dist/components/ThinkingBlock.d.ts.map +1 -0
  50. package/dist/components/ThinkingBlock.js +58 -0
  51. package/dist/components/ThinkingBlock.js.map +1 -0
  52. package/dist/dialogs/ApiKeyPromptDialog.d.ts +1 -1
  53. package/dist/dialogs/ApiKeyPromptDialog.d.ts.map +1 -1
  54. package/dist/dialogs/ApiKeyPromptDialog.js +3 -1
  55. package/dist/dialogs/ApiKeyPromptDialog.js.map +1 -1
  56. package/dist/dialogs/AttachmentOverlay.d.ts.map +1 -1
  57. package/dist/dialogs/AttachmentOverlay.js +3 -2
  58. package/dist/dialogs/AttachmentOverlay.js.map +1 -1
  59. package/dist/dialogs/CustomProviderDialog.d.ts +25 -0
  60. package/dist/dialogs/CustomProviderDialog.d.ts.map +1 -0
  61. package/dist/dialogs/CustomProviderDialog.js +270 -0
  62. package/dist/dialogs/CustomProviderDialog.js.map +1 -0
  63. package/dist/dialogs/ModelSelector.d.ts +6 -6
  64. package/dist/dialogs/ModelSelector.d.ts.map +1 -1
  65. package/dist/dialogs/ModelSelector.js +60 -74
  66. package/dist/dialogs/ModelSelector.js.map +1 -1
  67. package/dist/dialogs/PersistentStorageDialog.d.ts +1 -1
  68. package/dist/dialogs/PersistentStorageDialog.d.ts.map +1 -1
  69. package/dist/dialogs/PersistentStorageDialog.js +4 -1
  70. package/dist/dialogs/PersistentStorageDialog.js.map +1 -1
  71. package/dist/dialogs/ProvidersModelsTab.d.ts +20 -0
  72. package/dist/dialogs/ProvidersModelsTab.d.ts.map +1 -0
  73. package/dist/dialogs/ProvidersModelsTab.js +191 -0
  74. package/dist/dialogs/ProvidersModelsTab.js.map +1 -0
  75. package/dist/dialogs/SessionListDialog.d.ts +1 -1
  76. package/dist/dialogs/SessionListDialog.d.ts.map +1 -1
  77. package/dist/dialogs/SessionListDialog.js +3 -1
  78. package/dist/dialogs/SessionListDialog.js.map +1 -1
  79. package/dist/dialogs/SettingsDialog.d.ts +1 -2
  80. package/dist/dialogs/SettingsDialog.d.ts.map +1 -1
  81. package/dist/dialogs/SettingsDialog.js +10 -3
  82. package/dist/dialogs/SettingsDialog.js.map +1 -1
  83. package/dist/index.d.ts +4 -0
  84. package/dist/index.d.ts.map +1 -1
  85. package/dist/index.js +3 -0
  86. package/dist/index.js.map +1 -1
  87. package/dist/storage/app-storage.d.ts +3 -1
  88. package/dist/storage/app-storage.d.ts.map +1 -1
  89. package/dist/storage/app-storage.js +2 -1
  90. package/dist/storage/app-storage.js.map +1 -1
  91. package/dist/storage/stores/custom-providers-store.d.ts +25 -0
  92. package/dist/storage/stores/custom-providers-store.d.ts.map +1 -0
  93. package/dist/storage/stores/custom-providers-store.js +35 -0
  94. package/dist/storage/stores/custom-providers-store.js.map +1 -0
  95. package/dist/storage/stores/sessions-store.d.ts.map +1 -1
  96. package/dist/storage/stores/sessions-store.js +0 -1
  97. package/dist/storage/stores/sessions-store.js.map +1 -1
  98. package/dist/storage/types.d.ts +0 -2
  99. package/dist/storage/types.d.ts.map +1 -1
  100. package/dist/tools/artifacts/ArtifactPill.d.ts +1 -1
  101. package/dist/tools/artifacts/ArtifactPill.d.ts.map +1 -1
  102. package/dist/tools/artifacts/ArtifactPill.js +2 -1
  103. package/dist/tools/artifacts/ArtifactPill.js.map +1 -1
  104. package/dist/tools/artifacts/DocxArtifact.js +1 -1
  105. package/dist/tools/artifacts/DocxArtifact.js.map +1 -1
  106. package/dist/tools/artifacts/ExcelArtifact.js +1 -1
  107. package/dist/tools/artifacts/ExcelArtifact.js.map +1 -1
  108. package/dist/tools/artifacts/GenericArtifact.js +1 -1
  109. package/dist/tools/artifacts/GenericArtifact.js.map +1 -1
  110. package/dist/tools/artifacts/HtmlArtifact.d.ts.map +1 -1
  111. package/dist/tools/artifacts/HtmlArtifact.js +5 -1
  112. package/dist/tools/artifacts/HtmlArtifact.js.map +1 -1
  113. package/dist/tools/artifacts/ImageArtifact.js +1 -1
  114. package/dist/tools/artifacts/ImageArtifact.js.map +1 -1
  115. package/dist/tools/artifacts/MarkdownArtifact.d.ts.map +1 -1
  116. package/dist/tools/artifacts/MarkdownArtifact.js +3 -1
  117. package/dist/tools/artifacts/MarkdownArtifact.js.map +1 -1
  118. package/dist/tools/artifacts/PdfArtifact.js +1 -1
  119. package/dist/tools/artifacts/PdfArtifact.js.map +1 -1
  120. package/dist/tools/artifacts/SvgArtifact.d.ts.map +1 -1
  121. package/dist/tools/artifacts/SvgArtifact.js +3 -1
  122. package/dist/tools/artifacts/SvgArtifact.js.map +1 -1
  123. package/dist/tools/artifacts/TextArtifact.d.ts.map +1 -1
  124. package/dist/tools/artifacts/TextArtifact.js +2 -1
  125. package/dist/tools/artifacts/TextArtifact.js.map +1 -1
  126. package/dist/tools/artifacts/artifacts-tool-renderer.d.ts.map +1 -1
  127. package/dist/tools/artifacts/artifacts-tool-renderer.js +18 -8
  128. package/dist/tools/artifacts/artifacts-tool-renderer.js.map +1 -1
  129. package/dist/tools/artifacts/artifacts.d.ts.map +1 -1
  130. package/dist/tools/artifacts/artifacts.js +3 -2
  131. package/dist/tools/artifacts/artifacts.js.map +1 -1
  132. package/dist/tools/extract-document.d.ts.map +1 -1
  133. package/dist/tools/extract-document.js +78 -58
  134. package/dist/tools/extract-document.js.map +1 -1
  135. package/dist/tools/javascript-repl.d.ts.map +1 -1
  136. package/dist/tools/javascript-repl.js +7 -3
  137. package/dist/tools/javascript-repl.js.map +1 -1
  138. package/dist/tools/renderer-registry.d.ts +1 -1
  139. package/dist/tools/renderer-registry.d.ts.map +1 -1
  140. package/dist/tools/renderer-registry.js +20 -6
  141. package/dist/tools/renderer-registry.js.map +1 -1
  142. package/dist/tools/renderers/BashRenderer.d.ts.map +1 -1
  143. package/dist/tools/renderers/BashRenderer.js +5 -2
  144. package/dist/tools/renderers/BashRenderer.js.map +1 -1
  145. package/dist/tools/renderers/CalculateRenderer.d.ts.map +1 -1
  146. package/dist/tools/renderers/CalculateRenderer.js +5 -2
  147. package/dist/tools/renderers/CalculateRenderer.js.map +1 -1
  148. package/dist/tools/renderers/DefaultRenderer.d.ts.map +1 -1
  149. package/dist/tools/renderers/DefaultRenderer.js +5 -2
  150. package/dist/tools/renderers/DefaultRenderer.js.map +1 -1
  151. package/dist/tools/renderers/GetCurrentTimeRenderer.d.ts.map +1 -1
  152. package/dist/tools/renderers/GetCurrentTimeRenderer.js +9 -3
  153. package/dist/tools/renderers/GetCurrentTimeRenderer.js.map +1 -1
  154. package/dist/utils/auth-token.js +1 -1
  155. package/dist/utils/auth-token.js.map +1 -1
  156. package/dist/utils/i18n.d.ts +105 -3
  157. package/dist/utils/i18n.d.ts.map +1 -1
  158. package/dist/utils/i18n.js +72 -2
  159. package/dist/utils/i18n.js.map +1 -1
  160. package/dist/utils/model-discovery.d.ts +38 -0
  161. package/dist/utils/model-discovery.d.ts.map +1 -0
  162. package/dist/utils/model-discovery.js +243 -0
  163. package/dist/utils/model-discovery.js.map +1 -0
  164. package/dist/utils/proxy-utils.d.ts +37 -0
  165. package/dist/utils/proxy-utils.d.ts.map +1 -0
  166. package/dist/utils/proxy-utils.js +97 -0
  167. package/dist/utils/proxy-utils.js.map +1 -0
  168. package/example/package.json +2 -2
  169. package/example/src/custom-messages.ts +1 -1
  170. package/example/src/main.ts +17 -6
  171. package/package.json +9 -8
  172. package/src/ChatPanel.ts +4 -2
  173. package/src/agent/transports/ProviderTransport.ts +5 -10
  174. package/src/app.css +24 -0
  175. package/src/components/AgentInterface.ts +14 -3
  176. package/src/components/AttachmentTile.ts +2 -1
  177. package/src/components/ConsoleBlock.ts +2 -1
  178. package/src/components/CustomProviderCard.ts +100 -0
  179. package/src/components/Input.ts +2 -1
  180. package/src/components/MessageEditor.ts +6 -33
  181. package/src/components/MessageList.ts +4 -3
  182. package/src/components/Messages.ts +32 -20
  183. package/src/components/ProviderKeyInput.ts +19 -38
  184. package/src/components/StreamingMessageContainer.ts +3 -2
  185. package/src/components/ThinkingBlock.ts +43 -0
  186. package/src/dialogs/ApiKeyPromptDialog.ts +3 -1
  187. package/src/dialogs/AttachmentOverlay.ts +3 -2
  188. package/src/dialogs/CustomProviderDialog.ts +274 -0
  189. package/src/dialogs/ModelSelector.ts +61 -75
  190. package/src/dialogs/PersistentStorageDialog.ts +4 -1
  191. package/src/dialogs/ProvidersModelsTab.ts +212 -0
  192. package/src/dialogs/SessionListDialog.ts +3 -1
  193. package/src/dialogs/SettingsDialog.ts +10 -13
  194. package/src/index.ts +8 -0
  195. package/src/storage/app-storage.ts +4 -0
  196. package/src/storage/stores/custom-providers-store.ts +62 -0
  197. package/src/storage/stores/sessions-store.ts +0 -1
  198. package/src/storage/types.ts +0 -3
  199. package/src/tools/artifacts/ArtifactPill.ts +2 -1
  200. package/src/tools/artifacts/DocxArtifact.ts +1 -1
  201. package/src/tools/artifacts/ExcelArtifact.ts +1 -1
  202. package/src/tools/artifacts/GenericArtifact.ts +1 -1
  203. package/src/tools/artifacts/HtmlArtifact.ts +5 -1
  204. package/src/tools/artifacts/ImageArtifact.ts +1 -1
  205. package/src/tools/artifacts/MarkdownArtifact.ts +3 -1
  206. package/src/tools/artifacts/PdfArtifact.ts +1 -1
  207. package/src/tools/artifacts/SvgArtifact.ts +3 -1
  208. package/src/tools/artifacts/TextArtifact.ts +2 -1
  209. package/src/tools/artifacts/artifacts-tool-renderer.ts +20 -8
  210. package/src/tools/artifacts/artifacts.ts +3 -2
  211. package/src/tools/extract-document.ts +82 -61
  212. package/src/tools/javascript-repl.ts +8 -3
  213. package/src/tools/renderer-registry.ts +20 -6
  214. package/src/tools/renderers/BashRenderer.ts +6 -2
  215. package/src/tools/renderers/CalculateRenderer.ts +6 -2
  216. package/src/tools/renderers/DefaultRenderer.ts +6 -2
  217. package/src/tools/renderers/GetCurrentTimeRenderer.ts +11 -3
  218. package/src/utils/auth-token.ts +1 -1
  219. package/src/utils/i18n.ts +120 -5
  220. package/src/utils/model-discovery.ts +277 -0
  221. package/src/utils/proxy-utils.ts +112 -0
  222. package/example/package-lock.json +0 -1965
@@ -1,4 +1,6 @@
1
- import { DialogBase, DialogContent, DialogHeader, html } from "@mariozechner/mini-lit";
1
+ import { DialogContent, DialogHeader } from "@mariozechner/mini-lit/dist/Dialog.js";
2
+ import { DialogBase } from "@mariozechner/mini-lit/dist/DialogBase.js";
3
+ import { html } from "lit";
2
4
  import { customElement, state } from "lit/decorators.js";
3
5
  import { getAppStorage } from "../storage/app-storage.js";
4
6
  import type { SessionMetadata } from "../storage/types.js";
@@ -1,16 +1,10 @@
1
- import {
2
- Dialog,
3
- DialogContent,
4
- DialogHeader,
5
- html,
6
- Input,
7
- i18n,
8
- Label,
9
- Switch,
10
- type TemplateResult,
11
- } from "@mariozechner/mini-lit";
1
+ import { i18n } from "@mariozechner/mini-lit";
2
+ import { Dialog, DialogContent, DialogHeader } from "@mariozechner/mini-lit/dist/Dialog.js";
3
+ import { Input } from "@mariozechner/mini-lit/dist/Input.js";
4
+ import { Label } from "@mariozechner/mini-lit/dist/Label.js";
5
+ import { Switch } from "@mariozechner/mini-lit/dist/Switch.js";
12
6
  import { getProviders } from "@mariozechner/pi-ai";
13
- import { LitElement } from "lit";
7
+ import { html, LitElement, type TemplateResult } from "lit";
14
8
  import { customElement, property, state } from "lit/decorators.js";
15
9
  import "../components/ProviderKeyInput.js";
16
10
  import { getAppStorage } from "../storage/app-storage.js";
@@ -84,7 +78,7 @@ export class ProxyTab extends SettingsTab {
84
78
  return html`
85
79
  <div class="flex flex-col gap-4">
86
80
  <p class="text-sm text-muted-foreground">
87
- ${i18n("The CORS proxy strips CORS headers from API responses, allowing browser-based apps to make direct calls to LLM providers without CORS restrictions. It forwards requests to providers while removing headers that would otherwise block cross-origin requests.")}
81
+ ${i18n("Allows browser-based apps to bypass CORS restrictions when calling LLM providers. Required for Z-AI and Anthropic with OAuth token.")}
88
82
  </p>
89
83
 
90
84
  <div class="flex items-center justify-between">
@@ -109,6 +103,9 @@ export class ProxyTab extends SettingsTab {
109
103
  },
110
104
  onChange: () => this.saveProxySettings(),
111
105
  })}
106
+ <p class="text-xs text-muted-foreground">
107
+ ${i18n("Format: The proxy must accept requests as <proxy-url>/?url=<target-url>")}
108
+ </p>
112
109
  </div>
113
110
  </div>
114
111
  `;
package/src/index.ts CHANGED
@@ -46,11 +46,13 @@ export {
46
46
  export { RuntimeMessageBridge } from "./components/sandbox/RuntimeMessageBridge.js";
47
47
  export { RUNTIME_MESSAGE_ROUTER } from "./components/sandbox/RuntimeMessageRouter.js";
48
48
  export type { SandboxRuntimeProvider } from "./components/sandbox/SandboxRuntimeProvider.js";
49
+ export { ThinkingBlock } from "./components/ThinkingBlock.js";
49
50
  export { ApiKeyPromptDialog } from "./dialogs/ApiKeyPromptDialog.js";
50
51
  export { AttachmentOverlay } from "./dialogs/AttachmentOverlay.js";
51
52
  // Dialogs
52
53
  export { ModelSelector } from "./dialogs/ModelSelector.js";
53
54
  export { PersistentStorageDialog } from "./dialogs/PersistentStorageDialog.js";
55
+ export { ProvidersModelsTab } from "./dialogs/ProvidersModelsTab.js";
54
56
  export { SessionListDialog } from "./dialogs/SessionListDialog.js";
55
57
  export { ApiKeysTab, ProxyTab, SettingsDialog, SettingsTab } from "./dialogs/SettingsDialog.js";
56
58
  // Prompts
@@ -63,6 +65,12 @@ export {
63
65
  export { AppStorage, getAppStorage, setAppStorage } from "./storage/app-storage.js";
64
66
  export { IndexedDBStorageBackend } from "./storage/backends/indexeddb-storage-backend.js";
65
67
  export { Store } from "./storage/store.js";
68
+ export type {
69
+ AutoDiscoveryProviderType,
70
+ CustomProvider,
71
+ CustomProviderType,
72
+ } from "./storage/stores/custom-providers-store.js";
73
+ export { CustomProvidersStore } from "./storage/stores/custom-providers-store.js";
66
74
  export { ProviderKeysStore } from "./storage/stores/provider-keys-store.js";
67
75
  export { SessionsStore } from "./storage/stores/sessions-store.js";
68
76
  export { SettingsStore } from "./storage/stores/settings-store.js";
@@ -1,3 +1,4 @@
1
+ import type { CustomProvidersStore } from "./stores/custom-providers-store.js";
1
2
  import type { ProviderKeysStore } from "./stores/provider-keys-store.js";
2
3
  import type { SessionsStore } from "./stores/sessions-store.js";
3
4
  import type { SettingsStore } from "./stores/settings-store.js";
@@ -12,16 +13,19 @@ export class AppStorage {
12
13
  readonly settings: SettingsStore;
13
14
  readonly providerKeys: ProviderKeysStore;
14
15
  readonly sessions: SessionsStore;
16
+ readonly customProviders: CustomProvidersStore;
15
17
 
16
18
  constructor(
17
19
  settings: SettingsStore,
18
20
  providerKeys: ProviderKeysStore,
19
21
  sessions: SessionsStore,
22
+ customProviders: CustomProvidersStore,
20
23
  backend: StorageBackend,
21
24
  ) {
22
25
  this.settings = settings;
23
26
  this.providerKeys = providerKeys;
24
27
  this.sessions = sessions;
28
+ this.customProviders = customProviders;
25
29
  this.backend = backend;
26
30
  }
27
31
 
@@ -0,0 +1,62 @@
1
+ import type { Model } from "@mariozechner/pi-ai";
2
+ import { Store } from "../store.js";
3
+ import type { StoreConfig } from "../types.js";
4
+
5
+ export type AutoDiscoveryProviderType = "ollama" | "llama.cpp" | "vllm" | "lmstudio";
6
+
7
+ export type CustomProviderType =
8
+ | AutoDiscoveryProviderType // Auto-discovery - models fetched on-demand
9
+ | "openai-completions" // Manual models - stored in provider.models
10
+ | "openai-responses" // Manual models - stored in provider.models
11
+ | "anthropic-messages"; // Manual models - stored in provider.models
12
+
13
+ export interface CustomProvider {
14
+ id: string; // UUID
15
+ name: string; // Display name, also used as Model.provider
16
+ type: CustomProviderType;
17
+ baseUrl: string;
18
+ apiKey?: string; // Optional, applies to all models
19
+
20
+ // For manual types ONLY - models stored directly on provider
21
+ // Auto-discovery types: models fetched on-demand, never stored
22
+ models?: Model<any>[];
23
+ }
24
+
25
+ /**
26
+ * Store for custom LLM providers (auto-discovery servers + manual providers).
27
+ */
28
+ export class CustomProvidersStore extends Store {
29
+ getConfig(): StoreConfig {
30
+ return {
31
+ name: "custom-providers",
32
+ };
33
+ }
34
+
35
+ async get(id: string): Promise<CustomProvider | null> {
36
+ return this.getBackend().get("custom-providers", id);
37
+ }
38
+
39
+ async set(provider: CustomProvider): Promise<void> {
40
+ await this.getBackend().set("custom-providers", provider.id, provider);
41
+ }
42
+
43
+ async delete(id: string): Promise<void> {
44
+ await this.getBackend().delete("custom-providers", id);
45
+ }
46
+
47
+ async getAll(): Promise<CustomProvider[]> {
48
+ const keys = await this.getBackend().keys("custom-providers");
49
+ const providers: CustomProvider[] = [];
50
+ for (const key of keys) {
51
+ const provider = await this.get(key);
52
+ if (provider) {
53
+ providers.push(provider);
54
+ }
55
+ }
56
+ return providers;
57
+ }
58
+
59
+ async has(id: string): Promise<boolean> {
60
+ return this.getBackend().has("custom-providers", id);
61
+ }
62
+ }
@@ -103,7 +103,6 @@ export class SessionsStore extends Store {
103
103
  cacheWrite: 0,
104
104
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
105
105
  },
106
- modelId: state.model?.id || null,
107
106
  thinkingLevel: state.thinkingLevel || "off",
108
107
  preview: "",
109
108
  };
@@ -128,9 +128,6 @@ export interface SessionMetadata {
128
128
  };
129
129
  };
130
130
 
131
- /** Last used model ID (e.g., "claude-sonnet-4") */
132
- modelId: string | null;
133
-
134
131
  /** Last used thinking level */
135
132
  thinkingLevel: ThinkingLevel;
136
133
 
@@ -1,4 +1,5 @@
1
- import { html, icon, type TemplateResult } from "@mariozechner/mini-lit";
1
+ import { icon } from "@mariozechner/mini-lit";
2
+ import { html, type TemplateResult } from "lit";
2
3
  import { FileCode2 } from "lucide";
3
4
  import type { ArtifactsPanel } from "./artifacts.js";
4
5
 
@@ -1,4 +1,4 @@
1
- import { DownloadButton } from "@mariozechner/mini-lit";
1
+ import { DownloadButton } from "@mariozechner/mini-lit/dist/DownloadButton.js";
2
2
  import { renderAsync } from "docx-preview";
3
3
  import { html, type TemplateResult } from "lit";
4
4
  import { customElement, property, state } from "lit/decorators.js";
@@ -1,4 +1,4 @@
1
- import { DownloadButton } from "@mariozechner/mini-lit";
1
+ import { DownloadButton } from "@mariozechner/mini-lit/dist/DownloadButton.js";
2
2
  import { html, type TemplateResult } from "lit";
3
3
  import { customElement, property, state } from "lit/decorators.js";
4
4
  import * as XLSX from "xlsx";
@@ -1,4 +1,4 @@
1
- import { DownloadButton } from "@mariozechner/mini-lit";
1
+ import { DownloadButton } from "@mariozechner/mini-lit/dist/DownloadButton.js";
2
2
  import { html, type TemplateResult } from "lit";
3
3
  import { customElement, property } from "lit/decorators.js";
4
4
  import { i18n } from "../../utils/i18n.js";
@@ -1,4 +1,3 @@
1
- import { Button, CopyButton, DownloadButton, icon, PreviewCodeToggle } from "@mariozechner/mini-lit";
2
1
  import hljs from "highlight.js";
3
2
  import { html } from "lit";
4
3
  import { customElement, property, state } from "lit/decorators.js";
@@ -13,6 +12,11 @@ import "../../components/SandboxedIframe.js";
13
12
  import { ArtifactElement } from "./ArtifactElement.js";
14
13
  import type { Console } from "./Console.js";
15
14
  import "./Console.js";
15
+ import { icon } from "@mariozechner/mini-lit";
16
+ import { Button } from "@mariozechner/mini-lit/dist/Button.js";
17
+ import { CopyButton } from "@mariozechner/mini-lit/dist/CopyButton.js";
18
+ import { DownloadButton } from "@mariozechner/mini-lit/dist/DownloadButton.js";
19
+ import { PreviewCodeToggle } from "@mariozechner/mini-lit/dist/PreviewCodeToggle.js";
16
20
 
17
21
  @customElement("html-artifact")
18
22
  export class HtmlArtifact extends ArtifactElement {
@@ -1,4 +1,4 @@
1
- import { DownloadButton } from "@mariozechner/mini-lit";
1
+ import { DownloadButton } from "@mariozechner/mini-lit/dist/DownloadButton.js";
2
2
  import { html, type TemplateResult } from "lit";
3
3
  import { customElement, property } from "lit/decorators.js";
4
4
  import { i18n } from "../../utils/i18n.js";
@@ -1,10 +1,12 @@
1
- import { CopyButton, DownloadButton, PreviewCodeToggle } from "@mariozechner/mini-lit";
2
1
  import hljs from "highlight.js";
3
2
  import { html } from "lit";
4
3
  import { customElement, property, state } from "lit/decorators.js";
5
4
  import { unsafeHTML } from "lit/directives/unsafe-html.js";
6
5
  import { i18n } from "../../utils/i18n.js";
7
6
  import "@mariozechner/mini-lit/dist/MarkdownBlock.js";
7
+ import { CopyButton } from "@mariozechner/mini-lit/dist/CopyButton.js";
8
+ import { DownloadButton } from "@mariozechner/mini-lit/dist/DownloadButton.js";
9
+ import { PreviewCodeToggle } from "@mariozechner/mini-lit/dist/PreviewCodeToggle.js";
8
10
  import { ArtifactElement } from "./ArtifactElement.js";
9
11
 
10
12
  @customElement("markdown-artifact")
@@ -1,4 +1,4 @@
1
- import { DownloadButton } from "@mariozechner/mini-lit";
1
+ import { DownloadButton } from "@mariozechner/mini-lit/dist/DownloadButton.js";
2
2
  import { html, type TemplateResult } from "lit";
3
3
  import { customElement, property, state } from "lit/decorators.js";
4
4
  import * as pdfjsLib from "pdfjs-dist";
@@ -1,4 +1,6 @@
1
- import { CopyButton, DownloadButton, PreviewCodeToggle } from "@mariozechner/mini-lit";
1
+ import { CopyButton } from "@mariozechner/mini-lit/dist/CopyButton.js";
2
+ import { DownloadButton } from "@mariozechner/mini-lit/dist/DownloadButton.js";
3
+ import { PreviewCodeToggle } from "@mariozechner/mini-lit/dist/PreviewCodeToggle.js";
2
4
  import hljs from "highlight.js";
3
5
  import { html } from "lit";
4
6
  import { customElement, property, state } from "lit/decorators.js";
@@ -1,4 +1,5 @@
1
- import { CopyButton, DownloadButton } from "@mariozechner/mini-lit";
1
+ import { CopyButton } from "@mariozechner/mini-lit/dist/CopyButton.js";
2
+ import { DownloadButton } from "@mariozechner/mini-lit/dist/DownloadButton.js";
2
3
  import hljs from "highlight.js";
3
4
  import { html } from "lit";
4
5
  import { customElement, property } from "lit/decorators.js";
@@ -1,15 +1,27 @@
1
- import { Diff, html, type TemplateResult } from "@mariozechner/mini-lit";
2
1
  import "@mariozechner/mini-lit/dist/CodeBlock.js";
3
2
  import type { ToolResultMessage } from "@mariozechner/pi-ai";
4
3
  import { createRef, ref } from "lit/directives/ref.js";
5
4
  import { FileCode2 } from "lucide";
6
5
  import "../../components/ConsoleBlock.js";
6
+ import { Diff } from "@mariozechner/mini-lit/dist/Diff.js";
7
+ import { html, type TemplateResult } from "lit";
7
8
  import { i18n } from "../../utils/i18n.js";
8
9
  import { renderCollapsibleHeader, renderHeader } from "../renderer-registry.js";
9
10
  import type { ToolRenderer, ToolRenderResult } from "../types.js";
10
11
  import { ArtifactPill } from "./ArtifactPill.js";
11
12
  import type { ArtifactsPanel, ArtifactsParams } from "./artifacts.js";
12
13
 
14
+ // Helper to extract text from content blocks
15
+ function getTextOutput(result: ToolResultMessage<any> | undefined): string {
16
+ if (!result) return "";
17
+ return (
18
+ result.content
19
+ ?.filter((c) => c.type === "text")
20
+ .map((c: any) => c.text)
21
+ .join("\n") || ""
22
+ );
23
+ }
24
+
13
25
  // Helper to determine language for syntax highlighting
14
26
  function getLanguageFromFilename(filename?: string): string {
15
27
  if (!filename) return "text";
@@ -109,8 +121,8 @@ export class ArtifactsToolRenderer implements ToolRenderer<ArtifactsParams, unde
109
121
  ${isDiff ? diffContent : content ? html`<code-block .code=${content} language=${getLanguageFromFilename(filename)}></code-block>` : ""}
110
122
  ${
111
123
  isHtml
112
- ? html`<console-block .content=${result.output || i18n("An error occurred")} variant="error"></console-block>`
113
- : html`<div class="text-sm text-destructive">${result.output || i18n("An error occurred")}</div>`
124
+ ? html`<console-block .content=${getTextOutput(result) || i18n("An error occurred")} variant="error"></console-block>`
125
+ : html`<div class="text-sm text-destructive">${getTextOutput(result) || i18n("An error occurred")}</div>`
114
126
  }
115
127
  </div>
116
128
  </div>
@@ -124,7 +136,7 @@ export class ArtifactsToolRenderer implements ToolRenderer<ArtifactsParams, unde
124
136
  content: html`
125
137
  <div class="space-y-3">
126
138
  ${renderHeader(state, FileCode2, headerText)}
127
- <div class="text-sm text-destructive">${result.output || i18n("An error occurred")}</div>
139
+ <div class="text-sm text-destructive">${getTextOutput(result) || i18n("An error occurred")}</div>
128
140
  </div>
129
141
  `,
130
142
  isCustom: false,
@@ -141,7 +153,7 @@ export class ArtifactsToolRenderer implements ToolRenderer<ArtifactsParams, unde
141
153
 
142
154
  // GET command: show code block with file content
143
155
  if (command === "get") {
144
- const fileContent = result.output || i18n("(no output)");
156
+ const fileContent = getTextOutput(result) || i18n("(no output)");
145
157
  return {
146
158
  content: html`
147
159
  <div>
@@ -157,7 +169,7 @@ export class ArtifactsToolRenderer implements ToolRenderer<ArtifactsParams, unde
157
169
 
158
170
  // LOGS command: show console block
159
171
  if (command === "logs") {
160
- const logs = result.output || i18n("(no output)");
172
+ const logs = getTextOutput(result) || i18n("(no output)");
161
173
  return {
162
174
  content: html`
163
175
  <div>
@@ -175,7 +187,7 @@ export class ArtifactsToolRenderer implements ToolRenderer<ArtifactsParams, unde
175
187
  if (command === "create" || command === "rewrite") {
176
188
  const codeContent = content || "";
177
189
  const isHtml = filename?.endsWith(".html");
178
- const logs = result.output || "";
190
+ const logs = getTextOutput(result) || "";
179
191
 
180
192
  return {
181
193
  content: html`
@@ -193,7 +205,7 @@ export class ArtifactsToolRenderer implements ToolRenderer<ArtifactsParams, unde
193
205
 
194
206
  if (command === "update") {
195
207
  const isHtml = filename?.endsWith(".html");
196
- const logs = result.output || "";
208
+ const logs = getTextOutput(result) || "";
197
209
  return {
198
210
  content: html`
199
211
  <div>
@@ -1,5 +1,6 @@
1
- import { Button, icon } from "@mariozechner/mini-lit";
1
+ import { icon } from "@mariozechner/mini-lit";
2
2
  import "@mariozechner/mini-lit/dist/MarkdownBlock.js";
3
+ import { Button } from "@mariozechner/mini-lit/dist/Button.js";
3
4
  import { type AgentTool, type Message, StringEnum, type ToolCall } from "@mariozechner/pi-ai";
4
5
  import { type Static, Type } from "@sinclair/typebox";
5
6
  import { html, LitElement, type TemplateResult } from "lit";
@@ -284,7 +285,7 @@ export class ArtifactsPanel extends LitElement {
284
285
  // Execute mutates our local store and returns a plain output
285
286
  execute: async (_toolCallId: string, args: Static<typeof artifactsParamsSchema>, _signal?: AbortSignal) => {
286
287
  const output = await this.executeCommand(args);
287
- return { output, details: undefined };
288
+ return { content: [{ type: "text", text: output }], details: undefined };
288
289
  },
289
290
  };
290
291
  }
@@ -1,10 +1,11 @@
1
- import { html } from "@mariozechner/mini-lit";
2
1
  import type { AgentTool, ToolResultMessage } from "@mariozechner/pi-ai";
3
2
  import { type Static, Type } from "@sinclair/typebox";
3
+ import { html } from "lit";
4
4
  import { createRef, ref } from "lit/directives/ref.js";
5
5
  import { FileText } from "lucide";
6
6
  import { EXTRACT_DOCUMENT_DESCRIPTION } from "../prompts/prompts.js";
7
7
  import { loadAttachment } from "../utils/attachment-utils.js";
8
+ import { isCorsError } from "../utils/proxy-utils.js";
8
9
  import { registerToolRenderer, renderCollapsibleHeader, renderHeader } from "./renderer-registry.js";
9
10
  import type { ToolRenderer, ToolRenderResult } from "./types.js";
10
11
 
@@ -34,13 +35,13 @@ export interface ExtractDocumentResult {
34
35
  export function createExtractDocumentTool(): AgentTool<typeof extractDocumentSchema, ExtractDocumentResult> & {
35
36
  corsProxyUrl?: string;
36
37
  } {
37
- return {
38
+ const tool = {
38
39
  label: "Extract Document",
39
40
  name: "extract_document",
40
- corsProxyUrl: undefined, // Can be set by consumer (e.g., from user settings)
41
+ corsProxyUrl: undefined as string | undefined, // Can be set by consumer (e.g., from user settings)
41
42
  description: EXTRACT_DOCUMENT_DESCRIPTION,
42
43
  parameters: extractDocumentSchema,
43
- execute: async function (_toolCallId: string, args: ExtractDocumentParams, signal?: AbortSignal) {
44
+ execute: async (_toolCallId: string, args: ExtractDocumentParams, signal?: AbortSignal) => {
44
45
  if (signal?.aborted) {
45
46
  throw new Error("Extract document aborted");
46
47
  }
@@ -57,17 +58,11 @@ export function createExtractDocumentTool(): AgentTool<typeof extractDocumentSch
57
58
  throw new Error(`Invalid URL: ${url}`);
58
59
  }
59
60
 
60
- // Determine fetch URL (with or without CORS proxy)
61
- let fetchUrl = url;
62
- if (this.corsProxyUrl) {
63
- fetchUrl = this.corsProxyUrl + encodeURIComponent(url);
64
- }
65
-
66
61
  // Size limit: 50MB
67
62
  const MAX_SIZE = 50 * 1024 * 1024;
68
63
 
69
- try {
70
- // Attempt to fetch the document
64
+ // Helper function to fetch and process document
65
+ const fetchAndProcess = async (fetchUrl: string) => {
71
66
  const response = await fetch(fetchUrl, { signal });
72
67
 
73
68
  if (!response.ok) {
@@ -98,52 +93,31 @@ export function createExtractDocumentTool(): AgentTool<typeof extractDocumentSch
98
93
  );
99
94
  }
100
95
 
101
- // Extract filename from URL
102
- const urlParts = url.split("/");
103
- let fileName = urlParts[urlParts.length - 1]?.split("?")[0] || "document";
104
- if (url.startsWith("https://arxiv.org/")) {
105
- fileName = fileName + ".pdf";
106
- }
107
-
108
- // Use loadAttachment to process the document
109
- const attachment = await loadAttachment(arrayBuffer, fileName);
110
-
111
- if (!attachment.extractedText) {
112
- const mimeType = response.headers.get("content-type") || "unknown";
113
- throw new Error(
114
- `Document format not supported. Supported formats:\n` +
115
- `- PDF (.pdf)\n` +
116
- `- Word (.docx)\n` +
117
- `- Excel (.xlsx, .xls)\n` +
118
- `- PowerPoint (.pptx)\n\n` +
119
- `Detected: ${mimeType}`,
120
- );
121
- }
96
+ return arrayBuffer;
97
+ };
122
98
 
123
- // Determine format from attachment
124
- let format = "unknown";
125
- if (attachment.mimeType.includes("pdf")) {
126
- format = "pdf";
127
- } else if (attachment.mimeType.includes("wordprocessingml")) {
128
- format = "docx";
129
- } else if (attachment.mimeType.includes("spreadsheetml") || attachment.mimeType.includes("ms-excel")) {
130
- format = "xlsx";
131
- } else if (attachment.mimeType.includes("presentationml")) {
132
- format = "pptx";
133
- }
99
+ // Try without proxy first, fallback to proxy on CORS error
100
+ let arrayBuffer: ArrayBuffer;
134
101
 
135
- return {
136
- output: attachment.extractedText,
137
- details: {
138
- extractedText: attachment.extractedText,
139
- format,
140
- fileName: attachment.fileName,
141
- size: attachment.size,
142
- },
143
- };
144
- } catch (error: any) {
145
- // Handle CORS errors specifically
146
- if (error.name === "TypeError" && error.message.includes("Failed to fetch")) {
102
+ try {
103
+ // Attempt direct fetch first
104
+ arrayBuffer = await fetchAndProcess(url);
105
+ } catch (directError: any) {
106
+ // If CORS error and proxy is available, retry with proxy
107
+ if (isCorsError(directError) && tool.corsProxyUrl) {
108
+ try {
109
+ const proxiedUrl = tool.corsProxyUrl + encodeURIComponent(url);
110
+ arrayBuffer = await fetchAndProcess(proxiedUrl);
111
+ } catch (proxyError: any) {
112
+ // Proxy fetch also failed - throw helpful message
113
+ throw new Error(
114
+ `TELL USER: Unable to fetch the document due to CORS restrictions.\n\n` +
115
+ `Tried with proxy but it also failed: ${proxyError.message}\n\n` +
116
+ `INSTRUCT USER: Please download the file manually and attach it to your message using the attachment button (paperclip icon) in the message input area. I can then extract the text from the attached file.`,
117
+ );
118
+ }
119
+ } else if (isCorsError(directError) && !tool.corsProxyUrl) {
120
+ // CORS error but no proxy configured
147
121
  throw new Error(
148
122
  `TELL USER: Unable to fetch the document due to CORS restrictions (the server blocks requests from browser extensions).\n\n` +
149
123
  `To fix this, you need to configure a CORS proxy in Sitegeist settings:\n` +
@@ -151,15 +125,58 @@ export function createExtractDocumentTool(): AgentTool<typeof extractDocumentSch
151
125
  `2. Find "CORS Proxy URL" setting\n` +
152
126
  `3. Enter a proxy URL like: https://corsproxy.io/?\n` +
153
127
  `4. Save and try again\n\n` +
154
- `Would you like me to explain what a CORS proxy is and how to set one up?`,
128
+ `Alternatively, download the file manually and attach it to your message using the attachment button (paperclip icon).`,
155
129
  );
130
+ } else {
131
+ // Not a CORS error - re-throw
132
+ throw directError;
156
133
  }
134
+ }
135
+
136
+ // Extract filename from URL
137
+ const urlParts = url.split("/");
138
+ let fileName = urlParts[urlParts.length - 1]?.split("?")[0] || "document";
139
+ if (url.startsWith("https://arxiv.org/")) {
140
+ fileName = fileName + ".pdf";
141
+ }
157
142
 
158
- // Re-throw other errors
159
- throw error;
143
+ // Use loadAttachment to process the document
144
+ const attachment = await loadAttachment(arrayBuffer, fileName);
145
+
146
+ if (!attachment.extractedText) {
147
+ throw new Error(
148
+ `Document format not supported. Supported formats:\n` +
149
+ `- PDF (.pdf)\n` +
150
+ `- Word (.docx)\n` +
151
+ `- Excel (.xlsx, .xls)\n` +
152
+ `- PowerPoint (.pptx)`,
153
+ );
160
154
  }
155
+
156
+ // Determine format from attachment
157
+ let format = "unknown";
158
+ if (attachment.mimeType.includes("pdf")) {
159
+ format = "pdf";
160
+ } else if (attachment.mimeType.includes("wordprocessingml")) {
161
+ format = "docx";
162
+ } else if (attachment.mimeType.includes("spreadsheetml") || attachment.mimeType.includes("ms-excel")) {
163
+ format = "xlsx";
164
+ } else if (attachment.mimeType.includes("presentationml")) {
165
+ format = "pptx";
166
+ }
167
+
168
+ return {
169
+ content: [{ type: "text" as const, text: attachment.extractedText }],
170
+ details: {
171
+ extractedText: attachment.extractedText,
172
+ format,
173
+ fileName: attachment.fileName,
174
+ size: attachment.size,
175
+ },
176
+ };
161
177
  },
162
178
  };
179
+ return tool;
163
180
  }
164
181
 
165
182
  // Export a default instance
@@ -193,7 +210,11 @@ export const extractDocumentRenderer: ToolRenderer<ExtractDocumentParams, Extrac
193
210
  ? "Failed to extract document"
194
211
  : "Extracted text from document";
195
212
 
196
- const output = result.output || "";
213
+ const output =
214
+ result.content
215
+ ?.filter((c) => c.type === "text")
216
+ .map((c: any) => c.text)
217
+ .join("\n") || "";
197
218
 
198
219
  return {
199
220
  content: html`
@@ -214,7 +235,7 @@ export const extractDocumentRenderer: ToolRenderer<ExtractDocumentParams, Extrac
214
235
  }
215
236
  ${
216
237
  result.isError && output
217
- ? html`<console-block .content=${output} .variant="error"></console-block>`
238
+ ? html`<console-block .content=${output} .variant=${"error"}></console-block>`
218
239
  : ""
219
240
  }
220
241
  </div>
@@ -1,6 +1,7 @@
1
- import { html, i18n } from "@mariozechner/mini-lit";
1
+ import { i18n } from "@mariozechner/mini-lit";
2
2
  import type { AgentTool, ToolResultMessage } from "@mariozechner/pi-ai";
3
3
  import { type Static, Type } from "@sinclair/typebox";
4
+ import { html } from "lit";
4
5
  import { createRef, ref } from "lit/directives/ref.js";
5
6
  import { Code } from "lucide";
6
7
  import { type SandboxFile, SandboxIframe, type SandboxResult } from "../components/SandboxedIframe.js";
@@ -187,7 +188,7 @@ export function createJavaScriptReplTool(): AgentTool<typeof javascriptReplSchem
187
188
  contentBase64: base64,
188
189
  };
189
190
  });
190
- return { output: result.output, details: { files } };
191
+ return { content: [{ type: "text", text: result.output }], details: { files } };
191
192
  },
192
193
  };
193
194
  }
@@ -210,7 +211,11 @@ export const javascriptReplRenderer: ToolRenderer<JavaScriptReplParams, JavaScri
210
211
 
211
212
  // With result: show params + result
212
213
  if (result && params) {
213
- const output = result.output || "";
214
+ const output =
215
+ result.content
216
+ ?.filter((c) => c.type === "text")
217
+ .map((c: any) => c.text)
218
+ .join("\n") || "";
214
219
  const files = result.details?.files || [];
215
220
 
216
221
  const attachments: Attachment[] = files.map((f, i) => {