@desktalk/core 0.1.0-alpha.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 (281) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cli/index.d.ts +3 -0
  3. package/dist/cli/index.d.ts.map +1 -0
  4. package/dist/cli/index.js +82 -0
  5. package/dist/cli/index.js.map +1 -0
  6. package/dist/frontend/assets/frontend-B3QNYf3p.js +413 -0
  7. package/dist/frontend/assets/frontend-B3QNYf3p.js.map +1 -0
  8. package/dist/frontend/assets/frontend-B4aeXn9d.js +416 -0
  9. package/dist/frontend/assets/frontend-B4aeXn9d.js.map +1 -0
  10. package/dist/frontend/assets/frontend-BwyRSlHp.js +6513 -0
  11. package/dist/frontend/assets/frontend-BwyRSlHp.js.map +1 -0
  12. package/dist/frontend/assets/frontend-Dix2OWTT.js +229 -0
  13. package/dist/frontend/assets/frontend-Dix2OWTT.js.map +1 -0
  14. package/dist/frontend/assets/frontend-Dx41dEM9.js +407 -0
  15. package/dist/frontend/assets/frontend-Dx41dEM9.js.map +1 -0
  16. package/dist/frontend/assets/frontend-WpQng8Mt.js +1991 -0
  17. package/dist/frontend/assets/frontend-WpQng8Mt.js.map +1 -0
  18. package/dist/frontend/assets/frontend-rTwBdJbn.js +2123 -0
  19. package/dist/frontend/assets/frontend-rTwBdJbn.js.map +1 -0
  20. package/dist/frontend/assets/highlighted-body-TPN3WLV5-DD4wpkf4.js +2 -0
  21. package/dist/frontend/assets/highlighted-body-TPN3WLV5-DD4wpkf4.js.map +1 -0
  22. package/dist/frontend/assets/index-C5-XUOS7.js +1863 -0
  23. package/dist/frontend/assets/index-C5-XUOS7.js.map +1 -0
  24. package/dist/frontend/assets/index-C_e3_6yE.css +1 -0
  25. package/dist/frontend/index.html +22 -0
  26. package/dist/frontend/pcm-capture-processor.js +65 -0
  27. package/dist/i18n/manifest.json +34 -0
  28. package/dist/i18n/zh-CN.json +7 -0
  29. package/dist/index.d.ts +6 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +6 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/server/admin-routes.d.ts +14 -0
  34. package/dist/server/admin-routes.d.ts.map +1 -0
  35. package/dist/server/admin-routes.js +118 -0
  36. package/dist/server/admin-routes.js.map +1 -0
  37. package/dist/server/api-routes.d.ts +8 -0
  38. package/dist/server/api-routes.d.ts.map +1 -0
  39. package/dist/server/api-routes.js +203 -0
  40. package/dist/server/api-routes.js.map +1 -0
  41. package/dist/server/auth-routes.d.ts +19 -0
  42. package/dist/server/auth-routes.d.ts.map +1 -0
  43. package/dist/server/auth-routes.js +155 -0
  44. package/dist/server/auth-routes.js.map +1 -0
  45. package/dist/server/dtfs-routes.d.ts +3 -0
  46. package/dist/server/dtfs-routes.d.ts.map +1 -0
  47. package/dist/server/dtfs-routes.js +183 -0
  48. package/dist/server/dtfs-routes.js.map +1 -0
  49. package/dist/server/index.d.ts +15 -0
  50. package/dist/server/index.d.ts.map +1 -0
  51. package/dist/server/index.js +158 -0
  52. package/dist/server/index.js.map +1 -0
  53. package/dist/server/monaco-routes.d.ts +3 -0
  54. package/dist/server/monaco-routes.d.ts.map +1 -0
  55. package/dist/server/monaco-routes.js +45 -0
  56. package/dist/server/monaco-routes.js.map +1 -0
  57. package/dist/server/voice-routes.d.ts +7 -0
  58. package/dist/server/voice-routes.d.ts.map +1 -0
  59. package/dist/server/voice-routes.js +142 -0
  60. package/dist/server/voice-routes.js.map +1 -0
  61. package/dist/server/ws-routes.d.ts +29 -0
  62. package/dist/server/ws-routes.d.ts.map +1 -0
  63. package/dist/server/ws-routes.js +334 -0
  64. package/dist/server/ws-routes.js.map +1 -0
  65. package/dist/services/ai/action-tool.d.ts +9 -0
  66. package/dist/services/ai/action-tool.d.ts.map +1 -0
  67. package/dist/services/ai/action-tool.js +51 -0
  68. package/dist/services/ai/action-tool.js.map +1 -0
  69. package/dist/services/ai/app-tools.d.ts +15 -0
  70. package/dist/services/ai/app-tools.d.ts.map +1 -0
  71. package/dist/services/ai/app-tools.js +119 -0
  72. package/dist/services/ai/app-tools.js.map +1 -0
  73. package/dist/services/ai/chat-service.d.ts +27 -0
  74. package/dist/services/ai/chat-service.d.ts.map +1 -0
  75. package/dist/services/ai/chat-service.js +213 -0
  76. package/dist/services/ai/chat-service.js.map +1 -0
  77. package/dist/services/ai/create-liveapp-tool.d.ts +15 -0
  78. package/dist/services/ai/create-liveapp-tool.d.ts.map +1 -0
  79. package/dist/services/ai/create-liveapp-tool.js +142 -0
  80. package/dist/services/ai/create-liveapp-tool.js.map +1 -0
  81. package/dist/services/ai/desktop-tool.d.ts +27 -0
  82. package/dist/services/ai/desktop-tool.d.ts.map +1 -0
  83. package/dist/services/ai/desktop-tool.js +106 -0
  84. package/dist/services/ai/desktop-tool.js.map +1 -0
  85. package/dist/services/ai/edit-history.d.ts +16 -0
  86. package/dist/services/ai/edit-history.d.ts.map +1 -0
  87. package/dist/services/ai/edit-history.js +137 -0
  88. package/dist/services/ai/edit-history.js.map +1 -0
  89. package/dist/services/ai/edit-tool.d.ts +9 -0
  90. package/dist/services/ai/edit-tool.d.ts.map +1 -0
  91. package/dist/services/ai/edit-tool.js +113 -0
  92. package/dist/services/ai/edit-tool.js.map +1 -0
  93. package/dist/services/ai/generate-html-tool.d.ts +15 -0
  94. package/dist/services/ai/generate-html-tool.d.ts.map +1 -0
  95. package/dist/services/ai/generate-html-tool.js +140 -0
  96. package/dist/services/ai/generate-html-tool.js.map +1 -0
  97. package/dist/services/ai/generate-icon-tool.d.ts +10 -0
  98. package/dist/services/ai/generate-icon-tool.d.ts.map +1 -0
  99. package/dist/services/ai/generate-icon-tool.js +58 -0
  100. package/dist/services/ai/generate-icon-tool.js.map +1 -0
  101. package/dist/services/ai/html-bridge-script.d.ts +2 -0
  102. package/dist/services/ai/html-bridge-script.d.ts.map +1 -0
  103. package/dist/services/ai/html-bridge-script.js +2 -0
  104. package/dist/services/ai/html-bridge-script.js.map +1 -0
  105. package/dist/services/ai/html-guidelines-tool.d.ts +3 -0
  106. package/dist/services/ai/html-guidelines-tool.d.ts.map +1 -0
  107. package/dist/services/ai/html-guidelines-tool.js +157 -0
  108. package/dist/services/ai/html-guidelines-tool.js.map +1 -0
  109. package/dist/services/ai/html-stream-coordinator.d.ts +92 -0
  110. package/dist/services/ai/html-stream-coordinator.d.ts.map +1 -0
  111. package/dist/services/ai/html-stream-coordinator.js +314 -0
  112. package/dist/services/ai/html-stream-coordinator.js.map +1 -0
  113. package/dist/services/ai/html-theme-link.d.ts +9 -0
  114. package/dist/services/ai/html-theme-link.d.ts.map +1 -0
  115. package/dist/services/ai/html-theme-link.js +12 -0
  116. package/dist/services/ai/html-theme-link.js.map +1 -0
  117. package/dist/services/ai/html-ui-script.d.ts +9 -0
  118. package/dist/services/ai/html-ui-script.d.ts.map +1 -0
  119. package/dist/services/ai/html-ui-script.js +9 -0
  120. package/dist/services/ai/html-ui-script.js.map +1 -0
  121. package/dist/services/ai/image-generation-service.d.ts +26 -0
  122. package/dist/services/ai/image-generation-service.d.ts.map +1 -0
  123. package/dist/services/ai/image-generation-service.js +205 -0
  124. package/dist/services/ai/image-generation-service.js.map +1 -0
  125. package/dist/services/ai/manual-pages/desktop-actions.md +36 -0
  126. package/dist/services/ai/manual-pages/desktop-windows.md +24 -0
  127. package/dist/services/ai/manual-pages/dt-badge.md +36 -0
  128. package/dist/services/ai/manual-pages/dt-button.md +38 -0
  129. package/dist/services/ai/manual-pages/dt-card.md +22 -0
  130. package/dist/services/ai/manual-pages/dt-chart.md +261 -0
  131. package/dist/services/ai/manual-pages/dt-divider.md +25 -0
  132. package/dist/services/ai/manual-pages/dt-grid.md +29 -0
  133. package/dist/services/ai/manual-pages/dt-list-view.md +96 -0
  134. package/dist/services/ai/manual-pages/dt-markdown-editor.md +90 -0
  135. package/dist/services/ai/manual-pages/dt-markdown.md +86 -0
  136. package/dist/services/ai/manual-pages/dt-select.md +21 -0
  137. package/dist/services/ai/manual-pages/dt-stack.md +31 -0
  138. package/dist/services/ai/manual-pages/dt-stat.md +43 -0
  139. package/dist/services/ai/manual-pages/dt-table-view.md +120 -0
  140. package/dist/services/ai/manual-pages/dt-tooltip.md +18 -0
  141. package/dist/services/ai/manual-pages/editing-preview.md +25 -0
  142. package/dist/services/ai/manual-pages/html-actions.md +74 -0
  143. package/dist/services/ai/manual-pages/html-bridge.md +187 -0
  144. package/dist/services/ai/manual-pages/html-components.md +66 -0
  145. package/dist/services/ai/manual-pages/html-examples.md +185 -0
  146. package/dist/services/ai/manual-pages/html-layouts.md +128 -0
  147. package/dist/services/ai/manual-pages/html-storage.md +153 -0
  148. package/dist/services/ai/manual-pages/html-tokens.md +26 -0
  149. package/dist/services/ai/manual-pages/index.d.ts +10 -0
  150. package/dist/services/ai/manual-pages/index.d.ts.map +1 -0
  151. package/dist/services/ai/manual-pages/index.js +186 -0
  152. package/dist/services/ai/manual-pages/index.js.map +1 -0
  153. package/dist/services/ai/manual-tool.d.ts +3 -0
  154. package/dist/services/ai/manual-tool.d.ts.map +1 -0
  155. package/dist/services/ai/manual-tool.js +104 -0
  156. package/dist/services/ai/manual-tool.js.map +1 -0
  157. package/dist/services/ai/pi-session-service.d.ts +101 -0
  158. package/dist/services/ai/pi-session-service.d.ts.map +1 -0
  159. package/dist/services/ai/pi-session-service.js +697 -0
  160. package/dist/services/ai/pi-session-service.js.map +1 -0
  161. package/dist/services/ai/providers.d.ts +21 -0
  162. package/dist/services/ai/providers.d.ts.map +1 -0
  163. package/dist/services/ai/providers.js +93 -0
  164. package/dist/services/ai/providers.js.map +1 -0
  165. package/dist/services/ai/redo-edit-tool.d.ts +9 -0
  166. package/dist/services/ai/redo-edit-tool.d.ts.map +1 -0
  167. package/dist/services/ai/redo-edit-tool.js +44 -0
  168. package/dist/services/ai/redo-edit-tool.js.map +1 -0
  169. package/dist/services/ai/system-prompt.d.ts +7 -0
  170. package/dist/services/ai/system-prompt.d.ts.map +1 -0
  171. package/dist/services/ai/system-prompt.js +72 -0
  172. package/dist/services/ai/system-prompt.js.map +1 -0
  173. package/dist/services/ai/undo-edit-tool.d.ts +9 -0
  174. package/dist/services/ai/undo-edit-tool.d.ts.map +1 -0
  175. package/dist/services/ai/undo-edit-tool.js +44 -0
  176. package/dist/services/ai/undo-edit-tool.js.map +1 -0
  177. package/dist/services/ai/window-tools.d.ts +27 -0
  178. package/dist/services/ai/window-tools.d.ts.map +1 -0
  179. package/dist/services/ai/window-tools.js +155 -0
  180. package/dist/services/ai/window-tools.js.map +1 -0
  181. package/dist/services/backend-host.d.ts +10 -0
  182. package/dist/services/backend-host.d.ts.map +1 -0
  183. package/dist/services/backend-host.js +117 -0
  184. package/dist/services/backend-host.js.map +1 -0
  185. package/dist/services/backend-ipc.d.ts +60 -0
  186. package/dist/services/backend-ipc.d.ts.map +1 -0
  187. package/dist/services/backend-ipc.js +2 -0
  188. package/dist/services/backend-ipc.js.map +1 -0
  189. package/dist/services/backend-process-manager.d.ts +60 -0
  190. package/dist/services/backend-process-manager.d.ts.map +1 -0
  191. package/dist/services/backend-process-manager.js +203 -0
  192. package/dist/services/backend-process-manager.js.map +1 -0
  193. package/dist/services/filesystem.d.ts +8 -0
  194. package/dist/services/filesystem.d.ts.map +1 -0
  195. package/dist/services/filesystem.js +94 -0
  196. package/dist/services/filesystem.js.map +1 -0
  197. package/dist/services/i18n.d.ts +28 -0
  198. package/dist/services/i18n.d.ts.map +1 -0
  199. package/dist/services/i18n.js +76 -0
  200. package/dist/services/i18n.js.map +1 -0
  201. package/dist/services/liveapp-icon.d.ts +9 -0
  202. package/dist/services/liveapp-icon.d.ts.map +1 -0
  203. package/dist/services/liveapp-icon.js +28 -0
  204. package/dist/services/liveapp-icon.js.map +1 -0
  205. package/dist/services/liveapps.d.ts +12 -0
  206. package/dist/services/liveapps.d.ts.map +1 -0
  207. package/dist/services/liveapps.js +84 -0
  208. package/dist/services/liveapps.js.map +1 -0
  209. package/dist/services/logger.d.ts +34 -0
  210. package/dist/services/logger.d.ts.map +1 -0
  211. package/dist/services/logger.js +74 -0
  212. package/dist/services/logger.js.map +1 -0
  213. package/dist/services/messaging.d.ts +14 -0
  214. package/dist/services/messaging.d.ts.map +1 -0
  215. package/dist/services/messaging.js +41 -0
  216. package/dist/services/messaging.js.map +1 -0
  217. package/dist/services/miniapp-icon.d.ts +9 -0
  218. package/dist/services/miniapp-icon.d.ts.map +1 -0
  219. package/dist/services/miniapp-icon.js +25 -0
  220. package/dist/services/miniapp-icon.js.map +1 -0
  221. package/dist/services/miniapp-registry.d.ts +73 -0
  222. package/dist/services/miniapp-registry.d.ts.map +1 -0
  223. package/dist/services/miniapp-registry.js +144 -0
  224. package/dist/services/miniapp-registry.js.map +1 -0
  225. package/dist/services/onboarding-config.d.ts +37 -0
  226. package/dist/services/onboarding-config.d.ts.map +1 -0
  227. package/dist/services/onboarding-config.js +76 -0
  228. package/dist/services/onboarding-config.js.map +1 -0
  229. package/dist/services/preferences.d.ts +10 -0
  230. package/dist/services/preferences.d.ts.map +1 -0
  231. package/dist/services/preferences.js +48 -0
  232. package/dist/services/preferences.js.map +1 -0
  233. package/dist/services/proxy-dispatcher.d.ts +18 -0
  234. package/dist/services/proxy-dispatcher.d.ts.map +1 -0
  235. package/dist/services/proxy-dispatcher.js +33 -0
  236. package/dist/services/proxy-dispatcher.js.map +1 -0
  237. package/dist/services/storage.d.ts +7 -0
  238. package/dist/services/storage.d.ts.map +1 -0
  239. package/dist/services/storage.js +55 -0
  240. package/dist/services/storage.js.map +1 -0
  241. package/dist/services/theme-css.d.ts +21 -0
  242. package/dist/services/theme-css.d.ts.map +1 -0
  243. package/dist/services/theme-css.js +213 -0
  244. package/dist/services/theme-css.js.map +1 -0
  245. package/dist/services/user-db.d.ts +69 -0
  246. package/dist/services/user-db.d.ts.map +1 -0
  247. package/dist/services/user-db.js +175 -0
  248. package/dist/services/user-db.js.map +1 -0
  249. package/dist/services/voice/audio-format.d.ts +5 -0
  250. package/dist/services/voice/audio-format.d.ts.map +1 -0
  251. package/dist/services/voice/audio-format.js +27 -0
  252. package/dist/services/voice/audio-format.js.map +1 -0
  253. package/dist/services/voice/azure-openai-whisper-adapter.d.ts +23 -0
  254. package/dist/services/voice/azure-openai-whisper-adapter.d.ts.map +1 -0
  255. package/dist/services/voice/azure-openai-whisper-adapter.js +60 -0
  256. package/dist/services/voice/azure-openai-whisper-adapter.js.map +1 -0
  257. package/dist/services/voice/openai-whisper-adapter.d.ts +22 -0
  258. package/dist/services/voice/openai-whisper-adapter.d.ts.map +1 -0
  259. package/dist/services/voice/openai-whisper-adapter.js +61 -0
  260. package/dist/services/voice/openai-whisper-adapter.js.map +1 -0
  261. package/dist/services/voice/stt-adapter.d.ts +31 -0
  262. package/dist/services/voice/stt-adapter.d.ts.map +1 -0
  263. package/dist/services/voice/stt-adapter.js +8 -0
  264. package/dist/services/voice/stt-adapter.js.map +1 -0
  265. package/dist/services/voice/vad-segmenter.d.ts +68 -0
  266. package/dist/services/voice/vad-segmenter.d.ts.map +1 -0
  267. package/dist/services/voice/vad-segmenter.js +159 -0
  268. package/dist/services/voice/vad-segmenter.js.map +1 -0
  269. package/dist/services/voice/voice-session.d.ts +54 -0
  270. package/dist/services/voice/voice-session.d.ts.map +1 -0
  271. package/dist/services/voice/voice-session.js +137 -0
  272. package/dist/services/voice/voice-session.js.map +1 -0
  273. package/dist/services/window-manager.d.ts +94 -0
  274. package/dist/services/window-manager.d.ts.map +1 -0
  275. package/dist/services/window-manager.js +282 -0
  276. package/dist/services/window-manager.js.map +1 -0
  277. package/dist/services/workspace.d.ts +51 -0
  278. package/dist/services/workspace.d.ts.map +1 -0
  279. package/dist/services/workspace.js +144 -0
  280. package/dist/services/workspace.js.map +1 -0
  281. package/package.json +89 -0
