@mariozechner/pi-web-ui 0.30.2 → 0.31.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 (242) hide show
  1. package/CHANGELOG.md +90 -0
  2. package/README.md +420 -150
  3. package/dist/ChatPanel.d.ts +1 -2
  4. package/dist/ChatPanel.d.ts.map +1 -1
  5. package/dist/ChatPanel.js +22 -45
  6. package/dist/ChatPanel.js.map +1 -1
  7. package/dist/components/AgentInterface.d.ts +1 -1
  8. package/dist/components/AgentInterface.d.ts.map +1 -1
  9. package/dist/components/AgentInterface.js +113 -91
  10. package/dist/components/AgentInterface.js.map +1 -1
  11. package/dist/components/AttachmentTile.d.ts.map +1 -1
  12. package/dist/components/AttachmentTile.js +12 -28
  13. package/dist/components/AttachmentTile.js.map +1 -1
  14. package/dist/components/ConsoleBlock.d.ts.map +1 -1
  15. package/dist/components/ConsoleBlock.js +6 -21
  16. package/dist/components/ConsoleBlock.js.map +1 -1
  17. package/dist/components/CustomProviderCard.d.ts.map +1 -1
  18. package/dist/components/CustomProviderCard.js +15 -34
  19. package/dist/components/CustomProviderCard.js.map +1 -1
  20. package/dist/components/ExpandableSection.d.ts.map +1 -1
  21. package/dist/components/ExpandableSection.js +10 -27
  22. package/dist/components/ExpandableSection.js.map +1 -1
  23. package/dist/components/Input.js.map +1 -1
  24. package/dist/components/MessageEditor.d.ts +2 -1
  25. package/dist/components/MessageEditor.d.ts.map +1 -1
  26. package/dist/components/MessageEditor.js +147 -190
  27. package/dist/components/MessageEditor.js.map +1 -1
  28. package/dist/components/MessageList.d.ts +2 -3
  29. package/dist/components/MessageList.d.ts.map +1 -1
  30. package/dist/components/MessageList.js +11 -28
  31. package/dist/components/MessageList.js.map +1 -1
  32. package/dist/components/Messages.d.ts +37 -7
  33. package/dist/components/Messages.d.ts.map +1 -1
  34. package/dist/components/Messages.js +127 -103
  35. package/dist/components/Messages.js.map +1 -1
  36. package/dist/components/ProviderKeyInput.d.ts.map +1 -1
  37. package/dist/components/ProviderKeyInput.js +15 -39
  38. package/dist/components/ProviderKeyInput.js.map +1 -1
  39. package/dist/components/SandboxedIframe.d.ts.map +1 -1
  40. package/dist/components/SandboxedIframe.js +11 -15
  41. package/dist/components/SandboxedIframe.js.map +1 -1
  42. package/dist/components/StreamingMessageContainer.d.ts +3 -2
  43. package/dist/components/StreamingMessageContainer.d.ts.map +1 -1
  44. package/dist/components/StreamingMessageContainer.js +16 -34
  45. package/dist/components/StreamingMessageContainer.js.map +1 -1
  46. package/dist/components/ThinkingBlock.d.ts.map +1 -1
  47. package/dist/components/ThinkingBlock.js +9 -26
  48. package/dist/components/ThinkingBlock.js.map +1 -1
  49. package/dist/components/message-renderer-registry.d.ts +5 -5
  50. package/dist/components/message-renderer-registry.d.ts.map +1 -1
  51. package/dist/components/message-renderer-registry.js.map +1 -1
  52. package/dist/components/sandbox/ArtifactsRuntimeProvider.d.ts.map +1 -1
  53. package/dist/components/sandbox/ArtifactsRuntimeProvider.js +3 -0
  54. package/dist/components/sandbox/ArtifactsRuntimeProvider.js.map +1 -1
  55. package/dist/components/sandbox/AttachmentsRuntimeProvider.d.ts.map +1 -1
  56. package/dist/components/sandbox/AttachmentsRuntimeProvider.js +1 -0
  57. package/dist/components/sandbox/AttachmentsRuntimeProvider.js.map +1 -1
  58. package/dist/components/sandbox/ConsoleRuntimeProvider.d.ts.map +1 -1
  59. package/dist/components/sandbox/ConsoleRuntimeProvider.js +3 -5
  60. package/dist/components/sandbox/ConsoleRuntimeProvider.js.map +1 -1
  61. package/dist/components/sandbox/FileDownloadRuntimeProvider.d.ts.map +1 -1
  62. package/dist/components/sandbox/FileDownloadRuntimeProvider.js +1 -3
  63. package/dist/components/sandbox/FileDownloadRuntimeProvider.js.map +1 -1
  64. package/dist/components/sandbox/RuntimeMessageBridge.d.ts.map +1 -1
  65. package/dist/components/sandbox/RuntimeMessageBridge.js.map +1 -1
  66. package/dist/components/sandbox/RuntimeMessageRouter.d.ts.map +1 -1
  67. package/dist/components/sandbox/RuntimeMessageRouter.js +3 -5
  68. package/dist/components/sandbox/RuntimeMessageRouter.js.map +1 -1
  69. package/dist/dialogs/ApiKeyPromptDialog.d.ts.map +1 -1
  70. package/dist/dialogs/ApiKeyPromptDialog.js +10 -23
  71. package/dist/dialogs/ApiKeyPromptDialog.js.map +1 -1
  72. package/dist/dialogs/AttachmentOverlay.d.ts.map +1 -1
  73. package/dist/dialogs/AttachmentOverlay.js +34 -46
  74. package/dist/dialogs/AttachmentOverlay.js.map +1 -1
  75. package/dist/dialogs/CustomProviderDialog.d.ts.map +1 -1
  76. package/dist/dialogs/CustomProviderDialog.js +19 -39
  77. package/dist/dialogs/CustomProviderDialog.js.map +1 -1
  78. package/dist/dialogs/ModelSelector.d.ts.map +1 -1
  79. package/dist/dialogs/ModelSelector.js +25 -53
  80. package/dist/dialogs/ModelSelector.js.map +1 -1
  81. package/dist/dialogs/PersistentStorageDialog.d.ts.map +1 -1
  82. package/dist/dialogs/PersistentStorageDialog.js +9 -23
  83. package/dist/dialogs/PersistentStorageDialog.js.map +1 -1
  84. package/dist/dialogs/ProvidersModelsTab.d.ts.map +1 -1
  85. package/dist/dialogs/ProvidersModelsTab.js +7 -23
  86. package/dist/dialogs/ProvidersModelsTab.js.map +1 -1
  87. package/dist/dialogs/SessionListDialog.d.ts.map +1 -1
  88. package/dist/dialogs/SessionListDialog.js +14 -29
  89. package/dist/dialogs/SessionListDialog.js.map +1 -1
  90. package/dist/dialogs/SettingsDialog.d.ts.map +1 -1
  91. package/dist/dialogs/SettingsDialog.js +20 -52
  92. package/dist/dialogs/SettingsDialog.js.map +1 -1
  93. package/dist/index.d.ts +5 -8
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js +2 -6
  96. package/dist/index.js.map +1 -1
  97. package/dist/prompts/prompts.d.ts.map +1 -1
  98. package/dist/storage/app-storage.d.ts.map +1 -1
  99. package/dist/storage/app-storage.js +5 -0
  100. package/dist/storage/app-storage.js.map +1 -1
  101. package/dist/storage/backends/indexeddb-storage-backend.d.ts.map +1 -1
  102. package/dist/storage/backends/indexeddb-storage-backend.js +2 -1
  103. package/dist/storage/backends/indexeddb-storage-backend.js.map +1 -1
  104. package/dist/storage/store.d.ts.map +1 -1
  105. package/dist/storage/store.js +1 -3
  106. package/dist/storage/store.js.map +1 -1
  107. package/dist/storage/stores/custom-providers-store.d.ts.map +1 -1
  108. package/dist/storage/stores/custom-providers-store.js.map +1 -1
  109. package/dist/storage/stores/provider-keys-store.d.ts.map +1 -1
  110. package/dist/storage/stores/provider-keys-store.js.map +1 -1
  111. package/dist/storage/stores/sessions-store.d.ts +1 -1
  112. package/dist/storage/stores/sessions-store.d.ts.map +1 -1
  113. package/dist/storage/stores/sessions-store.js.map +1 -1
  114. package/dist/storage/stores/settings-store.d.ts.map +1 -1
  115. package/dist/storage/stores/settings-store.js.map +1 -1
  116. package/dist/storage/types.d.ts +2 -3
  117. package/dist/storage/types.d.ts.map +1 -1
  118. package/dist/tools/artifacts/ArtifactElement.d.ts.map +1 -1
  119. package/dist/tools/artifacts/ArtifactElement.js +1 -4
  120. package/dist/tools/artifacts/ArtifactElement.js.map +1 -1
  121. package/dist/tools/artifacts/ArtifactPill.js.map +1 -1
  122. package/dist/tools/artifacts/Console.d.ts.map +1 -1
  123. package/dist/tools/artifacts/Console.js +10 -28
  124. package/dist/tools/artifacts/Console.js.map +1 -1
  125. package/dist/tools/artifacts/DocxArtifact.d.ts.map +1 -1
  126. package/dist/tools/artifacts/DocxArtifact.js +7 -23
  127. package/dist/tools/artifacts/DocxArtifact.js.map +1 -1
  128. package/dist/tools/artifacts/ExcelArtifact.d.ts.map +1 -1
  129. package/dist/tools/artifacts/ExcelArtifact.js +7 -23
  130. package/dist/tools/artifacts/ExcelArtifact.js.map +1 -1
  131. package/dist/tools/artifacts/GenericArtifact.d.ts.map +1 -1
  132. package/dist/tools/artifacts/GenericArtifact.js +5 -19
  133. package/dist/tools/artifacts/GenericArtifact.js.map +1 -1
  134. package/dist/tools/artifacts/HtmlArtifact.d.ts.map +1 -1
  135. package/dist/tools/artifacts/HtmlArtifact.js +16 -35
  136. package/dist/tools/artifacts/HtmlArtifact.js.map +1 -1
  137. package/dist/tools/artifacts/ImageArtifact.d.ts.map +1 -1
  138. package/dist/tools/artifacts/ImageArtifact.js +5 -19
  139. package/dist/tools/artifacts/ImageArtifact.js.map +1 -1
  140. package/dist/tools/artifacts/MarkdownArtifact.d.ts.map +1 -1
  141. package/dist/tools/artifacts/MarkdownArtifact.js +8 -24
  142. package/dist/tools/artifacts/MarkdownArtifact.js.map +1 -1
  143. package/dist/tools/artifacts/PdfArtifact.d.ts.map +1 -1
  144. package/dist/tools/artifacts/PdfArtifact.js +8 -24
  145. package/dist/tools/artifacts/PdfArtifact.js.map +1 -1
  146. package/dist/tools/artifacts/SvgArtifact.d.ts.map +1 -1
  147. package/dist/tools/artifacts/SvgArtifact.js +8 -24
  148. package/dist/tools/artifacts/SvgArtifact.js.map +1 -1
  149. package/dist/tools/artifacts/TextArtifact.d.ts.map +1 -1
  150. package/dist/tools/artifacts/TextArtifact.js +6 -20
  151. package/dist/tools/artifacts/TextArtifact.js.map +1 -1
  152. package/dist/tools/artifacts/artifacts-tool-renderer.d.ts.map +1 -1
  153. package/dist/tools/artifacts/artifacts-tool-renderer.js +1 -0
  154. package/dist/tools/artifacts/artifacts-tool-renderer.js.map +1 -1
  155. package/dist/tools/artifacts/artifacts.d.ts +2 -3
  156. package/dist/tools/artifacts/artifacts.d.ts.map +1 -1
  157. package/dist/tools/artifacts/artifacts.js +30 -52
  158. package/dist/tools/artifacts/artifacts.js.map +1 -1
  159. package/dist/tools/extract-document.d.ts +2 -2
  160. package/dist/tools/extract-document.d.ts.map +1 -1
  161. package/dist/tools/extract-document.js.map +1 -1
  162. package/dist/tools/index.js.map +1 -1
  163. package/dist/tools/javascript-repl.d.ts +3 -3
  164. package/dist/tools/javascript-repl.d.ts.map +1 -1
  165. package/dist/tools/javascript-repl.js.map +1 -1
  166. package/dist/tools/renderer-registry.js.map +1 -1
  167. package/dist/tools/renderers/BashRenderer.d.ts.map +1 -1
  168. package/dist/tools/renderers/BashRenderer.js.map +1 -1
  169. package/dist/tools/renderers/CalculateRenderer.d.ts.map +1 -1
  170. package/dist/tools/renderers/CalculateRenderer.js.map +1 -1
  171. package/dist/tools/renderers/DefaultRenderer.d.ts.map +1 -1
  172. package/dist/tools/renderers/DefaultRenderer.js.map +1 -1
  173. package/dist/tools/renderers/GetCurrentTimeRenderer.d.ts.map +1 -1
  174. package/dist/tools/renderers/GetCurrentTimeRenderer.js.map +1 -1
  175. package/dist/utils/attachment-utils.js.map +1 -1
  176. package/dist/utils/auth-token.js.map +1 -1
  177. package/dist/utils/format.js.map +1 -1
  178. package/dist/utils/i18n.d.ts +14 -14
  179. package/dist/utils/i18n.d.ts.map +1 -1
  180. package/dist/utils/i18n.js.map +1 -1
  181. package/dist/utils/model-discovery.js.map +1 -1
  182. package/dist/utils/proxy-utils.d.ts +9 -1
  183. package/dist/utils/proxy-utils.d.ts.map +1 -1
  184. package/dist/utils/proxy-utils.js +19 -0
  185. package/dist/utils/proxy-utils.js.map +1 -1
  186. package/dist/utils/test-sessions.d.ts +47 -47
  187. package/dist/utils/test-sessions.js.map +1 -1
  188. package/example/package.json +1 -1
  189. package/example/src/custom-messages.ts +26 -36
  190. package/example/src/main.ts +11 -20
  191. package/example/tsconfig.json +1 -0
  192. package/package.json +4 -4
  193. package/src/ChatPanel.ts +2 -3
  194. package/src/components/AgentInterface.ts +57 -13
  195. package/src/components/MessageEditor.ts +2 -1
  196. package/src/components/MessageList.ts +3 -4
  197. package/src/components/Messages.ts +108 -19
  198. package/src/components/StreamingMessageContainer.ts +6 -5
  199. package/src/components/message-renderer-registry.ts +5 -5
  200. package/src/index.ts +13 -10
  201. package/src/storage/stores/sessions-store.ts +1 -1
  202. package/src/storage/types.ts +2 -3
  203. package/src/tools/artifacts/artifacts.ts +4 -4
  204. package/src/tools/extract-document.ts +2 -1
  205. package/src/tools/javascript-repl.ts +2 -1
  206. package/src/utils/proxy-utils.ts +23 -1
  207. package/dist/agent/agent.d.ts +0 -62
  208. package/dist/agent/agent.d.ts.map +0 -1
  209. package/dist/agent/agent.js +0 -274
  210. package/dist/agent/agent.js.map +0 -1
  211. package/dist/agent/transports/AppTransport.d.ts +0 -15
  212. package/dist/agent/transports/AppTransport.d.ts.map +0 -1
  213. package/dist/agent/transports/AppTransport.js +0 -327
  214. package/dist/agent/transports/AppTransport.js.map +0 -1
  215. package/dist/agent/transports/ProviderTransport.d.ts +0 -14
  216. package/dist/agent/transports/ProviderTransport.d.ts.map +0 -1
  217. package/dist/agent/transports/ProviderTransport.js +0 -55
  218. package/dist/agent/transports/ProviderTransport.js.map +0 -1
  219. package/dist/agent/transports/index.d.ts +0 -4
  220. package/dist/agent/transports/index.d.ts.map +0 -1
  221. package/dist/agent/transports/index.js +0 -4
  222. package/dist/agent/transports/index.js.map +0 -1
  223. package/dist/agent/transports/proxy-types.d.ts +0 -48
  224. package/dist/agent/transports/proxy-types.d.ts.map +0 -1
  225. package/dist/agent/transports/proxy-types.js +0 -2
  226. package/dist/agent/transports/proxy-types.js.map +0 -1
  227. package/dist/agent/transports/types.d.ts +0 -15
  228. package/dist/agent/transports/types.d.ts.map +0 -1
  229. package/dist/agent/transports/types.js +0 -2
  230. package/dist/agent/transports/types.js.map +0 -1
  231. package/dist/agent/types.d.ts +0 -15
  232. package/dist/agent/types.d.ts.map +0 -1
  233. package/dist/agent/types.js +0 -2
  234. package/dist/agent/types.js.map +0 -1
  235. package/example/src/test-sessions.ts +0 -104
  236. package/src/agent/agent.ts +0 -341
  237. package/src/agent/transports/AppTransport.ts +0 -371
  238. package/src/agent/transports/ProviderTransport.ts +0 -71
  239. package/src/agent/transports/index.ts +0 -3
  240. package/src/agent/transports/proxy-types.ts +0 -15
  241. package/src/agent/transports/types.ts +0 -26
  242. package/src/agent/types.ts +0 -11
