@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,6 +1,5 @@
1
- import { html } from "@mariozechner/mini-lit";
2
1
  import type { ToolResultMessage, Usage } from "@mariozechner/pi-ai";
3
- import { LitElement } from "lit";
2
+ import { html, LitElement } from "lit";
4
3
  import { customElement, property, query } from "lit/decorators.js";
5
4
  import { ModelSelector } from "../dialogs/ModelSelector.js";
6
5
  import type { MessageEditor } from "./MessageEditor.js";
@@ -29,6 +28,8 @@ export class AgentInterface extends LitElement {
29
28
  @property({ attribute: false }) onBeforeSend?: () => void | Promise<void>;
30
29
  // Optional callback called before executing a tool call - return false to prevent execution
31
30
  @property({ attribute: false }) onBeforeToolCall?: (toolName: string, args: any) => boolean | Promise<boolean>;
31
+ // Optional callback called when cost display is clicked
32
+ @property({ attribute: false }) onCostClick?: () => void;
32
33
 
33
34
  // References
34
35
  @query("message-editor") private _messageEditor!: MessageEditor;
@@ -226,6 +227,7 @@ export class AgentInterface extends LitElement {
226
227
  .tools=${state.tools}
227
228
  .pendingToolCalls=${this.session ? this.session.state.pendingToolCalls : new Set<string>()}
228
229
  .isStreaming=${state.isStreaming}
230
+ .onCostClick=${this.onCostClick}
229
231
  ></message-list>
230
232
 
231
233
  <!-- Streaming message container - manages its own updates -->
@@ -235,6 +237,7 @@ export class AgentInterface extends LitElement {
235
237
  .isStreaming=${state.isStreaming}
236
238
  .pendingToolCalls=${state.pendingToolCalls}
237
239
  .toolResultsById=${toolResultsById}
240
+ .onCostClick=${this.onCostClick}
238
241
  ></streaming-message-container>
239
242
  </div>
240
243
  `;
@@ -275,7 +278,15 @@ export class AgentInterface extends LitElement {
275
278
  <div class="flex items-center gap-1">
276
279
  ${this.showThemeToggle ? html`<theme-toggle></theme-toggle>` : html``}
277
280
  </div>
278
- <div class="flex ml-auto items-center gap-3">${totalsText ? html`<span>${totalsText}</span>` : ""}</div>
281
+ <div class="flex ml-auto items-center gap-3">
282
+ ${
283
+ totalsText
284
+ ? this.onCostClick
285
+ ? html`<span class="cursor-pointer hover:text-foreground transition-colors" @click=${this.onCostClick}>${totalsText}</span>`
286
+ : html`<span>${totalsText}</span>`
287
+ : ""
288
+ }
289
+ </div>
279
290
  </div>
280
291
  `;
281
292
  }
@@ -1,6 +1,7 @@
1
- import { html, icon } from "@mariozechner/mini-lit";
1
+ import { icon } from "@mariozechner/mini-lit/dist/icons.js";
2
2
  import { LitElement } from "lit";
3
3
  import { customElement, property } from "lit/decorators.js";
4
+ import { html } from "lit/html.js";
4
5
  import { FileSpreadsheet, FileText, X } from "lucide";
5
6
  import { AttachmentOverlay } from "../dialogs/AttachmentOverlay.js";
6
7
  import type { Attachment } from "../utils/attachment-utils.js";
@@ -1,6 +1,7 @@
1
- import { html, icon } from "@mariozechner/mini-lit";
1
+ import { icon } from "@mariozechner/mini-lit";
2
2
  import { LitElement } from "lit";
3
3
  import { property, state } from "lit/decorators.js";
4
+ import { html } from "lit/html.js";
4
5
  import { Check, Copy } from "lucide";
5
6
  import { i18n } from "../utils/i18n.js";
6
7
 
@@ -0,0 +1,100 @@
1
+ import { i18n } from "@mariozechner/mini-lit";
2
+ import { Button } from "@mariozechner/mini-lit/dist/Button.js";
3
+ import { html, LitElement, type TemplateResult } from "lit";
4
+ import { customElement, property } from "lit/decorators.js";
5
+ import type { CustomProvider } from "../storage/stores/custom-providers-store.js";
6
+
7
+ @customElement("custom-provider-card")
8
+ export class CustomProviderCard extends LitElement {
9
+ @property({ type: Object }) provider!: CustomProvider;
10
+ @property({ type: Boolean }) isAutoDiscovery = false;
11
+ @property({ type: Object }) status?: { modelCount: number; status: "connected" | "disconnected" | "checking" };
12
+ @property() onRefresh?: (provider: CustomProvider) => void;
13
+ @property() onEdit?: (provider: CustomProvider) => void;
14
+ @property() onDelete?: (provider: CustomProvider) => void;
15
+
16
+ protected createRenderRoot() {
17
+ return this;
18
+ }
19
+
20
+ private renderStatus(): TemplateResult {
21
+ if (!this.isAutoDiscovery) {
22
+ return html`
23
+ <div class="text-xs text-muted-foreground mt-1">
24
+ ${i18n("Models")}: ${this.provider.models?.length || 0}
25
+ </div>
26
+ `;
27
+ }
28
+
29
+ if (!this.status) return html``;
30
+
31
+ const statusIcon =
32
+ this.status.status === "connected"
33
+ ? html`<span class="text-green-500">●</span>`
34
+ : this.status.status === "checking"
35
+ ? html`<span class="text-yellow-500">●</span>`
36
+ : html`<span class="text-red-500">●</span>`;
37
+
38
+ const statusText =
39
+ this.status.status === "connected"
40
+ ? `${this.status.modelCount} ${i18n("models")}`
41
+ : this.status.status === "checking"
42
+ ? i18n("Checking...")
43
+ : i18n("Disconnected");
44
+
45
+ return html`
46
+ <div class="text-xs text-muted-foreground mt-1 flex items-center gap-1">
47
+ ${statusIcon} ${statusText}
48
+ </div>
49
+ `;
50
+ }
51
+
52
+ render(): TemplateResult {
53
+ return html`
54
+ <div class="border border-border rounded-lg p-4 space-y-2">
55
+ <div class="flex items-center justify-between">
56
+ <div class="flex-1">
57
+ <div class="font-medium text-sm text-foreground">${this.provider.name}</div>
58
+ <div class="text-xs text-muted-foreground mt-1">
59
+ <span class="capitalize">${this.provider.type}</span>
60
+ ${this.provider.baseUrl ? html` • ${this.provider.baseUrl}` : ""}
61
+ </div>
62
+ ${this.renderStatus()}
63
+ </div>
64
+ <div class="flex gap-2">
65
+ ${
66
+ this.isAutoDiscovery && this.onRefresh
67
+ ? Button({
68
+ onClick: () => this.onRefresh?.(this.provider),
69
+ variant: "ghost",
70
+ size: "sm",
71
+ children: i18n("Refresh"),
72
+ })
73
+ : ""
74
+ }
75
+ ${
76
+ this.onEdit
77
+ ? Button({
78
+ onClick: () => this.onEdit?.(this.provider),
79
+ variant: "ghost",
80
+ size: "sm",
81
+ children: i18n("Edit"),
82
+ })
83
+ : ""
84
+ }
85
+ ${
86
+ this.onDelete
87
+ ? Button({
88
+ onClick: () => this.onDelete?.(this.provider),
89
+ variant: "ghost",
90
+ size: "sm",
91
+ children: i18n("Delete"),
92
+ })
93
+ : ""
94
+ }
95
+ </div>
96
+ </div>
97
+ </div>
98
+ `;
99
+ }
100
+ }
@@ -1,4 +1,5 @@
1
- import { type BaseComponentProps, fc, html } from "@mariozechner/mini-lit";
1
+ import { type BaseComponentProps, fc } from "@mariozechner/mini-lit/dist/mini.js";
2
+ import { html } from "lit";
2
3
  import { type Ref, ref } from "lit/directives/ref.js";
3
4
  import { i18n } from "../utils/i18n.js";
4
5
 
@@ -1,12 +1,14 @@
1
- import { Button, html, icon, Select, type SelectOption } from "@mariozechner/mini-lit";
1
+ import { icon } from "@mariozechner/mini-lit";
2
+ import { Button } from "@mariozechner/mini-lit/dist/Button.js";
3
+ import { Select, type SelectOption } from "@mariozechner/mini-lit/dist/Select.js";
2
4
  import type { Model } from "@mariozechner/pi-ai";
3
- import { LitElement } from "lit";
5
+ import { html, LitElement } from "lit";
4
6
  import { customElement, property, state } from "lit/decorators.js";
5
7
  import { createRef, ref } from "lit/directives/ref.js";
6
8
  import { Brain, Loader2, Paperclip, Send, Sparkles, Square } from "lucide";
7
- import "./AttachmentTile.js";
8
9
  import { type Attachment, loadAttachment } from "../utils/attachment-utils.js";
9
10
  import { i18n } from "../utils/i18n.js";
11
+ import "./AttachmentTile.js";
10
12
 
11
13
  @customElement("message-editor")
12
14
  export class MessageEditor extends LitElement {
@@ -22,13 +24,6 @@ export class MessageEditor extends LitElement {
22
24
  const oldValue = this._value;
23
25
  this._value = val;
24
26
  this.requestUpdate("value", oldValue);
25
- this.updateComplete.then(() => {
26
- const textarea = this.textareaRef.value;
27
- if (textarea) {
28
- textarea.style.height = "auto";
29
- textarea.style.height = `${Math.min(textarea.scrollHeight, 200)}px`;
30
- }
31
- });
32
27
  }
33
28
 
34
29
  @property() isStreaming = false;
@@ -60,8 +55,6 @@ export class MessageEditor extends LitElement {
60
55
  private handleTextareaInput = (e: Event) => {
61
56
  const textarea = e.target as HTMLTextAreaElement;
62
57
  this.value = textarea.value;
63
- textarea.style.height = "auto";
64
- textarea.style.height = `${Math.min(textarea.scrollHeight, 200)}px`;
65
58
  this.onInput?.(this.value);
66
59
  };
67
60
 
@@ -231,33 +224,13 @@ export class MessageEditor extends LitElement {
231
224
  this.processingFiles = false;
232
225
  };
233
226
 
234
- private adjustTextareaHeight() {
235
- const textarea = this.textareaRef.value;
236
- if (textarea) {
237
- // Reset height to auto to get accurate scrollHeight
238
- textarea.style.height = "auto";
239
- // Only adjust if there's content, otherwise keep minimal height
240
- if (this.value.trim()) {
241
- textarea.style.height = `${Math.min(textarea.scrollHeight, 200)}px`;
242
- }
243
- }
244
- }
245
-
246
227
  override firstUpdated() {
247
228
  const textarea = this.textareaRef.value;
248
229
  if (textarea) {
249
- // Don't adjust height on first render - let it be minimal
250
230
  textarea.focus();
251
231
  }
252
232
  }
253
233
 
254
- override updated() {
255
- // Only adjust height when component updates if there's content
256
- if (this.value) {
257
- this.adjustTextareaHeight();
258
- }
259
- }
260
-
261
234
  override render() {
262
235
  // Check if current model supports thinking/reasoning
263
236
  const model = this.currentModel;
@@ -304,7 +277,7 @@ export class MessageEditor extends LitElement {
304
277
  class="w-full bg-transparent p-4 text-foreground placeholder-muted-foreground outline-none resize-none overflow-y-auto"
305
278
  placeholder=${i18n("Type a message...")}
306
279
  rows="1"
307
- style="max-height: 200px;"
280
+ style="max-height: 200px; field-sizing: content; min-height: 1lh; height: auto;"
308
281
  .value=${this.value}
309
282
  @input=${this.handleTextareaInput}
310
283
  @keydown=${this.handleKeyDown}
@@ -1,10 +1,9 @@
1
- import { html } from "@mariozechner/mini-lit";
2
1
  import type {
3
2
  AgentTool,
4
3
  AssistantMessage as AssistantMessageType,
5
4
  ToolResultMessage as ToolResultMessageType,
6
5
  } from "@mariozechner/pi-ai";
7
- import { LitElement, type TemplateResult } from "lit";
6
+ import { html, LitElement, type TemplateResult } from "lit";
8
7
  import { property } from "lit/decorators.js";
9
8
  import { repeat } from "lit/directives/repeat.js";
10
9
  import type { AppMessage } from "./Messages.js";
@@ -15,6 +14,7 @@ export class MessageList extends LitElement {
15
14
  @property({ type: Array }) tools: AgentTool[] = [];
16
15
  @property({ type: Object }) pendingToolCalls?: Set<string>;
17
16
  @property({ type: Boolean }) isStreaming: boolean = false;
17
+ @property({ attribute: false }) onCostClick?: () => void;
18
18
 
19
19
  protected override createRenderRoot(): HTMLElement | DocumentFragment {
20
20
  return this;
@@ -64,10 +64,11 @@ export class MessageList extends LitElement {
64
64
  template: html`<assistant-message
65
65
  .message=${amsg}
66
66
  .tools=${this.tools}
67
- .isStreaming=${this.isStreaming}
67
+ .isStreaming=${false}
68
68
  .pendingToolCalls=${this.pendingToolCalls}
69
69
  .toolResultsById=${resultByCallId}
70
70
  .hideToolCalls=${false}
71
+ .onCostClick=${this.onCostClick}
71
72
  ></assistant-message>`,
72
73
  });
73
74
  index++;
@@ -1,4 +1,3 @@
1
- import { html } from "@mariozechner/mini-lit";
2
1
  import type {
3
2
  AgentTool,
4
3
  AssistantMessage as AssistantMessageType,
@@ -6,12 +5,13 @@ import type {
6
5
  ToolResultMessage as ToolResultMessageType,
7
6
  UserMessage as UserMessageType,
8
7
  } from "@mariozechner/pi-ai";
9
- import { LitElement, type TemplateResult } from "lit";
8
+ import { html, LitElement, type TemplateResult } from "lit";
10
9
  import { customElement, property } from "lit/decorators.js";
11
10
  import { renderTool } from "../tools/index.js";
12
11
  import type { Attachment } from "../utils/attachment-utils.js";
13
12
  import { formatUsage } from "../utils/format.js";
14
13
  import { i18n } from "../utils/i18n.js";
14
+ import "./ThinkingBlock.js";
15
15
 
16
16
  export type UserMessageWithAttachments = UserMessageType & { attachments?: Attachment[] };
17
17
 
@@ -62,19 +62,21 @@ export class UserMessage extends LitElement {
62
62
  : this.message.content.find((c) => c.type === "text")?.text || "";
63
63
 
64
64
  return html`
65
- <div class="py-2 px-4 border-l-4 border-accent-foreground/60 text-primary-foreground">
66
- <markdown-block .content=${content}></markdown-block>
67
- ${
68
- this.message.attachments && this.message.attachments.length > 0
69
- ? html`
70
- <div class="mt-3 flex flex-wrap gap-2">
71
- ${this.message.attachments.map(
72
- (attachment) => html` <attachment-tile .attachment=${attachment}></attachment-tile> `,
73
- )}
74
- </div>
75
- `
76
- : ""
77
- }
65
+ <div class="flex justify-start mx-4">
66
+ <div class="user-message-container py-2 px-4 rounded-xl">
67
+ <markdown-block .content=${content}></markdown-block>
68
+ ${
69
+ this.message.attachments && this.message.attachments.length > 0
70
+ ? html`
71
+ <div class="mt-3 flex flex-wrap gap-2">
72
+ ${this.message.attachments.map(
73
+ (attachment) => html` <attachment-tile .attachment=${attachment}></attachment-tile> `,
74
+ )}
75
+ </div>
76
+ `
77
+ : ""
78
+ }
79
+ </div>
78
80
  </div>
79
81
  `;
80
82
  }
@@ -88,6 +90,7 @@ export class AssistantMessage extends LitElement {
88
90
  @property({ type: Boolean }) hideToolCalls = false;
89
91
  @property({ type: Object }) toolResultsById?: Map<string, ToolResultMessageType>;
90
92
  @property({ type: Boolean }) isStreaming: boolean = false;
93
+ @property({ attribute: false }) onCostClick?: () => void;
91
94
 
92
95
  protected override createRenderRoot(): HTMLElement | DocumentFragment {
93
96
  return this;
@@ -106,7 +109,9 @@ export class AssistantMessage extends LitElement {
106
109
  if (chunk.type === "text" && chunk.text.trim() !== "") {
107
110
  orderedParts.push(html`<markdown-block .content=${chunk.text}></markdown-block>`);
108
111
  } else if (chunk.type === "thinking" && chunk.thinking.trim() !== "") {
109
- orderedParts.push(html` <markdown-block .content=${chunk.thinking} .isThinking=${true}></markdown-block> `);
112
+ orderedParts.push(
113
+ html`<thinking-block .content=${chunk.thinking} .isStreaming=${this.isStreaming}></thinking-block>`,
114
+ );
110
115
  } else if (chunk.type === "toolCall") {
111
116
  if (!this.hideToolCalls) {
112
117
  const tool = this.tools?.find((t) => t.name === chunk.name);
@@ -132,8 +137,10 @@ export class AssistantMessage extends LitElement {
132
137
  <div>
133
138
  ${orderedParts.length ? html` <div class="px-4 flex flex-col gap-3">${orderedParts}</div> ` : ""}
134
139
  ${
135
- this.message.usage
136
- ? html` <div class="px-4 mt-2 text-xs text-muted-foreground">${formatUsage(this.message.usage)}</div> `
140
+ this.message.usage && !this.isStreaming
141
+ ? this.onCostClick
142
+ ? html` <div class="px-4 mt-2 text-xs text-muted-foreground cursor-pointer hover:text-foreground transition-colors" @click=${this.onCostClick}>${formatUsage(this.message.usage)}</div> `
143
+ : html` <div class="px-4 mt-2 text-xs text-muted-foreground">${formatUsage(this.message.usage)}</div> `
137
144
  : ""
138
145
  }
139
146
  ${
@@ -183,7 +190,12 @@ export class ToolMessageDebugView extends LitElement {
183
190
  }
184
191
 
185
192
  override render() {
186
- const output = this.pretty(this.result?.output);
193
+ const textOutput =
194
+ this.result?.content
195
+ ?.filter((c) => c.type === "text")
196
+ .map((c: any) => c.text)
197
+ .join("\n") || "";
198
+ const output = this.pretty(textOutput);
187
199
  const details = this.pretty(this.result?.details);
188
200
 
189
201
  return html`
@@ -232,7 +244,7 @@ export class ToolMessage extends LitElement {
232
244
  ? {
233
245
  role: "toolResult",
234
246
  isError: true,
235
- output: "",
247
+ content: [],
236
248
  toolCallId: this.toolCall.id,
237
249
  toolName: this.toolCall.name,
238
250
  timestamp: Date.now(),
@@ -1,8 +1,12 @@
1
- import { Badge, Button, html, Input, i18n } from "@mariozechner/mini-lit";
1
+ import { i18n } from "@mariozechner/mini-lit";
2
+ import { Badge } from "@mariozechner/mini-lit/dist/Badge.js";
3
+ import { Button } from "@mariozechner/mini-lit/dist/Button.js";
2
4
  import { type Context, complete, getModel } from "@mariozechner/pi-ai";
3
- import { LitElement } from "lit";
5
+ import { html, LitElement } from "lit";
4
6
  import { customElement, property, state } from "lit/decorators.js";
5
7
  import { getAppStorage } from "../storage/app-storage.js";
8
+ import { applyProxyIfNeeded } from "../utils/proxy-utils.js";
9
+ import { Input } from "./Input.js";
6
10
 
7
11
  // Test models for each provider
8
12
  const TEST_MODELS: Record<string, string> = {
@@ -23,6 +27,7 @@ export class ProviderKeyInput extends LitElement {
23
27
  @state() private testing = false;
24
28
  @state() private failed = false;
25
29
  @state() private hasKey = false;
30
+ @state() private inputChanged = false;
26
31
 
27
32
  protected createRenderRoot() {
28
33
  return this;
@@ -51,16 +56,12 @@ export class ProviderKeyInput extends LitElement {
51
56
  let model = getModel(provider as any, modelId);
52
57
  if (!model) return false;
53
58
 
54
- // Check if CORS proxy is enabled and apply it
59
+ // Get proxy URL from settings (if available)
55
60
  const proxyEnabled = await getAppStorage().settings.get<boolean>("proxy.enabled");
56
61
  const proxyUrl = await getAppStorage().settings.get<string>("proxy.url");
57
62
 
58
- if (proxyEnabled && proxyUrl && model.baseUrl) {
59
- model = {
60
- ...model,
61
- baseUrl: `${proxyUrl}/?url=${encodeURIComponent(model.baseUrl)}`,
62
- };
63
- }
63
+ // Apply proxy only if this provider/key combination requires it
64
+ model = applyProxyIfNeeded(model, apiKey, proxyEnabled ? proxyUrl || undefined : undefined);
64
65
 
65
66
  const context: Context = {
66
67
  messages: [{ role: "user", content: "Reply with: ok", timestamp: Date.now() }],
@@ -92,7 +93,7 @@ export class ProviderKeyInput extends LitElement {
92
93
  try {
93
94
  await getAppStorage().providerKeys.set(this.provider, this.keyInput);
94
95
  this.hasKey = true;
95
- this.keyInput = "";
96
+ this.inputChanged = false;
96
97
  this.requestUpdate();
97
98
  } catch (error) {
98
99
  console.error("Failed to save API key:", error);
@@ -111,17 +112,6 @@ export class ProviderKeyInput extends LitElement {
111
112
  }
112
113
  }
113
114
 
114
- private async removeKey() {
115
- try {
116
- await getAppStorage().providerKeys.delete(this.provider);
117
- this.hasKey = false;
118
- this.keyInput = "";
119
- this.requestUpdate();
120
- } catch (error) {
121
- console.error("Failed to remove API key:", error);
122
- }
123
- }
124
-
125
115
  render() {
126
116
  return html`
127
117
  <div class="space-y-3">
@@ -143,27 +133,18 @@ export class ProviderKeyInput extends LitElement {
143
133
  value: this.keyInput,
144
134
  onInput: (e: Event) => {
145
135
  this.keyInput = (e.target as HTMLInputElement).value;
136
+ this.inputChanged = true;
146
137
  this.requestUpdate();
147
138
  },
148
139
  className: "flex-1",
149
140
  })}
150
- ${
151
- this.hasKey
152
- ? Button({
153
- onClick: () => this.removeKey(),
154
- variant: "ghost",
155
- size: "sm",
156
- children: i18n("Clear"),
157
- className: "!text-destructive",
158
- })
159
- : Button({
160
- onClick: () => this.saveKey(),
161
- variant: "default",
162
- size: "sm",
163
- disabled: !this.keyInput || this.testing,
164
- children: i18n("Save"),
165
- })
166
- }
141
+ ${Button({
142
+ onClick: () => this.saveKey(),
143
+ variant: "default",
144
+ size: "sm",
145
+ disabled: !this.keyInput || this.testing || (this.hasKey && !this.inputChanged),
146
+ children: i18n("Save"),
147
+ })}
167
148
  </div>
168
149
  </div>
169
150
  `;
@@ -1,6 +1,5 @@
1
- import { html } from "@mariozechner/mini-lit";
2
1
  import type { AgentTool, Message, ToolResultMessage } from "@mariozechner/pi-ai";
3
- import { LitElement } from "lit";
2
+ import { html, LitElement } from "lit";
4
3
  import { property, state } from "lit/decorators.js";
5
4
 
6
5
  export class StreamingMessageContainer extends LitElement {
@@ -8,6 +7,7 @@ export class StreamingMessageContainer extends LitElement {
8
7
  @property({ type: Boolean }) isStreaming = false;
9
8
  @property({ type: Object }) pendingToolCalls?: Set<string>;
10
9
  @property({ type: Object }) toolResultsById?: Map<string, ToolResultMessage>;
10
+ @property({ attribute: false }) onCostClick?: () => void;
11
11
 
12
12
  @state() private _message: Message | null = null;
13
13
  private _pendingMessage: Message | null = null;
@@ -87,6 +87,7 @@ export class StreamingMessageContainer extends LitElement {
87
87
  .pendingToolCalls=${this.pendingToolCalls}
88
88
  .toolResultsById=${this.toolResultsById}
89
89
  .hideToolCalls=${false}
90
+ .onCostClick=${this.onCostClick}
90
91
  ></assistant-message>
91
92
  ${this.isStreaming ? html`<span class="mx-4 inline-block w-2 h-4 bg-muted-foreground animate-pulse"></span>` : ""}
92
93
  </div>
@@ -0,0 +1,43 @@
1
+ import { icon } from "@mariozechner/mini-lit";
2
+ import { html, LitElement } from "lit";
3
+ import { customElement, property, state } from "lit/decorators.js";
4
+ import { ChevronRight } from "lucide";
5
+
6
+ @customElement("thinking-block")
7
+ export class ThinkingBlock extends LitElement {
8
+ @property() content!: string;
9
+ @property({ type: Boolean }) isStreaming = false;
10
+ @state() private isExpanded = false;
11
+
12
+ protected override createRenderRoot(): HTMLElement | DocumentFragment {
13
+ return this;
14
+ }
15
+
16
+ override connectedCallback(): void {
17
+ super.connectedCallback();
18
+ this.style.display = "block";
19
+ }
20
+
21
+ private toggleExpanded() {
22
+ this.isExpanded = !this.isExpanded;
23
+ }
24
+
25
+ override render() {
26
+ const shimmerClasses = this.isStreaming
27
+ ? "animate-shimmer bg-gradient-to-r from-muted-foreground via-foreground to-muted-foreground bg-[length:200%_100%] bg-clip-text text-transparent"
28
+ : "";
29
+
30
+ return html`
31
+ <div class="thinking-block">
32
+ <div
33
+ class="thinking-header cursor-pointer select-none flex items-center gap-2 py-1 text-sm text-muted-foreground hover:text-foreground transition-colors"
34
+ @click=${this.toggleExpanded}
35
+ >
36
+ <span class="transition-transform inline-block ${this.isExpanded ? "rotate-90" : ""}">${icon(ChevronRight, "sm")}</span>
37
+ <span class="${shimmerClasses}">Thinking...</span>
38
+ </div>
39
+ ${this.isExpanded ? html`<markdown-block .content=${this.content} .isThinking=${true}></markdown-block>` : ""}
40
+ </div>
41
+ `;
42
+ }
43
+ }
@@ -1,6 +1,8 @@
1
- import { DialogBase, DialogContent, DialogHeader, html } from "@mariozechner/mini-lit";
2
1
  import { customElement, state } from "lit/decorators.js";
3
2
  import "../components/ProviderKeyInput.js";
3
+ import { DialogContent, DialogHeader } from "@mariozechner/mini-lit/dist/Dialog.js";
4
+ import { DialogBase } from "@mariozechner/mini-lit/dist/DialogBase.js";
5
+ import { html } from "lit";
4
6
  import { getAppStorage } from "../storage/app-storage.js";
5
7
  import { i18n } from "../utils/i18n.js";
6
8
 
@@ -1,7 +1,8 @@
1
- import { Button, html, icon } from "@mariozechner/mini-lit";
2
1
  import "@mariozechner/mini-lit/dist/ModeToggle.js";
2
+ import { icon } from "@mariozechner/mini-lit";
3
+ import { Button } from "@mariozechner/mini-lit/dist/Button.js";
3
4
  import { renderAsync } from "docx-preview";
4
- import { LitElement } from "lit";
5
+ import { html, LitElement } from "lit";
5
6
  import { state } from "lit/decorators.js";
6
7
  import { Download, X } from "lucide";
7
8
  import * as pdfjsLib from "pdfjs-dist";