@@ -0,0 +1,314 @@
1
+ import { parse as parsePartialJson, Allow } from 'partial-json';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { broadcastEvent } from '../messaging.js';
4
+ import { DEFAULT_THEME_PREFERENCES } from '@desktalk/ui/theme-css';
5
+ import { createHtmlBridgeScript } from './html-bridge-script.js';
6
+ import { createThemeLinkTag } from './html-theme-link.js';
7
+ import { UI_BUNDLE_SCRIPT_TAG } from './html-ui-script.js';
8
+ // ─── Coordinator ─────────────────────────────────────────────────────────────
9
+ let streamCounter = 0;
10
+ function nextStreamId() {
11
+ return `html-stream-${++streamCounter}-${Date.now()}`;
12
+ }
13
+ export class HtmlStreamCoordinator {
14
+ /** The currently active streaming session, or null. */
15
+ activeSession = null;
16
+ /**
17
+ * A promise chain that serializes all async operations (opening the window,
18
+ * sending the preamble, broadcasting chunks). Every call to `onToolcallDelta`
19
+ * appends to this chain so that operations execute in order without races.
20
+ */
21
+ pendingWork = Promise.resolve();
22
+ sendAiCommand;
23
+ activateMiniApp;
24
+ getPreference;
25
+ log;
26
+ constructor(options) {
27
+ this.sendAiCommand = options.sendAiCommand;
28
+ this.activateMiniApp = options.activateMiniApp;
29
+ this.getPreference = options.getPreference;
30
+ this.log = options.logger;
31
+ }
32
+ /**
33
+ * Returns the active session, if any.
34
+ */
35
+ getActiveSession() {
36
+ return this.activeSession;
37
+ }
38
+ /**
39
+ * Called when a `toolcall_start` event is detected for `create_liveapp`.
40
+ * Creates a new streaming session.
41
+ */
42
+ onToolcallStart() {
43
+ this.pendingWork = Promise.resolve();
44
+ const streamId = nextStreamId();
45
+ this.log.debug({ streamId }, 'onToolcallStart — new session');
46
+ this.activeSession = {
47
+ streamId,
48
+ bridgeToken: randomUUID(),
49
+ state: 'streaming',
50
+ streamedContentLength: 0,
51
+ accumulatedJson: '',
52
+ title: null,
53
+ windowOpened: false,
54
+ preambleSent: false,
55
+ windowId: null,
56
+ contentStartOffset: -1,
57
+ };
58
+ }
59
+ /**
60
+ * Called on each `toolcall_delta` event while a session is active.
61
+ *
62
+ * This method is synchronous — it accumulates the JSON fragment immediately,
63
+ * then queues any async work (window open, preamble, chunk broadcast) onto
64
+ * the serialized `pendingWork` chain so operations never race.
65
+ */
66
+ onToolcallDelta(delta) {
67
+ const session = this.activeSession;
68
+ if (!session || session.state !== 'streaming')
69
+ return;
70
+ // Skip empty deltas (OpenAI sometimes sends these on the first chunk)
71
+ if (!delta)
72
+ return;
73
+ session.accumulatedJson += delta;
74
+ // Attempt to parse partial JSON from the accumulated argument string
75
+ let parsed = null;
76
+ try {
77
+ parsed = parsePartialJson(session.accumulatedJson, Allow.ALL);
78
+ }
79
+ catch {
80
+ // Partial JSON not parseable yet — that's fine, wait for more data
81
+ return;
82
+ }
83
+ if (!parsed)
84
+ return;
85
+ // Extract title as soon as it's available
86
+ if (!session.title && typeof parsed.title === 'string' && parsed.title.length > 0) {
87
+ session.title = parsed.title;
88
+ this.log.debug({ title: session.title }, 'title extracted');
89
+ }
90
+ // Capture the parsed content value for the queued async work
91
+ const parsedTitle = session.title;
92
+ const parsedContent = typeof parsed.content === 'string' ? parsed.content : null;
93
+ const contentLen = parsedContent?.length ?? 0;
94
+ const alreadyStreamed = session.streamedContentLength;
95
+ // Only queue work if there's something new to do
96
+ const needsWindowOpen = parsedTitle && !session.windowOpened;
97
+ const hasNewContent = parsedContent !== null && contentLen > alreadyStreamed;
98
+ if (!needsWindowOpen && !hasNewContent)
99
+ return;
100
+ // Queue async work onto the serialized chain
101
+ this.pendingWork = this.pendingWork
102
+ .then(async () => {
103
+ // Re-check state — a previous step may have failed
104
+ if (session.state !== 'streaming')
105
+ return;
106
+ // Open the preview window once we have a title
107
+ if (parsedTitle && !session.windowOpened) {
108
+ this.log.debug({ title: parsedTitle }, 'opening preview window');
109
+ await this.openPreviewWindow(session);
110
+ if (session.state !== 'streaming') {
111
+ this.log.debug({ state: session.state }, 'openPreviewWindow failed');
112
+ return; // failed to open
113
+ }
114
+ this.log.debug({ windowId: session.windowId }, 'preview window opened');
115
+ }
116
+ // Stream new HTML content
117
+ if (parsedContent !== null && session.windowOpened) {
118
+ // Send the preamble (theme + bridge script) before any content
119
+ if (!session.preambleSent) {
120
+ this.log.debug('sending preamble');
121
+ await this.sendPreamble(session);
122
+ this.log.debug('preamble sent');
123
+ }
124
+ // The preamble already provides `<!DOCTYPE html><html><head>`.
125
+ // Detect the same structural prefix in the LLM content so we
126
+ // can skip it and avoid the browser seeing duplicate tags.
127
+ if (session.contentStartOffset < 0) {
128
+ const headMatch = parsedContent.match(/<head(\s[^>]*)?>|<head>/i);
129
+ if (headMatch && headMatch.index !== undefined) {
130
+ session.contentStartOffset = headMatch.index + headMatch[0].length;
131
+ this.log.debug({ contentStartOffset: session.contentStartOffset }, 'structural prefix detected — skipping');
132
+ }
133
+ else if (parsedContent.length > 200) {
134
+ // Content is long enough that <head> would have appeared by now.
135
+ // Assume there is no <head> and stream from the beginning.
136
+ session.contentStartOffset = 0;
137
+ this.log.debug('no <head> found after 200 chars — streaming from start');
138
+ }
139
+ else {
140
+ // Not enough content yet to decide; wait for more data.
141
+ return;
142
+ }
143
+ // Jump our cursor past the structural prefix
144
+ session.streamedContentLength = Math.max(session.streamedContentLength, session.contentStartOffset);
145
+ }
146
+ const newContent = parsedContent.slice(session.streamedContentLength);
147
+ if (newContent.length > 0) {
148
+ this.log.debug({
149
+ chunkLength: newContent.length,
150
+ totalStreamed: session.streamedContentLength + newContent.length,
151
+ }, 'broadcasting chunk');
152
+ broadcastEvent('preview', 'preview.html-chunk', {
153
+ streamId: session.streamId,
154
+ chunk: newContent,
155
+ });
156
+ session.streamedContentLength = parsedContent.length;
157
+ }
158
+ }
159
+ })
160
+ .catch((err) => {
161
+ // If any step in the chain fails, mark the session as failed
162
+ this.log.error({ err }, 'error in streaming chain');
163
+ if (session.state === 'streaming') {
164
+ session.state = 'failed';
165
+ }
166
+ });
167
+ }
168
+ /**
169
+ * Called when the `toolcall_end` event fires. The tool's `execute()` will run
170
+ * next with the complete params, so we just mark the LLM streaming as done.
171
+ */
172
+ onToolcallEnd() {
173
+ // Don't finalize here — execute() will handle the final flush + done signal.
174
+ }
175
+ /**
176
+ * Called by `create-liveapp-tool.ts` execute() to finalize the stream.
177
+ *
178
+ * Waits for all pending async work (window open, preamble, chunk sends) to
179
+ * complete, then sends any remaining content and the done signal.
180
+ *
181
+ * Returns the session info so execute() can build its result, or null if
182
+ * no streaming session was active (fallback to non-streaming path).
183
+ */
184
+ async finalize(finalContent) {
185
+ const session = this.activeSession;
186
+ this.log.debug({
187
+ hasSession: !!session,
188
+ state: session?.state,
189
+ windowOpened: session?.windowOpened,
190
+ streamedContentLength: session?.streamedContentLength,
191
+ finalContentLength: finalContent.length,
192
+ }, 'finalize() called');
193
+ if (!session || (session.state !== 'streaming' && session.state !== 'failed')) {
194
+ this.log.debug('finalize() — no active session or wrong state, returning null');
195
+ return null;
196
+ }
197
+ // Wait for all queued async work to finish
198
+ await this.pendingWork;
199
+ if (session.state === 'failed' || !session.windowOpened) {
200
+ this.log.debug('finalize() — session failed or window not opened, returning null');
201
+ this.activeSession = null;
202
+ return null;
203
+ }
204
+ // Send any remaining content that wasn't streamed during deltas.
205
+ // The `finalContent` here is the raw HTML content (not yet theme-injected),
206
+ // because theme/bridge were sent as a preamble.
207
+ if (session.preambleSent) {
208
+ // If contentStartOffset was never determined during streaming (e.g. very
209
+ // short content), detect it now from the final content.
210
+ if (session.contentStartOffset < 0) {
211
+ const headMatch = finalContent.match(/<head(\s[^>]*)?>|<head>/i);
212
+ session.contentStartOffset =
213
+ headMatch && headMatch.index !== undefined ? headMatch.index + headMatch[0].length : 0;
214
+ session.streamedContentLength = Math.max(session.streamedContentLength, session.contentStartOffset);
215
+ }
216
+ const remaining = finalContent.slice(session.streamedContentLength);
217
+ if (remaining.length > 0) {
218
+ this.log.debug({ remainingLength: remaining.length }, 'finalize() — sending remaining content');
219
+ broadcastEvent('preview', 'preview.html-chunk', {
220
+ streamId: session.streamId,
221
+ chunk: remaining,
222
+ });
223
+ }
224
+ }
225
+ // Signal that streaming is complete
226
+ this.log.debug({ streamId: session.streamId }, 'finalize() — sending html-done');
227
+ broadcastEvent('preview', 'preview.html-done', {
228
+ streamId: session.streamId,
229
+ html: finalContent,
230
+ });
231
+ session.state = 'done';
232
+ const result = { ...session };
233
+ this.activeSession = null;
234
+ return result;
235
+ }
236
+ /**
237
+ * Abort the current session (e.g. on error).
238
+ */
239
+ abort() {
240
+ const session = this.activeSession;
241
+ if (session && session.windowOpened) {
242
+ broadcastEvent('preview', 'preview.html-done', { streamId: session.streamId });
243
+ }
244
+ if (session) {
245
+ session.state = 'failed';
246
+ }
247
+ this.activeSession = null;
248
+ }
249
+ // ─── Private helpers ────────────────────────────────────────────────────
250
+ async openPreviewWindow(session) {
251
+ this.activateMiniApp('preview');
252
+ const commandResult = await this.sendAiCommand({
253
+ action: 'open',
254
+ miniAppId: 'preview',
255
+ title: session.title ?? 'HTML Preview',
256
+ args: {
257
+ streamId: session.streamId,
258
+ title: session.title ?? 'HTML Preview',
259
+ bridgeToken: session.bridgeToken,
260
+ },
261
+ });
262
+ if (!commandResult.ok) {
263
+ session.state = 'failed';
264
+ return;
265
+ }
266
+ session.windowId = commandResult.windowId ?? null;
267
+ session.windowOpened = true;
268
+ }
269
+ async sendPreamble(session) {
270
+ const themePreferences = await readThemePreferences(this.getPreference);
271
+ const themeLink = createThemeLinkTag(themePreferences.accentColor, themePreferences.theme);
272
+ const bridgeScript = createHtmlBridgeScript(session.streamId, session.bridgeToken);
273
+ // We send the preamble as the very first chunk, *before* the LLM's HTML.
274
+ // The LLM content typically starts with `<!DOCTYPE html><html><head>…`.
275
+ // To avoid the browser's parser creating implicit structure around our
276
+ // preamble (which would clash with the LLM's structural tags), we wrap
277
+ // the preamble in a structure the browser can merge cleanly:
278
+ //
279
+ // <!DOCTYPE html><html><head>
280
+ // <link rel="stylesheet" href="/api/ui/desktalk-theme.css?…">
281
+ // <script>…bridge…</script>
282
+ // <!-- preamble end -->
283
+ //
284
+ // Then, when the LLM's `<!DOCTYPE html><html><head>` arrives, the parser
285
+ // sees duplicate structural tags and simply ignores them (per the HTML
286
+ // spec, duplicate <html>/<head> tags are dropped). The LLM's <head>
287
+ // children (like <title>, <meta>, additional <style>) are adopted into
288
+ // the already-open <head>.
289
+ const preamble = '<!DOCTYPE html><html><head>\n' +
290
+ themeLink +
291
+ '\n' +
292
+ UI_BUNDLE_SCRIPT_TAG +
293
+ '\n' +
294
+ bridgeScript +
295
+ '\n';
296
+ broadcastEvent('preview', 'preview.html-chunk', {
297
+ streamId: session.streamId,
298
+ chunk: preamble,
299
+ });
300
+ session.preambleSent = true;
301
+ }
302
+ }
303
+ // ─── Utility (shared with create-liveapp-tool) ───────────────────────────────
304
+ async function readThemePreferences(getPreference) {
305
+ const accentColor = await getPreference('general.accentColor');
306
+ const theme = await getPreference('general.theme');
307
+ return {
308
+ accentColor: typeof accentColor === 'string' && accentColor
309
+ ? accentColor
310
+ : DEFAULT_THEME_PREFERENCES.accentColor,
311
+ theme: theme === 'light' ? 'light' : DEFAULT_THEME_PREFERENCES.theme,
312
+ };
313
+ }
314
+ //# sourceMappingURL=html-stream-coordinator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-stream-coordinator.js","sourceRoot":"","sources":["../../../src/services/ai/html-stream-coordinator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Returns a `<link>` tag that loads the DeskTalk theme + base stylesheet from
3
+ * the core server's `/api/ui/desktalk-theme.css` endpoint.
4
+ *
5
+ * Query parameters encode the user's current accent color and theme mode so
6
+ * the browser can cache each configuration independently.
7
+ */
8
+ export declare function createThemeLinkTag(accentColor: string, theme: string): string;
9
+ //# sourceMappingURL=html-theme-link.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-theme-link.d.ts","sourceRoot":"","sources":["../../../src/services/ai/html-theme-link.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG7E"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Returns a `<link>` tag that loads the DeskTalk theme + base stylesheet from
3
+ * the core server's `/api/ui/desktalk-theme.css` endpoint.
4
+ *
5
+ * Query parameters encode the user's current accent color and theme mode so
6
+ * the browser can cache each configuration independently.
7
+ */
8
+ export function createThemeLinkTag(accentColor, theme) {
9
+ const params = new URLSearchParams({ accent: accentColor, theme });
10
+ return `<link rel="stylesheet" href="/api/ui/desktalk-theme.css?${params.toString()}" data-dt-theme>`;
11
+ }
12
+ //# sourceMappingURL=html-theme-link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-theme-link.js","sourceRoot":"","sources":["../../../src/services/ai/html-theme-link.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,WAAmB,EAAE,KAAa;IACnE,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;IACnE,OAAO,2DAA2D,MAAM,CAAC,QAAQ,EAAE,kBAAkB,CAAC;AACxG,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Returns an inline `<script>` tag that loads the @desktalk/ui UMD bundle
3
+ * from the core server's `/api/ui/desktalk-ui.js` endpoint.
4
+ *
5
+ * This registers all DeskTalk web components (`<dt-card>`, `<dt-tooltip>`,
6
+ * etc.) inside the generated HTML iframe so AI-produced markup can use them.
7
+ */
8
+ export declare const UI_BUNDLE_SCRIPT_TAG = "<script src=\"/api/ui/desktalk-ui.js\" data-dt-ui></script>";
9
+ //# sourceMappingURL=html-ui-script.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-ui-script.d.ts","sourceRoot":"","sources":["../../../src/services/ai/html-ui-script.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,gEAA8D,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Returns an inline `<script>` tag that loads the @desktalk/ui UMD bundle
3
+ * from the core server's `/api/ui/desktalk-ui.js` endpoint.
4
+ *
5
+ * This registers all DeskTalk web components (`<dt-card>`, `<dt-tooltip>`,
6
+ * etc.) inside the generated HTML iframe so AI-produced markup can use them.
7
+ */
8
+ export const UI_BUNDLE_SCRIPT_TAG = '<script src="/api/ui/desktalk-ui.js" data-dt-ui></script>';
9
+ //# sourceMappingURL=html-ui-script.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-ui-script.js","sourceRoot":"","sources":["../../../src/services/ai/html-ui-script.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,2DAA2D,CAAC"}
@@ -0,0 +1,26 @@
1
+ import type pino from 'pino';
2
+ import { type PreferenceReader } from './providers.js';
3
+ import type { ModelRegistry } from '@mariozechner/pi-coding-agent';
4
+ export interface GeneratedIconResult {
5
+ image: Buffer;
6
+ provider: string;
7
+ model: string;
8
+ }
9
+ interface ImageGenerationServiceOptions {
10
+ modelRegistry: ModelRegistry;
11
+ getPreference: PreferenceReader;
12
+ logger: pino.Logger;
13
+ }
14
+ declare function getPrompt(description: string): string;
15
+ export declare class ImageGenerationService {
16
+ private readonly modelRegistry;
17
+ private readonly getPreference;
18
+ private readonly log;
19
+ constructor(options: ImageGenerationServiceOptions);
20
+ generateIcon(description: string): Promise<GeneratedIconResult>;
21
+ private generateOpenAiCompatibleIcon;
22
+ private generateGoogleIcon;
23
+ private generateOpenRouterIcon;
24
+ }
25
+ export { getPrompt as buildIconPrompt };
26
+ //# sourceMappingURL=image-generation-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-generation-service.d.ts","sourceRoot":"","sources":["../../../src/services/ai/image-generation-service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAkD,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AASnE,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,UAAU,6BAA6B;IACrC,aAAa,EAAE,aAAa,CAAC;IAC7B,aAAa,EAAE,gBAAgB,CAAC;IAChC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;CACrB;AAED,iBAAS,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE9C;AA0CD,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC;IAC/B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC;IAC/B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;gBAET,OAAO,EAAE,6BAA6B;IAM5C,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;YAyDvD,4BAA4B;YA4D5B,kBAAkB;YAoDlB,sBAAsB;CAyDrC;AAED,OAAO,EAAE,SAAS,IAAI,eAAe,EAAE,CAAC"}
@@ -0,0 +1,205 @@
1
+ import sharp from 'sharp';
2
+ import { getAiProviderPreferences, getDefaultAiProvider } from './providers.js';
3
+ const ICON_PROMPT_PREFIX = 'A minimal flat-design app icon: ${description}. Single centered symbol, solid color background, rounded square, clean and modern, no text, 256x256 pixels.';
4
+ const OPENAI_ICON_MODEL = 'dall-e-3';
5
+ const OPENROUTER_ICON_MODEL = 'google/gemini-3.1-flash-image-preview';
6
+ const GOOGLE_ICON_MODEL = 'gemini-2.0-flash-preview-image-generation';
7
+ function getPrompt(description) {
8
+ return ICON_PROMPT_PREFIX.replace('${description}', description.trim());
9
+ }
10
+ function resolveBaseUrl(provider, baseUrl) {
11
+ if (baseUrl)
12
+ return baseUrl.replace(/\/+$/, '');
13
+ switch (provider) {
14
+ case 'openai':
15
+ return 'https://api.openai.com/v1';
16
+ case 'openrouter':
17
+ return 'https://openrouter.ai/api/v1';
18
+ case 'google':
19
+ return 'https://generativelanguage.googleapis.com/v1beta';
20
+ default:
21
+ return '';
22
+ }
23
+ }
24
+ async function resizeToIconPng(input) {
25
+ return sharp(input).resize({ width: 256, height: 256, fit: 'cover' }).png().toBuffer();
26
+ }
27
+ function decodeBase64Image(data) {
28
+ return Buffer.from(data, 'base64');
29
+ }
30
+ function decodeDataUrlImage(dataUrl) {
31
+ const match = dataUrl.match(/^data:[^;]+;base64,(.+)$/);
32
+ if (!match) {
33
+ throw new Error('Provider returned an unsupported image URL format.');
34
+ }
35
+ return decodeBase64Image(match[1]);
36
+ }
37
+ function summarizeErrorBody(body) {
38
+ const trimmed = body.trim();
39
+ if (trimmed.length <= 1000) {
40
+ return trimmed;
41
+ }
42
+ return `${trimmed.slice(0, 1000)}...`;
43
+ }
44
+ export class ImageGenerationService {
45
+ modelRegistry;
46
+ getPreference;
47
+ log;
48
+ constructor(options) {
49
+ this.modelRegistry = options.modelRegistry;
50
+ this.getPreference = options.getPreference;
51
+ this.log = options.logger;
52
+ }
53
+ async generateIcon(description) {
54
+ const prompt = getPrompt(description);
55
+ const provider = await getDefaultAiProvider(this.getPreference);
56
+ const providerPreferences = await getAiProviderPreferences(this.getPreference, provider);
57
+ const apiKey = await this.modelRegistry.getApiKeyForProvider(provider);
58
+ const baseUrl = resolveBaseUrl(provider, providerPreferences.baseUrl);
59
+ if (provider !== 'openai' && provider !== 'openrouter' && provider !== 'google') {
60
+ throw new Error(`Current AI provider does not support icon generation yet: ${provider}`);
61
+ }
62
+ if (!apiKey) {
63
+ throw new Error(`No API key configured for ${provider}.`);
64
+ }
65
+ if (!baseUrl) {
66
+ throw new Error(`No base URL available for ${provider}.`);
67
+ }
68
+ this.log.debug({
69
+ provider,
70
+ baseUrl,
71
+ promptLength: prompt.length,
72
+ }, 'generating liveapp icon');
73
+ switch (provider) {
74
+ case 'openai':
75
+ return {
76
+ image: await this.generateOpenAiCompatibleIcon(baseUrl, apiKey, OPENAI_ICON_MODEL, prompt),
77
+ provider,
78
+ model: OPENAI_ICON_MODEL,
79
+ };
80
+ case 'openrouter':
81
+ return {
82
+ image: await this.generateOpenRouterIcon(baseUrl, apiKey, prompt),
83
+ provider,
84
+ model: OPENROUTER_ICON_MODEL,
85
+ };
86
+ case 'google':
87
+ return {
88
+ image: await this.generateGoogleIcon(baseUrl, apiKey, prompt),
89
+ provider,
90
+ model: GOOGLE_ICON_MODEL,
91
+ };
92
+ default:
93
+ throw new Error(`Unsupported icon provider: ${provider}`);
94
+ }
95
+ }
96
+ async generateOpenAiCompatibleIcon(baseUrl, apiKey, model, prompt, isOpenRouter = false) {
97
+ const endpoint = `${baseUrl}/images/generations`;
98
+ this.log.debug({ provider: isOpenRouter ? 'openrouter' : 'openai', endpoint, model }, 'requesting icon image');
99
+ const headers = {
100
+ 'Content-Type': 'application/json',
101
+ Authorization: `Bearer ${apiKey}`,
102
+ };
103
+ if (isOpenRouter) {
104
+ headers['HTTP-Referer'] = 'https://desktalk.local';
105
+ headers['X-Title'] = 'DeskTalk';
106
+ }
107
+ const response = await fetch(endpoint, {
108
+ method: 'POST',
109
+ headers,
110
+ body: JSON.stringify({
111
+ model,
112
+ prompt,
113
+ size: '1024x1024',
114
+ response_format: 'b64_json',
115
+ }),
116
+ });
117
+ if (!response.ok) {
118
+ const errorText = summarizeErrorBody(await response.text());
119
+ this.log.error({
120
+ provider: isOpenRouter ? 'openrouter' : 'openai',
121
+ endpoint,
122
+ model,
123
+ status: response.status,
124
+ errorText,
125
+ }, 'icon image request failed');
126
+ throw new Error(`Image request failed (${response.status}): ${errorText}`);
127
+ }
128
+ const result = (await response.json());
129
+ const base64Image = result.data?.[0]?.b64_json;
130
+ if (!base64Image) {
131
+ throw new Error('No image data returned by the provider.');
132
+ }
133
+ return resizeToIconPng(decodeBase64Image(base64Image));
134
+ }
135
+ async generateGoogleIcon(baseUrl, apiKey, prompt) {
136
+ const endpoint = `${baseUrl}/models/${encodeURIComponent(GOOGLE_ICON_MODEL)}:generateContent?key=${encodeURIComponent(apiKey)}`;
137
+ this.log.debug({ provider: 'google', endpoint, model: GOOGLE_ICON_MODEL }, 'requesting icon image');
138
+ const response = await fetch(endpoint, {
139
+ method: 'POST',
140
+ headers: {
141
+ 'Content-Type': 'application/json',
142
+ },
143
+ body: JSON.stringify({
144
+ contents: [{ parts: [{ text: prompt }] }],
145
+ generationConfig: {
146
+ responseModalities: ['TEXT', 'IMAGE'],
147
+ },
148
+ }),
149
+ });
150
+ if (!response.ok) {
151
+ const errorText = summarizeErrorBody(await response.text());
152
+ this.log.error({
153
+ provider: 'google',
154
+ endpoint,
155
+ model: GOOGLE_ICON_MODEL,
156
+ status: response.status,
157
+ errorText,
158
+ }, 'icon image request failed');
159
+ throw new Error(`Image request failed (${response.status}): ${errorText}`);
160
+ }
161
+ const result = (await response.json());
162
+ const base64Image = result.candidates?.[0]?.content?.parts?.find((part) => part.inlineData?.data)?.inlineData?.data;
163
+ if (!base64Image) {
164
+ throw new Error('No image data returned by Google.');
165
+ }
166
+ return resizeToIconPng(decodeBase64Image(base64Image));
167
+ }
168
+ async generateOpenRouterIcon(baseUrl, apiKey, prompt) {
169
+ const endpoint = `${baseUrl}/chat/completions`;
170
+ this.log.debug({ provider: 'openrouter', endpoint, model: OPENROUTER_ICON_MODEL }, 'requesting icon image');
171
+ const response = await fetch(endpoint, {
172
+ method: 'POST',
173
+ headers: {
174
+ 'Content-Type': 'application/json',
175
+ Authorization: `Bearer ${apiKey}`,
176
+ 'HTTP-Referer': 'https://desktalk.local',
177
+ 'X-Title': 'DeskTalk',
178
+ },
179
+ body: JSON.stringify({
180
+ model: OPENROUTER_ICON_MODEL,
181
+ messages: [{ role: 'user', content: prompt }],
182
+ modalities: ['image', 'text'],
183
+ }),
184
+ });
185
+ if (!response.ok) {
186
+ const errorText = summarizeErrorBody(await response.text());
187
+ this.log.error({
188
+ provider: 'openrouter',
189
+ endpoint,
190
+ model: OPENROUTER_ICON_MODEL,
191
+ status: response.status,
192
+ errorText,
193
+ }, 'icon image request failed');
194
+ throw new Error(`Image request failed (${response.status}): ${errorText}`);
195
+ }
196
+ const result = (await response.json());
197
+ const imageUrl = result.choices?.[0]?.message?.images?.[0]?.image_url?.url;
198
+ if (!imageUrl) {
199
+ throw new Error('No image data returned by OpenRouter.');
200
+ }
201
+ return resizeToIconPng(decodeDataUrlImage(imageUrl));
202
+ }
203
+ }
204
+ export { getPrompt as buildIconPrompt };
205
+ //# sourceMappingURL=image-generation-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-generation-service.js","sourceRoot":"","sources":["../../../src/services/ai/image-generation-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,wBAAwB,EAAE,oBAAoB,EAAyB,MAAM,aAAa,CAAC;AAGpG,MAAM,kBAAkB,GACtB,4JAA4J,CAAC;AAE/J,MAAM,iBAAiB,GAAG,UAAU,CAAC;AACrC,MAAM,qBAAqB,GAAG,uCAAuC,CAAC;AACtE,MAAM,iBAAiB,GAAG,2CAA2C,CAAC;AActE,SAAS,SAAS,CAAC,WAAmB;IACpC,OAAO,kBAAkB,CAAC,OAAO,CAAC,gBAAgB,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;AAC1E,CAAC;AAED,SAAS,cAAc,CAAC,QAAgB,EAAE,OAAgB;IACxD,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAEhD,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,QAAQ;YACX,OAAO,2BAA2B,CAAC;QACrC,KAAK,YAAY;YACf,OAAO,8BAA8B,CAAC;QACxC,KAAK,QAAQ;YACX,OAAO,kDAAkD,CAAC;QAC5D;YACE,OAAO,EAAE,CAAC;IACd,CAAC;AACH,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,KAAa;IAC1C,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;AACzF,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAY;IACrC,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AACrC,CAAC;AAED,SAAS,kBAAkB,CAAC,OAAe;IACzC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;IACxD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACxE,CAAC;IAED,OAAO,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AACrC,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAY;IACtC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,IAAI,OAAO,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;QAC3B,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC;AACxC,CAAC;AAED,MAAM,OAAO,sBAAsB;IAChB,aAAa,CAAC;IACd,aAAa,CAAC;IACd,GAAG,CAAC;IAErB,YAAY,OAAsC;QAChD,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;QAC3C,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;QAC3C,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,WAAmB;QACpC,MAAM,MAAM,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC;QACtC,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAChE,MAAM,mBAAmB,GAAG,MAAM,wBAAwB,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;QACzF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC;QACvE,MAAM,OAAO,GAAG,cAAc,CAAC,QAAQ,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC;QAEtE,IAAI,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,YAAY,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAChF,MAAM,IAAI,KAAK,CAAC,6DAA6D,QAAQ,EAAE,CAAC,CAAC;QAC3F,CAAC;QAED,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,6BAA6B,QAAQ,GAAG,CAAC,CAAC;QAC5D,CAAC;QAED,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,6BAA6B,QAAQ,GAAG,CAAC,CAAC;QAC5D,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,KAAK,CACZ;YACE,QAAQ;YACR,OAAO;YACP,YAAY,EAAE,MAAM,CAAC,MAAM;SAC5B,EACD,yBAAyB,CAC1B,CAAC;QAEF,QAAQ,QAAQ,EAAE,CAAC;YACjB,KAAK,QAAQ;gBACX,OAAO;oBACL,KAAK,EAAE,MAAM,IAAI,CAAC,4BAA4B,CAC5C,OAAO,EACP,MAAM,EACN,iBAAiB,EACjB,MAAM,CACP;oBACD,QAAQ;oBACR,KAAK,EAAE,iBAAiB;iBACzB,CAAC;YACJ,KAAK,YAAY;gBACf,OAAO;oBACL,KAAK,EAAE,MAAM,IAAI,CAAC,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC;oBACjE,QAAQ;oBACR,KAAK,EAAE,qBAAqB;iBAC7B,CAAC;YACJ,KAAK,QAAQ;gBACX,OAAO;oBACL,KAAK,EAAE,MAAM,IAAI,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC;oBAC7D,QAAQ;oBACR,KAAK,EAAE,iBAAiB;iBACzB,CAAC;YACJ;gBACE,MAAM,IAAI,KAAK,CAAC,8BAA8B,QAAQ,EAAE,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,4BAA4B,CACxC,OAAe,EACf,MAAc,EACd,KAAa,EACb,MAAc,EACd,YAAY,GAAG,KAAK;QAEpB,MAAM,QAAQ,GAAG,GAAG,OAAO,qBAAqB,CAAC;QACjD,IAAI,CAAC,GAAG,CAAC,KAAK,CACZ,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,EACrE,uBAAuB,CACxB,CAAC;QAEF,MAAM,OAAO,GAA2B;YACtC,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,UAAU,MAAM,EAAE;SAClC,CAAC;QAEF,IAAI,YAAY,EAAE,CAAC;YACjB,OAAO,CAAC,cAAc,CAAC,GAAG,wBAAwB,CAAC;YACnD,OAAO,CAAC,SAAS,CAAC,GAAG,UAAU,CAAC;QAClC,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,QAAQ,EAAE;YACrC,MAAM,EAAE,MAAM;YACd,OAAO;YACP,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,KAAK;gBACL,MAAM;gBACN,IAAI,EAAE,WAAW;gBACjB,eAAe,EAAE,UAAU;aAC5B,CAAC;SACH,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,SAAS,GAAG,kBAAkB,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;YAC5D,IAAI,CAAC,GAAG,CAAC,KAAK,CACZ;gBACE,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ;gBAChD,QAAQ;gBACR,KAAK;gBACL,MAAM,EAAE,QAAQ,CAAC,MAAM;gBACvB,SAAS;aACV,EACD,2BAA2B,CAC5B,CAAC;YACF,MAAM,IAAI,KAAK,CAAC,yBAAyB,QAAQ,CAAC,MAAM,MAAM,SAAS,EAAE,CAAC,CAAC;QAC7E,CAAC;QAED,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAEpC,CAAC;QACF,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC;QAC/C,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;QAC7D,CAAC;QAED,OAAO,eAAe,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC;IACzD,CAAC;IAEO,KAAK,CAAC,kBAAkB,CAC9B,OAAe,EACf,MAAc,EACd,MAAc;QAEd,MAAM,QAAQ,GAAG,GAAG,OAAO,WAAW,kBAAkB,CAAC,iBAAiB,CAAC,wBAAwB,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC;QAChI,IAAI,CAAC,GAAG,CAAC,KAAK,CACZ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAC1D,uBAAuB,CACxB,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,QAAQ,EAAE;YACrC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;aACnC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;gBACzC,gBAAgB,EAAE;oBAChB,kBAAkB,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC;iBACtC;aACF,CAAC;SACH,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,SAAS,GAAG,kBAAkB,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;YAC5D,IAAI,CAAC,GAAG,CAAC,KAAK,CACZ;gBACE,QAAQ,EAAE,QAAQ;gBAClB,QAAQ;gBACR,KAAK,EAAE,iBAAiB;gBACxB,MAAM,EAAE,QAAQ,CAAC,MAAM;gBACvB,SAAS;aACV,EACD,2BAA2B,CAC5B,CAAC;YACF,MAAM,IAAI,KAAK,CAAC,yBAAyB,QAAQ,CAAC,MAAM,MAAM,SAAS,EAAE,CAAC,CAAC;QAC7E,CAAC;QAED,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAEpC,CAAC;QACF,MAAM,WAAW,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAC9D,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAChC,EAAE,UAAU,EAAE,IAAI,CAAC;QACpB,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;QACvD,CAAC;QAED,OAAO,eAAe,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC;IACzD,CAAC;IAEO,KAAK,CAAC,sBAAsB,CAClC,OAAe,EACf,MAAc,EACd,MAAc;QAEd,MAAM,QAAQ,GAAG,GAAG,OAAO,mBAAmB,CAAC;QAC/C,IAAI,CAAC,GAAG,CAAC,KAAK,CACZ,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,qBAAqB,EAAE,EAClE,uBAAuB,CACxB,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,QAAQ,EAAE;YACrC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,UAAU,MAAM,EAAE;gBACjC,cAAc,EAAE,wBAAwB;gBACxC,SAAS,EAAE,UAAU;aACtB;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,KAAK,EAAE,qBAAqB;gBAC5B,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;gBAC7C,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;aAC9B,CAAC;SACH,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,SAAS,GAAG,kBAAkB,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;YAC5D,IAAI,CAAC,GAAG,CAAC,KAAK,CACZ;gBACE,QAAQ,EAAE,YAAY;gBACtB,QAAQ;gBACR,KAAK,EAAE,qBAAqB;gBAC5B,MAAM,EAAE,QAAQ,CAAC,MAAM;gBACvB,SAAS;aACV,EACD,2BAA2B,CAC5B,CAAC;YACF,MAAM,IAAI,KAAK,CAAC,yBAAyB,QAAQ,CAAC,MAAM,MAAM,SAAS,EAAE,CAAC,CAAC;QAC7E,CAAC;QAED,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAQpC,CAAC;QACF,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,GAAG,CAAC;QAC3E,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;QAC3D,CAAC;QAED,OAAO,eAAe,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC;IACvD,CAAC;CACF;AAED,OAAO,EAAE,SAAS,IAAI,eAAe,EAAE,CAAC"}