package/README.md CHANGED
@@ -1,23 +1,23 @@
1
1
  # @mariozechner/pi-web-ui
2
2
 
3
- Reusable web UI components for building AI chat interfaces powered by [@mariozechner/pi-ai](../ai).
3
+ Reusable web UI components for building AI chat interfaces powered by [@mariozechner/pi-ai](../ai) and [@mariozechner/pi-agent-core](../agent).
4
4
 
5
- Built with [mini-lit](https://github.com/badlogic/mini-lit) web components and Tailwind CSS v4.
5
+ Built with [mini-lit](https://github.com/badlogic/mini-lit) web components and Tailwind CSS v4.
6
6
 
7
7
  ## Features
8
8
 
9
- - Modern Chat Interface - Complete chat UI with message history, streaming responses, and tool execution
10
- - Tool Support - Built-in renderers for calculator, bash, time, and custom tools
11
- - Attachments - PDF, Office documents, images with preview and text extraction
12
- - Artifacts - HTML, SVG, Markdown, and text artifact rendering with sandboxed execution
13
- - Pluggable Transports - Direct API calls or proxy server support
14
- - Platform Agnostic - Works in browser extensions, web apps, VS Code extensions, Electron apps
15
- - TypeScript - Full type safety with TypeScript
9
+ - **Chat UI**: Complete interface with message history, streaming, and tool execution
10
+ - **Tools**: JavaScript REPL, document extraction, and artifacts (HTML, SVG, Markdown, etc.)
11
+ - **Attachments**: PDF, DOCX, XLSX, PPTX, images with preview and text extraction
12
+ - **Artifacts**: Interactive HTML, SVG, Markdown with sandboxed execution
13
+ - **Storage**: IndexedDB-backed storage for sessions, API keys, and settings
14
+ - **CORS Proxy**: Automatic proxy handling for browser environments
15
+ - **Custom Providers**: Support for Ollama, LM Studio, vLLM, and OpenAI-compatible APIs
16
16
 
17
17
  ## Installation
18
18
 
19
19
  ```bash
20
- npm install @mariozechner/pi-web-ui
20
+ npm install @mariozechner/pi-web-ui @mariozechner/pi-agent-core @mariozechner/pi-ai
21
21
  ```
22
22
 
23
23
  ## Quick Start
@@ -25,19 +25,43 @@ npm install @mariozechner/pi-web-ui
25
25
  See the [example](./example) directory for a complete working application.
26
26
 
27
27
  ```typescript
28
- import { Agent, ChatPanel, ProviderTransport, AppStorage,
29
- SessionIndexedDBBackend, setAppStorage } from '@mariozechner/pi-web-ui';
28
+ import { Agent } from '@mariozechner/pi-agent-core';
30
29
  import { getModel } from '@mariozechner/pi-ai';
30
+ import {
31
+ ChatPanel,
32
+ AppStorage,
33
+ IndexedDBStorageBackend,
34
+ ProviderKeysStore,
35
+ SessionsStore,
36
+ SettingsStore,
37
+ setAppStorage,
38
+ defaultConvertToLlm,
39
+ ApiKeyPromptDialog,
40
+ } from '@mariozechner/pi-web-ui';
31
41
  import '@mariozechner/pi-web-ui/app.css';
32
42
 
33
43
  // Set up storage
34
- const storage = new AppStorage({
35
- sessions: new SessionIndexedDBBackend('my-app-sessions'),
44
+ const settings = new SettingsStore();
45
+ const providerKeys = new ProviderKeysStore();
46
+ const sessions = new SessionsStore();
47
+
48
+ const backend = new IndexedDBStorageBackend({
49
+ dbName: 'my-app',
50
+ version: 1,
51
+ stores: [
52
+ settings.getConfig(),
53
+ providerKeys.getConfig(),
54
+ sessions.getConfig(),
55
+ SessionsStore.getMetadataConfig(),
56
+ ],
36
57
  });
37
- setAppStorage(storage);
38
58
 
39
- // Create transport
40
- const transport = new ProviderTransport();
59
+ settings.setBackend(backend);
60
+ providerKeys.setBackend(backend);
61
+ sessions.setBackend(backend);
62
+
63
+ const storage = new AppStorage(settings, providerKeys, sessions, undefined, backend);
64
+ setAppStorage(storage);
41
65
 
42
66
  // Create agent
43
67
  const agent = new Agent({
@@ -48,135 +72,286 @@ const agent = new Agent({
48
72
  messages: [],
49
73
  tools: [],
50
74
  },
51
- transport,
75
+ convertToLlm: defaultConvertToLlm,
52
76
  });
53
77
 
54
- // Create chat panel and attach agent
78
+ // Create chat panel
55
79
  const chatPanel = new ChatPanel();
56
- await chatPanel.setAgent(agent);
80
+ await chatPanel.setAgent(agent, {
81
+ onApiKeyRequired: (provider) => ApiKeyPromptDialog.prompt(provider),
82
+ });
57
83
 
58
84
  document.body.appendChild(chatPanel);
59
85
  ```
60
86
 
61
- **Run the example:**
87
+ ## Architecture
62
88
 
63
- ```bash
64
- cd example
65
- npm install
66
- npm run dev
89
+ ```
90
+ ┌─────────────────────────────────────────────────────┐
91
+ │ ChatPanel │
92
+ │ ┌─────────────────────┐ ┌─────────────────────┐ │
93
+ │ │ AgentInterface │ │ ArtifactsPanel │ │
94
+ │ │ (messages, input) │ │ (HTML, SVG, MD) │ │
95
+ │ └─────────────────────┘ └─────────────────────┘ │
96
+ └─────────────────────────────────────────────────────┘
97
+
98
+
99
+ ┌─────────────────────────────────────────────────────┐
100
+ │ Agent (from pi-agent-core) │
101
+ │ - State management (messages, model, tools) │
102
+ │ - Event emission (agent_start, message_update, ...) │
103
+ │ - Tool execution │
104
+ └─────────────────────────────────────────────────────┘
105
+
106
+
107
+ ┌─────────────────────────────────────────────────────┐
108
+ │ AppStorage │
109
+ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
110
+ │ │ Settings │ │ Provider │ │ Sessions │ │
111
+ │ │ Store │ │Keys Store│ │ Store │ │
112
+ │ └──────────┘ └──────────┘ └──────────┘ │
113
+ │ │ │
114
+ │ IndexedDBStorageBackend │
115
+ └─────────────────────────────────────────────────────┘
67
116
  ```
68
117
 
69
- ## Core Components
118
+ ## Components
70
119
 
71
120
  ### ChatPanel
72
121
 
73
- The main chat interface component. Displays messages, handles input, and coordinates with the Agent.
122
+ High-level chat interface with built-in artifacts panel.
74
123
 
75
124
  ```typescript
76
- import { ChatPanel, ApiKeyPromptDialog } from '@mariozechner/pi-web-ui';
77
-
78
125
  const chatPanel = new ChatPanel();
126
+ await chatPanel.setAgent(agent, {
127
+ // Prompt for API key when needed
128
+ onApiKeyRequired: async (provider) => ApiKeyPromptDialog.prompt(provider),
79
129
 
80
- // Optional: Handle API key prompts
81
- chatPanel.onApiKeyRequired = async (provider: string) => {
82
- return await ApiKeyPromptDialog.prompt(provider);
83
- };
130
+ // Hook before sending messages
131
+ onBeforeSend: async () => { /* save draft, etc. */ },
84
132
 
85
- // Attach an agent
86
- await chatPanel.setAgent(agent);
133
+ // Handle cost display click
134
+ onCostClick: () => { /* show cost breakdown */ },
135
+
136
+ // Custom sandbox URL for browser extensions
137
+ sandboxUrlProvider: () => chrome.runtime.getURL('sandbox.html'),
138
+
139
+ // Add custom tools
140
+ toolsFactory: (agent, agentInterface, artifactsPanel, runtimeProvidersFactory) => {
141
+ const replTool = createJavaScriptReplTool();
142
+ replTool.runtimeProvidersFactory = runtimeProvidersFactory;
143
+ return [replTool];
144
+ },
145
+ });
87
146
  ```
88
147
 
89
- ### Agent
148
+ ### AgentInterface
90
149
 
91
- Core state manager that handles conversation state, tool execution, and streaming.
150
+ Lower-level chat interface for custom layouts.
92
151
 
93
152
  ```typescript
94
- import { Agent, ProviderTransport } from '@mariozechner/pi-web-ui';
95
- import { getModel } from '@mariozechner/pi-ai';
153
+ const chat = document.createElement('agent-interface') as AgentInterface;
154
+ chat.session = agent;
155
+ chat.enableAttachments = true;
156
+ chat.enableModelSelector = true;
157
+ chat.enableThinkingSelector = true;
158
+ chat.onApiKeyRequired = async (provider) => { /* ... */ };
159
+ chat.onBeforeSend = async () => { /* ... */ };
160
+ ```
161
+
162
+ Properties:
163
+ - `session`: Agent instance
164
+ - `enableAttachments`: Show attachment button (default: true)
165
+ - `enableModelSelector`: Show model selector (default: true)
166
+ - `enableThinkingSelector`: Show thinking level selector (default: true)
167
+ - `showThemeToggle`: Show theme toggle (default: false)
168
+
169
+ ### Agent (from pi-agent-core)
170
+
171
+ ```typescript
172
+ import { Agent } from '@mariozechner/pi-agent-core';
96
173
 
97
174
  const agent = new Agent({
98
175
  initialState: {
99
176
  model: getModel('anthropic', 'claude-sonnet-4-5-20250929'),
100
- systemPrompt: 'You are a helpful assistant.',
177
+ systemPrompt: 'You are helpful.',
101
178
  thinkingLevel: 'off',
102
179
  messages: [],
103
180
  tools: [],
104
181
  },
105
- transport: new ProviderTransport(),
182
+ convertToLlm: defaultConvertToLlm,
106
183
  });
107
184
 
108
- // Subscribe to events
185
+ // Events
109
186
  agent.subscribe((event) => {
110
- if (event.type === 'state-update') {
111
- console.log('Messages:', event.state.messages);
187
+ switch (event.type) {
188
+ case 'agent_start': // Agent loop started
189
+ case 'agent_end': // Agent loop finished
190
+ case 'turn_start': // LLM call started
191
+ case 'turn_end': // LLM call finished
192
+ case 'message_start':
193
+ case 'message_update': // Streaming update
194
+ case 'message_end':
195
+ break;
112
196
  }
113
197
  });
114
198
 
115
- // Send a message
116
- await agent.send('Hello!');
199
+ // Send message
200
+ await agent.prompt('Hello!');
201
+ await agent.prompt({ role: 'user-with-attachments', content: 'Check this', attachments, timestamp: Date.now() });
202
+
203
+ // Control
204
+ agent.abort();
205
+ agent.setModel(newModel);
206
+ agent.setThinkingLevel('medium');
207
+ agent.setTools([...]);
208
+ agent.queueMessage(customMessage);
117
209
  ```
118
210
 
119
- ### AgentInterface
211
+ ## Message Types
120
212
 
121
- Lower-level chat interface for custom implementations. Used internally by ChatPanel.
213
+ ### UserMessageWithAttachments
214
+
215
+ User message with file attachments:
122
216
 
123
217
  ```typescript
124
- import { AgentInterface } from '@mariozechner/pi-web-ui';
218
+ const message: UserMessageWithAttachments = {
219
+ role: 'user-with-attachments',
220
+ content: 'Analyze this document',
221
+ attachments: [pdfAttachment],
222
+ timestamp: Date.now(),
223
+ };
125
224
 
126
- const chat = new AgentInterface();
127
- await chat.setAgent(agent);
225
+ // Type guard
226
+ if (isUserMessageWithAttachments(msg)) {
227
+ console.log(msg.attachments);
228
+ }
128
229
  ```
129
230
 
130
- ## Transports
231
+ ### ArtifactMessage
131
232
 
132
- Transport layers handle communication with AI providers.
233
+ For session persistence of artifacts:
133
234
 
134
- ### ProviderTransport
235
+ ```typescript
236
+ const artifact: ArtifactMessage = {
237
+ role: 'artifact',
238
+ action: 'create', // or 'update', 'delete'
239
+ filename: 'chart.html',
240
+ content: '<div>...</div>',
241
+ timestamp: new Date().toISOString(),
242
+ };
135
243
 
136
- The main transport that calls AI provider APIs using stored API keys.
244
+ // Type guard
245
+ if (isArtifactMessage(msg)) {
246
+ console.log(msg.filename);
247
+ }
248
+ ```
137
249
 
138
- ```typescript
139
- import { ProviderTransport } from '@mariozechner/pi-web-ui';
250
+ ### Custom Message Types
140
251
 
141
- const transport = new ProviderTransport();
252
+ Extend via declaration merging:
142
253
 
143
- const agent = new Agent({
144
- initialState: { /* ... */ },
145
- transport,
254
+ ```typescript
255
+ interface SystemNotification {
256
+ role: 'system-notification';
257
+ message: string;
258
+ level: 'info' | 'warning' | 'error';
259
+ timestamp: string;
260
+ }
261
+
262
+ declare module '@mariozechner/pi-agent-core' {
263
+ interface CustomAgentMessages {
264
+ 'system-notification': SystemNotification;
265
+ }
266
+ }
267
+
268
+ // Register renderer
269
+ registerMessageRenderer('system-notification', {
270
+ render: (msg) => html`<div class="alert">${msg.message}</div>`,
146
271
  });
272
+
273
+ // Extend convertToLlm
274
+ function myConvertToLlm(messages: AgentMessage[]): Message[] {
275
+ const processed = messages.map((m) => {
276
+ if (m.role === 'system-notification') {
277
+ return { role: 'user', content: `<system>${m.message}</system>`, timestamp: Date.now() };
278
+ }
279
+ return m;
280
+ });
281
+ return defaultConvertToLlm(processed);
282
+ }
147
283
  ```
148
284
 
149
- ### AppTransport
285
+ ## Message Transformer
150
286
 
151
- Alternative transport for proxying requests through a custom server.
287
+ `convertToLlm` transforms app messages to LLM-compatible format:
152
288
 
153
289
  ```typescript
154
- import { AppTransport } from '@mariozechner/pi-web-ui';
290
+ import { defaultConvertToLlm, convertAttachments } from '@mariozechner/pi-web-ui';
155
291
 
156
- const transport = new AppTransport();
292
+ // defaultConvertToLlm handles:
293
+ // - UserMessageWithAttachments → user message with image/text content blocks
294
+ // - ArtifactMessage → filtered out (UI-only)
295
+ // - Standard messages (user, assistant, toolResult) → passed through
296
+ ```
157
297
 
158
- const agent = new Agent({
159
- initialState: { /* ... */ },
160
- transport,
161
- });
298
+ ## Tools
299
+
300
+ ### JavaScript REPL
301
+
302
+ Execute JavaScript in a sandboxed browser environment:
303
+
304
+ ```typescript
305
+ import { createJavaScriptReplTool } from '@mariozechner/pi-web-ui';
306
+
307
+ const replTool = createJavaScriptReplTool();
308
+
309
+ // Configure runtime providers for artifact/attachment access
310
+ replTool.runtimeProvidersFactory = () => [
311
+ new AttachmentsRuntimeProvider(attachments),
312
+ new ArtifactsRuntimeProvider(artifactsPanel, agent, true), // read-write
313
+ ];
314
+
315
+ agent.setTools([replTool]);
162
316
  ```
163
317
 
164
- ## Tool Renderers
318
+ ### Extract Document
165
319
 
166
- Customize how tool calls and results are displayed.
320
+ Extract text from documents at URLs:
321
+
322
+ ```typescript
323
+ import { createExtractDocumentTool } from '@mariozechner/pi-web-ui';
324
+
325
+ const extractTool = createExtractDocumentTool();
326
+ extractTool.corsProxyUrl = 'https://corsproxy.io/?';
327
+
328
+ agent.setTools([extractTool]);
329
+ ```
330
+
331
+ ### Artifacts Tool
332
+
333
+ Built into ArtifactsPanel, supports: HTML, SVG, Markdown, text, JSON, images, PDF, DOCX, XLSX.
334
+
335
+ ```typescript
336
+ const artifactsPanel = new ArtifactsPanel();
337
+ artifactsPanel.agent = agent;
338
+
339
+ // The tool is available as artifactsPanel.tool
340
+ agent.setTools([artifactsPanel.tool]);
341
+ ```
342
+
343
+ ### Custom Tool Renderers
167
344
 
168
345
  ```typescript
169
346
  import { registerToolRenderer, type ToolRenderer } from '@mariozechner/pi-web-ui';
170
- import { html } from '@mariozechner/mini-lit';
171
347
 
172
348
  const myRenderer: ToolRenderer = {
173
- renderParams(params, isStreaming) {
174
- return html`<div>Calling tool with: ${JSON.stringify(params)}</div>`;
349
+ render(params, result, isStreaming) {
350
+ return {
351
+ content: html`<div>...</div>`,
352
+ isCustom: false, // true = no card wrapper
353
+ };
175
354
  },
176
-
177
- renderResult(params, result) {
178
- return html`<div>Result: ${result.output}</div>`;
179
- }
180
355
  };
181
356
 
182
357
  registerToolRenderer('my_tool', myRenderer);
@@ -184,147 +359,242 @@ registerToolRenderer('my_tool', myRenderer);
184
359
 
185
360
  ## Storage
186
361
 
187
- The package provides flexible storage backends for API keys, settings, and session persistence.
188
-
189
- ### AppStorage
190
-
191
- Central storage configuration for the application.
362
+ ### Setup
192
363
 
193
364
  ```typescript
194
- import { AppStorage, setAppStorage, SessionIndexedDBBackend } from '@mariozechner/pi-web-ui';
195
-
196
- const storage = new AppStorage({
197
- sessions: new SessionIndexedDBBackend('my-app-sessions'),
365
+ import {
366
+ AppStorage,
367
+ IndexedDBStorageBackend,
368
+ SettingsStore,
369
+ ProviderKeysStore,
370
+ SessionsStore,
371
+ CustomProvidersStore,
372
+ setAppStorage,
373
+ getAppStorage,
374
+ } from '@mariozechner/pi-web-ui';
375
+
376
+ // Create stores
377
+ const settings = new SettingsStore();
378
+ const providerKeys = new ProviderKeysStore();
379
+ const sessions = new SessionsStore();
380
+ const customProviders = new CustomProvidersStore();
381
+
382
+ // Create backend with all store configs
383
+ const backend = new IndexedDBStorageBackend({
384
+ dbName: 'my-app',
385
+ version: 1,
386
+ stores: [
387
+ settings.getConfig(),
388
+ providerKeys.getConfig(),
389
+ sessions.getConfig(),
390
+ SessionsStore.getMetadataConfig(),
391
+ customProviders.getConfig(),
392
+ ],
198
393
  });
199
394
 
395
+ // Wire stores to backend
396
+ settings.setBackend(backend);
397
+ providerKeys.setBackend(backend);
398
+ sessions.setBackend(backend);
399
+ customProviders.setBackend(backend);
400
+
401
+ // Create and set global storage
402
+ const storage = new AppStorage(settings, providerKeys, sessions, customProviders, backend);
200
403
  setAppStorage(storage);
201
404
  ```
202
405
 
203
- ### Available Backends
406
+ ### SettingsStore
407
+
408
+ Key-value settings:
409
+
410
+ ```typescript
411
+ await storage.settings.set('proxy.enabled', true);
412
+ await storage.settings.set('proxy.url', 'https://proxy.example.com');
413
+ const enabled = await storage.settings.get<boolean>('proxy.enabled');
414
+ ```
204
415
 
205
- - `LocalStorageBackend` - Uses browser localStorage
206
- - `IndexedDBBackend` - Uses IndexedDB for larger data
207
- - `SessionIndexedDBBackend` - Specialized for session storage
208
- - `WebExtensionStorageBackend` - For browser extensions using chrome.storage API
416
+ ### ProviderKeysStore
209
417
 
210
- ### Session Management
418
+ API keys by provider:
211
419
 
212
420
  ```typescript
213
- import { getAppStorage } from '@mariozechner/pi-web-ui';
421
+ await storage.providerKeys.set('anthropic', 'sk-ant-...');
422
+ const key = await storage.providerKeys.get('anthropic');
423
+ const providers = await storage.providerKeys.list();
424
+ ```
425
+
426
+ ### SessionsStore
214
427
 
215
- const storage = getAppStorage();
428
+ Chat sessions with metadata:
216
429
 
430
+ ```typescript
217
431
  // Save session
218
- await storage.sessions?.saveSession(sessionId, agentState, undefined, title);
432
+ await storage.sessions.save(sessionData, metadata);
219
433
 
220
434
  // Load session
221
- const sessionData = await storage.sessions?.loadSession(sessionId);
435
+ const data = await storage.sessions.get(sessionId);
436
+ const metadata = await storage.sessions.getMetadata(sessionId);
437
+
438
+ // List sessions (sorted by lastModified)
439
+ const allMetadata = await storage.sessions.getAllMetadata();
222
440
 
223
- // List sessions
224
- const sessions = await storage.sessions?.listSessions();
441
+ // Update title
442
+ await storage.sessions.updateTitle(sessionId, 'New Title');
443
+
444
+ // Delete
445
+ await storage.sessions.delete(sessionId);
225
446
  ```
226
447
 
227
- ## Styling
448
+ ### CustomProvidersStore
228
449
 
229
- The package includes pre-built Tailwind CSS with the Claude theme:
450
+ Custom LLM providers:
230
451
 
231
452
  ```typescript
232
- import '@mariozechner/pi-web-ui/app.css';
453
+ const provider: CustomProvider = {
454
+ id: crypto.randomUUID(),
455
+ name: 'My Ollama',
456
+ type: 'ollama',
457
+ baseUrl: 'http://localhost:11434',
458
+ };
459
+
460
+ await storage.customProviders.set(provider);
461
+ const all = await storage.customProviders.getAll();
233
462
  ```
234
463
 
235
- Or customize with your own Tailwind config:
464
+ ## Attachments
236
465
 
237
- ```css
238
- @import '@mariozechner/mini-lit/themes/claude.css';
239
- @tailwind base;
240
- @tailwind components;
241
- @tailwind utilities;
466
+ Load and process files:
467
+
468
+ ```typescript
469
+ import { loadAttachment, type Attachment } from '@mariozechner/pi-web-ui';
470
+
471
+ // From File input
472
+ const file = inputElement.files[0];
473
+ const attachment = await loadAttachment(file);
474
+
475
+ // From URL
476
+ const attachment = await loadAttachment('https://example.com/doc.pdf');
477
+
478
+ // From ArrayBuffer
479
+ const attachment = await loadAttachment(arrayBuffer, 'document.pdf');
480
+
481
+ // Attachment structure
482
+ interface Attachment {
483
+ id: string;
484
+ type: 'image' | 'document';
485
+ fileName: string;
486
+ mimeType: string;
487
+ size: number;
488
+ content: string; // base64 encoded
489
+ extractedText?: string; // For documents
490
+ preview?: string; // base64 preview image
491
+ }
242
492
  ```
243
493
 
244
- ## Dialogs
494
+ Supported formats: PDF, DOCX, XLSX, PPTX, images, text files.
245
495
 
246
- The package includes several dialog components for common interactions.
496
+ ## CORS Proxy
247
497
 
248
- ### SettingsDialog
498
+ For browser environments with CORS restrictions:
249
499
 
250
- Settings dialog with tabbed interface for API keys, proxy configuration, etc.
500
+ ```typescript
501
+ import { createStreamFn, shouldUseProxyForProvider, isCorsError } from '@mariozechner/pi-web-ui';
502
+
503
+ // AgentInterface auto-configures proxy from settings
504
+ // For manual setup:
505
+ agent.streamFn = createStreamFn(async () => {
506
+ const enabled = await storage.settings.get<boolean>('proxy.enabled');
507
+ return enabled ? await storage.settings.get<string>('proxy.url') : undefined;
508
+ });
509
+
510
+ // Providers requiring proxy:
511
+ // - zai: always
512
+ // - anthropic: only OAuth tokens (sk-ant-oat-*)
513
+ ```
514
+
515
+ ## Dialogs
516
+
517
+ ### SettingsDialog
251
518
 
252
519
  ```typescript
253
- import { SettingsDialog, ApiKeysTab, ProxyTab } from '@mariozechner/pi-web-ui';
520
+ import { SettingsDialog, ProvidersModelsTab, ProxyTab, ApiKeysTab } from '@mariozechner/pi-web-ui';
254
521
 
255
- // Open settings with tabs
256
- SettingsDialog.open([new ApiKeysTab(), new ProxyTab()]);
522
+ SettingsDialog.open([
523
+ new ProvidersModelsTab(), // Custom providers + model list
524
+ new ProxyTab(), // CORS proxy settings
525
+ new ApiKeysTab(), // API keys per provider
526
+ ]);
257
527
  ```
258
528
 
259
529
  ### SessionListDialog
260
530
 
261
- Display and load saved sessions.
262
-
263
531
  ```typescript
264
532
  import { SessionListDialog } from '@mariozechner/pi-web-ui';
265
533
 
266
- SessionListDialog.open(async (sessionId) => {
267
- await loadSession(sessionId);
268
- });
534
+ SessionListDialog.open(
535
+ async (sessionId) => { /* load session */ },
536
+ (deletedId) => { /* handle deletion */ },
537
+ );
269
538
  ```
270
539
 
271
540
  ### ApiKeyPromptDialog
272
541
 
273
- Prompt user for API key when needed.
274
-
275
542
  ```typescript
276
543
  import { ApiKeyPromptDialog } from '@mariozechner/pi-web-ui';
277
544
 
278
- const apiKey = await ApiKeyPromptDialog.prompt('anthropic');
545
+ const success = await ApiKeyPromptDialog.prompt('anthropic');
279
546
  ```
280
547
 
281
- ### PersistentStorageDialog
282
-
283
- Request persistent storage permission.
548
+ ### ModelSelector
284
549
 
285
550
  ```typescript
286
- import { PersistentStorageDialog } from '@mariozechner/pi-web-ui';
551
+ import { ModelSelector } from '@mariozechner/pi-web-ui';
287
552
 
288
- await PersistentStorageDialog.request();
553
+ ModelSelector.open(currentModel, (selectedModel) => {
554
+ agent.setModel(selectedModel);
555
+ });
289
556
  ```
290
557
 
291
- ## Platform Integration
558
+ ## Styling
292
559
 
293
- ### Browser Extension
560
+ Import the pre-built CSS:
294
561
 
295
562
  ```typescript
296
- import { AppStorage, WebExtensionStorageBackend, Agent, ProviderTransport } from '@mariozechner/pi-web-ui';
563
+ import '@mariozechner/pi-web-ui/app.css';
564
+ ```
297
565
 
298
- const storage = new AppStorage({
299
- providerKeys: new WebExtensionStorageBackend(),
300
- settings: new WebExtensionStorageBackend(),
301
- });
302
- setAppStorage(storage);
566
+ Or use Tailwind with custom config:
567
+
568
+ ```css
569
+ @import '@mariozechner/mini-lit/themes/claude.css';
570
+ @tailwind base;
571
+ @tailwind components;
572
+ @tailwind utilities;
303
573
  ```
304
574
 
305
- ### Web Application
575
+ ## Internationalization
306
576
 
307
577
  ```typescript
308
- import { AppStorage, SessionIndexedDBBackend, setAppStorage } from '@mariozechner/pi-web-ui';
578
+ import { i18n, setLanguage, translations } from '@mariozechner/pi-web-ui';
309
579
 
310
- const storage = new AppStorage({
311
- sessions: new SessionIndexedDBBackend('my-app-sessions'),
312
- });
313
- setAppStorage(storage);
580
+ // Add translations
581
+ translations.de = {
582
+ 'Loading...': 'Laden...',
583
+ 'No sessions yet': 'Noch keine Sitzungen',
584
+ };
585
+
586
+ setLanguage('de');
587
+ console.log(i18n('Loading...')); // "Laden..."
314
588
  ```
315
589
 
316
590
  ## Examples
317
591
 
318
- - [example/](./example) - Complete web application with session management
319
- - [sitegeist](https://github.com/badlogic/sitegeist) - Browser extension for AI-powered web navigation
320
-
321
- ## API Reference
322
-
323
- See [src/index.ts](src/index.ts) for the full public API.
592
+ - [example/](./example) - Complete web app with sessions, artifacts, custom messages
593
+ - [sitegeist](https://sitegeist.ai) - Browser extension using pi-web-ui
324
594
 
325
- ## Known Bugs
595
+ ## Known Issues
326
596
 
327
- - **PersistentStorageDialog**: Currently broken and commented out in examples. The dialog for requesting persistent storage does not work correctly and needs to be fixed.
597
+ - **PersistentStorageDialog**: Currently broken
328
598
 
329
599
  ## License
330
600