aimeat 2.4.0 → 2.6.0

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 (308) hide show
  1. package/dist/.env.example +33 -0
  2. package/dist/build-stamp.json +13 -0
  3. package/dist/locales/en.json +186 -20
  4. package/dist/locales/fi.json +186 -20
  5. package/dist/public/components/VoiceRecorder.js +116 -0
  6. package/dist/public/connection-done.html +53 -0
  7. package/dist/public/css/components/voice-recorder.css +66 -0
  8. package/dist/public/css/views/inbox.css +41 -0
  9. package/dist/public/css/views/profile.css +171 -95
  10. package/dist/public/css/views/transparency.css +5 -0
  11. package/dist/public/js/services/audio-recorder.js +171 -0
  12. package/dist/public/js/services/messages.js +21 -2
  13. package/dist/public/spa.html +3 -0
  14. package/dist/public/views/landing.js +4 -0
  15. package/dist/public/views/profile/access-tab/connections.js +264 -0
  16. package/dist/public/views/profile/access-tab.js +2 -0
  17. package/dist/public/views/profile/ai-transparency-card.js +22 -17
  18. package/dist/public/views/profile/inbox-tab/components.js +60 -10
  19. package/dist/public/views/profile/inbox-tab/helpers.js +33 -0
  20. package/dist/public/views/profile/inbox-tab/panels.js +6 -1
  21. package/dist/public/views/profile/inbox-tab/use-voice.js +50 -0
  22. package/dist/public/views/profile/inbox-tab/voice-parts.js +111 -0
  23. package/dist/public/views/profile/inbox-tab.js +18 -24
  24. package/dist/public/views/profile/openrouter/budget-panel.js +224 -0
  25. package/dist/public/views/profile/openrouter/model-picker.js +150 -0
  26. package/dist/public/views/profile/openrouter/pricing.js +139 -0
  27. package/dist/public/views/profile/openrouter-settings.js +425 -488
  28. package/dist/public/views/transparency.js +50 -0
  29. package/dist/scripts/build-sdk-libs.d.ts.map +1 -1
  30. package/dist/scripts/build-sdk-libs.js +1 -0
  31. package/dist/scripts/build-sdk-libs.js.map +1 -1
  32. package/dist/scripts/check-ai-disclosure.d.ts.map +1 -1
  33. package/dist/scripts/check-ai-disclosure.js +182 -0
  34. package/dist/scripts/check-ai-disclosure.js.map +1 -1
  35. package/dist/src/cli/connect/ai-provenance-carry.d.ts +247 -0
  36. package/dist/src/cli/connect/ai-provenance-carry.d.ts.map +1 -0
  37. package/dist/src/cli/connect/ai-provenance-carry.js +334 -0
  38. package/dist/src/cli/connect/ai-provenance-carry.js.map +1 -0
  39. package/dist/src/cli/connect/api-client.d.ts +11 -0
  40. package/dist/src/cli/connect/api-client.d.ts.map +1 -1
  41. package/dist/src/cli/connect/api-client.js +2 -0
  42. package/dist/src/cli/connect/api-client.js.map +1 -1
  43. package/dist/src/cli/connect/mcp/local-server.d.ts.map +1 -1
  44. package/dist/src/cli/connect/mcp/local-server.js +11 -0
  45. package/dist/src/cli/connect/mcp/local-server.js.map +1 -1
  46. package/dist/src/cli/connect/mcp/tools/agent-messages.d.ts +2 -0
  47. package/dist/src/cli/connect/mcp/tools/agent-messages.d.ts.map +1 -1
  48. package/dist/src/cli/connect/mcp/tools/agent-messages.js +5 -2
  49. package/dist/src/cli/connect/mcp/tools/agent-messages.js.map +1 -1
  50. package/dist/src/cli/connect/mcp/tools/agent-tasks.d.ts +2 -0
  51. package/dist/src/cli/connect/mcp/tools/agent-tasks.d.ts.map +1 -1
  52. package/dist/src/cli/connect/mcp/tools/agent-tasks.js +5 -2
  53. package/dist/src/cli/connect/mcp/tools/agent-tasks.js.map +1 -1
  54. package/dist/src/cli/connect/mcp/tools/apps.d.ts +3 -0
  55. package/dist/src/cli/connect/mcp/tools/apps.d.ts.map +1 -1
  56. package/dist/src/cli/connect/mcp/tools/apps.js +13 -5
  57. package/dist/src/cli/connect/mcp/tools/apps.js.map +1 -1
  58. package/dist/src/cli/connect/mcp/tools/boards.d.ts +3 -0
  59. package/dist/src/cli/connect/mcp/tools/boards.d.ts.map +1 -1
  60. package/dist/src/cli/connect/mcp/tools/boards.js +5 -2
  61. package/dist/src/cli/connect/mcp/tools/boards.js.map +1 -1
  62. package/dist/src/cli/connect/mcp/tools/core.d.ts +5 -0
  63. package/dist/src/cli/connect/mcp/tools/core.d.ts.map +1 -1
  64. package/dist/src/cli/connect/mcp/tools/core.js +25 -5
  65. package/dist/src/cli/connect/mcp/tools/core.js.map +1 -1
  66. package/dist/src/cli/connect/mcp/tools/dm-messages.d.ts +2 -0
  67. package/dist/src/cli/connect/mcp/tools/dm-messages.d.ts.map +1 -1
  68. package/dist/src/cli/connect/mcp/tools/dm-messages.js +11 -6
  69. package/dist/src/cli/connect/mcp/tools/dm-messages.js.map +1 -1
  70. package/dist/src/cli/connect/mcp/tools/exchange.d.ts +2 -0
  71. package/dist/src/cli/connect/mcp/tools/exchange.d.ts.map +1 -1
  72. package/dist/src/cli/connect/mcp/tools/exchange.js +8 -2
  73. package/dist/src/cli/connect/mcp/tools/exchange.js.map +1 -1
  74. package/dist/src/cli/connect/mcp/tools/knowledge.d.ts +3 -0
  75. package/dist/src/cli/connect/mcp/tools/knowledge.d.ts.map +1 -1
  76. package/dist/src/cli/connect/mcp/tools/knowledge.js +7 -3
  77. package/dist/src/cli/connect/mcp/tools/knowledge.js.map +1 -1
  78. package/dist/src/cli/connect/mcp/tools/organisms.d.ts +2 -0
  79. package/dist/src/cli/connect/mcp/tools/organisms.d.ts.map +1 -1
  80. package/dist/src/cli/connect/mcp/tools/organisms.js +7 -2
  81. package/dist/src/cli/connect/mcp/tools/organisms.js.map +1 -1
  82. package/dist/src/cli/connect/mcp/tools/workspaces.d.ts +3 -0
  83. package/dist/src/cli/connect/mcp/tools/workspaces.d.ts.map +1 -1
  84. package/dist/src/cli/connect/mcp/tools/workspaces.js +8 -2
  85. package/dist/src/cli/connect/mcp/tools/workspaces.js.map +1 -1
  86. package/dist/src/cli/connect/tool-call-defs-core.d.ts +3 -0
  87. package/dist/src/cli/connect/tool-call-defs-core.d.ts.map +1 -1
  88. package/dist/src/cli/connect/tool-call-defs-core.js +7 -0
  89. package/dist/src/cli/connect/tool-call-defs-core.js.map +1 -1
  90. package/dist/src/cli/connect/tool-call.d.ts +3 -0
  91. package/dist/src/cli/connect/tool-call.d.ts.map +1 -1
  92. package/dist/src/cli/connect/tool-call.js +11 -1
  93. package/dist/src/cli/connect/tool-call.js.map +1 -1
  94. package/dist/src/config-types.d.ts +14 -0
  95. package/dist/src/config-types.d.ts.map +1 -1
  96. package/dist/src/config.d.ts.map +1 -1
  97. package/dist/src/config.js +10 -0
  98. package/dist/src/config.js.map +1 -1
  99. package/dist/src/data/public-pages.d.ts +12 -0
  100. package/dist/src/data/public-pages.d.ts.map +1 -1
  101. package/dist/src/data/public-pages.js +12 -0
  102. package/dist/src/data/public-pages.js.map +1 -1
  103. package/dist/src/generated/api-types.d.ts +969 -85
  104. package/dist/src/generated/api-types.d.ts.map +1 -1
  105. package/dist/src/mcp/ai-provenance-input.d.ts +45 -2
  106. package/dist/src/mcp/ai-provenance-input.d.ts.map +1 -1
  107. package/dist/src/mcp/ai-provenance-input.js +48 -2
  108. package/dist/src/mcp/ai-provenance-input.js.map +1 -1
  109. package/dist/src/mcp/apps.d.ts +7 -0
  110. package/dist/src/mcp/apps.d.ts.map +1 -1
  111. package/dist/src/mcp/apps.js +20 -2
  112. package/dist/src/mcp/apps.js.map +1 -1
  113. package/dist/src/models/ai-provenance-schemas.d.ts +7 -1
  114. package/dist/src/models/ai-provenance-schemas.d.ts.map +1 -1
  115. package/dist/src/models/ai-provenance-schemas.js +11 -1
  116. package/dist/src/models/ai-provenance-schemas.js.map +1 -1
  117. package/dist/src/models/connection-schemas.d.ts +203 -0
  118. package/dist/src/models/connection-schemas.d.ts.map +1 -0
  119. package/dist/src/models/connection-schemas.js +33 -0
  120. package/dist/src/models/connection-schemas.js.map +1 -0
  121. package/dist/src/models/message-schemas.d.ts +40 -0
  122. package/dist/src/models/message-schemas.d.ts.map +1 -1
  123. package/dist/src/models/message-schemas.js +23 -0
  124. package/dist/src/models/message-schemas.js.map +1 -1
  125. package/dist/src/routes/ai-provenance.d.ts +6 -0
  126. package/dist/src/routes/ai-provenance.d.ts.map +1 -1
  127. package/dist/src/routes/ai-provenance.js +34 -1
  128. package/dist/src/routes/ai-provenance.js.map +1 -1
  129. package/dist/src/routes/ai-transparency.d.ts +6 -1
  130. package/dist/src/routes/ai-transparency.d.ts.map +1 -1
  131. package/dist/src/routes/ai-transparency.js +17 -3
  132. package/dist/src/routes/ai-transparency.js.map +1 -1
  133. package/dist/src/routes/ai.d.ts +5 -0
  134. package/dist/src/routes/ai.d.ts.map +1 -1
  135. package/dist/src/routes/ai.js +83 -0
  136. package/dist/src/routes/ai.js.map +1 -1
  137. package/dist/src/routes/app-grants.d.ts.map +1 -1
  138. package/dist/src/routes/app-grants.js +4 -0
  139. package/dist/src/routes/app-grants.js.map +1 -1
  140. package/dist/src/routes/apps/drafts.d.ts +3 -0
  141. package/dist/src/routes/apps/drafts.d.ts.map +1 -1
  142. package/dist/src/routes/apps/drafts.js +13 -0
  143. package/dist/src/routes/apps/drafts.js.map +1 -1
  144. package/dist/src/routes/apps/publish.d.ts +5 -0
  145. package/dist/src/routes/apps/publish.d.ts.map +1 -1
  146. package/dist/src/routes/apps/publish.js +21 -5
  147. package/dist/src/routes/apps/publish.js.map +1 -1
  148. package/dist/src/routes/connections.d.ts +35 -0
  149. package/dist/src/routes/connections.d.ts.map +1 -0
  150. package/dist/src/routes/connections.js +406 -0
  151. package/dist/src/routes/connections.js.map +1 -0
  152. package/dist/src/routes/libs.js +1 -1
  153. package/dist/src/routes/libs.js.map +1 -1
  154. package/dist/src/routes/messages.d.ts +5 -0
  155. package/dist/src/routes/messages.d.ts.map +1 -1
  156. package/dist/src/routes/messages.js +92 -0
  157. package/dist/src/routes/messages.js.map +1 -1
  158. package/dist/src/routes/openrouter.d.ts +6 -1
  159. package/dist/src/routes/openrouter.d.ts.map +1 -1
  160. package/dist/src/routes/openrouter.js +37 -7
  161. package/dist/src/routes/openrouter.js.map +1 -1
  162. package/dist/src/routes/upload.d.ts +5 -0
  163. package/dist/src/routes/upload.d.ts.map +1 -1
  164. package/dist/src/routes/upload.js +32 -0
  165. package/dist/src/routes/upload.js.map +1 -1
  166. package/dist/src/server-bootstrap/routes-loader.d.ts.map +1 -1
  167. package/dist/src/server-bootstrap/routes-loader.js +2 -0
  168. package/dist/src/server-bootstrap/routes-loader.js.map +1 -1
  169. package/dist/src/services/ai-completion.d.ts +48 -0
  170. package/dist/src/services/ai-completion.d.ts.map +1 -1
  171. package/dist/src/services/ai-completion.js +112 -72
  172. package/dist/src/services/ai-completion.js.map +1 -1
  173. package/dist/src/services/ai-disclosure.d.ts +6 -0
  174. package/dist/src/services/ai-disclosure.d.ts.map +1 -1
  175. package/dist/src/services/ai-disclosure.js +20 -2
  176. package/dist/src/services/ai-disclosure.js.map +1 -1
  177. package/dist/src/services/ai-provenance-page.d.ts +59 -0
  178. package/dist/src/services/ai-provenance-page.d.ts.map +1 -0
  179. package/dist/src/services/ai-provenance-page.js +185 -0
  180. package/dist/src/services/ai-provenance-page.js.map +1 -0
  181. package/dist/src/services/ai-provenance.d.ts +6 -0
  182. package/dist/src/services/ai-provenance.d.ts.map +1 -1
  183. package/dist/src/services/ai-provenance.js +5 -0
  184. package/dist/src/services/ai-provenance.js.map +1 -1
  185. package/dist/src/services/ai-transcription.d.ts +66 -0
  186. package/dist/src/services/ai-transcription.d.ts.map +1 -0
  187. package/dist/src/services/ai-transcription.js +90 -0
  188. package/dist/src/services/ai-transcription.js.map +1 -0
  189. package/dist/src/services/app-publish.d.ts.map +1 -1
  190. package/dist/src/services/app-publish.js +16 -1
  191. package/dist/src/services/app-publish.js.map +1 -1
  192. package/dist/src/services/build-app-prompt.d.ts.map +1 -1
  193. package/dist/src/services/build-app-prompt.js +2 -1
  194. package/dist/src/services/build-app-prompt.js.map +1 -1
  195. package/dist/src/services/connections/attach.d.ts +62 -0
  196. package/dist/src/services/connections/attach.d.ts.map +1 -0
  197. package/dist/src/services/connections/attach.js +168 -0
  198. package/dist/src/services/connections/attach.js.map +1 -0
  199. package/dist/src/services/connections/credential.d.ts +39 -0
  200. package/dist/src/services/connections/credential.d.ts.map +1 -0
  201. package/dist/src/services/connections/credential.js +66 -0
  202. package/dist/src/services/connections/credential.js.map +1 -0
  203. package/dist/src/services/connections/instance.d.ts +65 -0
  204. package/dist/src/services/connections/instance.d.ts.map +1 -0
  205. package/dist/src/services/connections/instance.js +144 -0
  206. package/dist/src/services/connections/instance.js.map +1 -0
  207. package/dist/src/services/connections/oauth.d.ts +77 -0
  208. package/dist/src/services/connections/oauth.d.ts.map +1 -0
  209. package/dist/src/services/connections/oauth.js +290 -0
  210. package/dist/src/services/connections/oauth.js.map +1 -0
  211. package/dist/src/services/connections/providers.d.ts +118 -0
  212. package/dist/src/services/connections/providers.d.ts.map +1 -0
  213. package/dist/src/services/connections/providers.js +260 -0
  214. package/dist/src/services/connections/providers.js.map +1 -0
  215. package/dist/src/services/connections/publish-gate.d.ts +103 -0
  216. package/dist/src/services/connections/publish-gate.d.ts.map +1 -0
  217. package/dist/src/services/connections/publish-gate.js +0 -0
  218. package/dist/src/services/connections/publish-gate.js.map +1 -0
  219. package/dist/src/services/connections/publish.d.ts +60 -0
  220. package/dist/src/services/connections/publish.d.ts.map +1 -0
  221. package/dist/src/services/connections/publish.js +340 -0
  222. package/dist/src/services/connections/publish.js.map +1 -0
  223. package/dist/src/services/connections/refresh.d.ts +55 -0
  224. package/dist/src/services/connections/refresh.d.ts.map +1 -0
  225. package/dist/src/services/connections/refresh.js +307 -0
  226. package/dist/src/services/connections/refresh.js.map +1 -0
  227. package/dist/src/services/markdown-negotiation.d.ts +17 -0
  228. package/dist/src/services/markdown-negotiation.d.ts.map +1 -1
  229. package/dist/src/services/markdown-negotiation.js +15 -0
  230. package/dist/src/services/markdown-negotiation.js.map +1 -1
  231. package/dist/src/services/message-send.d.ts.map +1 -1
  232. package/dist/src/services/message-send.js +16 -2
  233. package/dist/src/services/message-send.js.map +1 -1
  234. package/dist/src/services/openrouter.d.ts +67 -1
  235. package/dist/src/services/openrouter.d.ts.map +1 -1
  236. package/dist/src/services/openrouter.js +102 -11
  237. package/dist/src/services/openrouter.js.map +1 -1
  238. package/dist/src/services/upload-token.d.ts +5 -1
  239. package/dist/src/services/upload-token.d.ts.map +1 -1
  240. package/dist/src/services/upload-token.js +13 -1
  241. package/dist/src/services/upload-token.js.map +1 -1
  242. package/dist/src/services/workflow/engine-util.d.ts +18 -0
  243. package/dist/src/services/workflow/engine-util.d.ts.map +1 -1
  244. package/dist/src/services/workflow/engine-util.js +27 -0
  245. package/dist/src/services/workflow/engine-util.js.map +1 -1
  246. package/dist/src/services/workflow/engine.d.ts.map +1 -1
  247. package/dist/src/services/workflow/engine.js +4 -2
  248. package/dist/src/services/workflow/engine.js.map +1 -1
  249. package/dist/src/storage/interface.d.ts +2 -1
  250. package/dist/src/storage/interface.d.ts.map +1 -1
  251. package/dist/src/storage/providers/postgres-kysely/db-types.d.ts +65 -0
  252. package/dist/src/storage/providers/postgres-kysely/db-types.d.ts.map +1 -1
  253. package/dist/src/storage/providers/postgres-kysely/index.d.ts.map +1 -1
  254. package/dist/src/storage/providers/postgres-kysely/index.js +2 -1
  255. package/dist/src/storage/providers/postgres-kysely/index.js.map +1 -1
  256. package/dist/src/storage/providers/postgres-kysely/methods/connections.d.ts +44 -0
  257. package/dist/src/storage/providers/postgres-kysely/methods/connections.d.ts.map +1 -0
  258. package/dist/src/storage/providers/postgres-kysely/methods/connections.js +308 -0
  259. package/dist/src/storage/providers/postgres-kysely/methods/connections.js.map +1 -0
  260. package/dist/src/storage/providers/postgres-kysely/methods/node-infra.d.ts.map +1 -1
  261. package/dist/src/storage/providers/postgres-kysely/methods/node-infra.js +4 -2
  262. package/dist/src/storage/providers/postgres-kysely/methods/node-infra.js.map +1 -1
  263. package/dist/src/storage/providers/postgres-kysely/migrations/0021_connections.sql +116 -0
  264. package/dist/src/storage/providers/postgres-kysely/migrations/0022_provider_clients.sql +36 -0
  265. package/dist/src/storage/providers/postgres-kysely/migrations/0023_nonce_payload.sql +13 -0
  266. package/dist/src/storage/providers/sqlite/index.d.ts.map +1 -1
  267. package/dist/src/storage/providers/sqlite/index.js +2 -1
  268. package/dist/src/storage/providers/sqlite/index.js.map +1 -1
  269. package/dist/src/storage/providers/sqlite/methods/community.d.ts.map +1 -1
  270. package/dist/src/storage/providers/sqlite/methods/community.js +2 -1
  271. package/dist/src/storage/providers/sqlite/methods/community.js.map +1 -1
  272. package/dist/src/storage/providers/sqlite/methods/connections.d.ts +60 -0
  273. package/dist/src/storage/providers/sqlite/methods/connections.d.ts.map +1 -0
  274. package/dist/src/storage/providers/sqlite/methods/connections.js +287 -0
  275. package/dist/src/storage/providers/sqlite/methods/connections.js.map +1 -0
  276. package/dist/src/storage/providers/sqlite/schema-tables-1.d.ts.map +1 -1
  277. package/dist/src/storage/providers/sqlite/schema-tables-1.js +4 -0
  278. package/dist/src/storage/providers/sqlite/schema-tables-1.js.map +1 -1
  279. package/dist/src/storage/providers/sqlite/schema-tables-3.d.ts.map +1 -1
  280. package/dist/src/storage/providers/sqlite/schema-tables-3.js +106 -0
  281. package/dist/src/storage/providers/sqlite/schema-tables-3.js.map +1 -1
  282. package/dist/src/storage/providers/sqlite/schema.d.ts.map +1 -1
  283. package/dist/src/storage/providers/sqlite/schema.js +2 -0
  284. package/dist/src/storage/providers/sqlite/schema.js.map +1 -1
  285. package/dist/src/storage/repositories/connection.repository.d.ts +124 -0
  286. package/dist/src/storage/repositories/connection.repository.d.ts.map +1 -0
  287. package/dist/src/storage/repositories/connection.repository.js +2 -0
  288. package/dist/src/storage/repositories/connection.repository.js.map +1 -0
  289. package/dist/src/storage/types/agents-messaging.d.ts +19 -0
  290. package/dist/src/storage/types/agents-messaging.d.ts.map +1 -1
  291. package/dist/src/storage/types/agents-messaging.js.map +1 -1
  292. package/dist/src/storage/types/auth.d.ts +8 -1
  293. package/dist/src/storage/types/auth.d.ts.map +1 -1
  294. package/dist/src/utils/build-stamp.d.ts +56 -0
  295. package/dist/src/utils/build-stamp.d.ts.map +1 -0
  296. package/dist/src/utils/build-stamp.js +142 -0
  297. package/dist/src/utils/build-stamp.js.map +1 -0
  298. package/dist/src/utils/messaging.d.ts +14 -0
  299. package/dist/src/utils/messaging.d.ts.map +1 -1
  300. package/dist/src/utils/messaging.js +0 -0
  301. package/dist/src/utils/messaging.js.map +1 -1
  302. package/dist/static/sdk-libs/connect/index.js +199 -0
  303. package/dist/static/sdk-libs/connect/notes.js +47 -0
  304. package/dist/static/sdk-libs/connect/panel.js +238 -0
  305. package/dist/static/sdk-libs/data/index.js +63 -11
  306. package/dist/static/sdk-libs/dist/aimeat-connect.js +357 -0
  307. package/dist/static/sdk-libs/dist/aimeat-data.js +40 -9
  308. package/package.json +2 -2
@@ -0,0 +1,116 @@
1
+ /**
2
+ * @file VoiceRecorder.js
3
+ * @description Record-a-voice-clip control, shared by the inbox composer and the OpenRouter
4
+ * settings' transcription test. Wraps /js/services/audio-recorder.js in a Preact button that
5
+ * shows what is happening: elapsed time, a live input level so a dead microphone is obvious
6
+ * immediately, and separate stop-and-keep versus cancel-and-discard actions.
7
+ *
8
+ * Press to start, press again to stop. Not press-and-hold: on a touch screen that loses the
9
+ * recording to any accidental lift, and it makes a long message physically tiring.
10
+ * @structure VoiceRecorder (control) · useRecorder (state machine)
11
+ * @usage <${VoiceRecorder} maxSeconds=${300} onRecorded=${(file, seconds) => …} />
12
+ * @version-history
13
+ * v1.0.0 — 2026-08-01 — Initial version (voice messages + STT settings test).
14
+ */
15
+ import { h } from 'preact';
16
+ import { useState, useRef, useEffect } from 'preact/hooks';
17
+ import htm from 'htm';
18
+ const html = htm.bind(h);
19
+ import { t } from '/js/i18n.js';
20
+ import { startRecording, isRecordingSupported, fmtDuration } from '/js/services/audio-recorder.js';
21
+
22
+ /**
23
+ * @param {object} props
24
+ * @param {(file: File, durationSeconds: number) => void} props.onRecorded
25
+ * @param {number} [props.maxSeconds] hard stop (the node's AIMEAT_VOICE_MSG_MAX_SECONDS)
26
+ * @param {boolean} [props.disabled]
27
+ * @param {string} [props.label] text next to the icon; omitted = icon only
28
+ * @param {string} [props.className] extra class on the idle button (host styling)
29
+ */
30
+ export function VoiceRecorder({ onRecorded, maxSeconds = 300, disabled = false, label, className = '' }) {
31
+ const [state, setState] = useState('idle'); // idle | starting | recording | finishing
32
+ const [seconds, setSeconds] = useState(0);
33
+ const [level, setLevel] = useState(0);
34
+ const [error, setError] = useState(null);
35
+ const [capped, setCapped] = useState(false);
36
+ const recRef = useRef(null);
37
+
38
+ // Leaving the view mid-recording must release the microphone. Without this the browser's recording
39
+ // indicator stays lit after the user has navigated away, which reads as an app still listening.
40
+ useEffect(() => () => { try { recRef.current?.cancel(); } catch { /* gone already */ } }, []); // eslint-disable-line aimeat/no-silent-catch -- teardown of an already-torn-down recorder
41
+
42
+ if (!isRecordingSupported()) return null;
43
+
44
+ async function begin() {
45
+ setError(null); setCapped(false); setSeconds(0); setLevel(0); setState('starting');
46
+ try {
47
+ recRef.current = await startRecording({
48
+ maxSeconds,
49
+ onTick: setSeconds,
50
+ onLevel: setLevel,
51
+ onAutoStop: () => { setCapped(true); finish(); },
52
+ });
53
+ setState('recording');
54
+ } catch (e) {
55
+ setState('idle');
56
+ // Say which of the three it was: a denied permission, no microphone, or a browser that cannot.
57
+ const name = e?.name || '';
58
+ setError(
59
+ name === 'NotAllowedError' || name === 'SecurityError' ? t('voice.errDenied')
60
+ : name === 'NotFoundError' ? t('voice.errNoMic')
61
+ : e?.message === 'unsupported' ? t('voice.errUnsupported')
62
+ : (e?.message || t('voice.errFailed')),
63
+ );
64
+ }
65
+ }
66
+
67
+ async function finish() {
68
+ const rec = recRef.current;
69
+ if (!rec) return;
70
+ setState('finishing');
71
+ try {
72
+ const { file, durationSeconds } = await rec.stop();
73
+ recRef.current = null;
74
+ setState('idle'); setSeconds(0); setLevel(0);
75
+ onRecorded(file, durationSeconds);
76
+ } catch (e) {
77
+ recRef.current = null;
78
+ setState('idle');
79
+ setError(e?.message || t('voice.errFailed'));
80
+ }
81
+ }
82
+
83
+ function discard() {
84
+ try { recRef.current?.cancel(); } catch { /* already stopped */ } // eslint-disable-line aimeat/no-silent-catch -- cancel is best-effort; the state reset below is what matters
85
+ recRef.current = null;
86
+ setState('idle'); setSeconds(0); setLevel(0); setCapped(false);
87
+ }
88
+
89
+ if (state === 'recording' || state === 'finishing') {
90
+ const near = maxSeconds > 0 && seconds >= maxSeconds - 10;
91
+ return html`
92
+ <span class="vr-active" role="group" aria-label=${t('voice.recording')}>
93
+ <span class="vr-dot" aria-hidden="true"></span>
94
+ <span class=${`vr-time${near ? ' vr-time--near' : ''}`}>${fmtDuration(seconds)}</span>
95
+ <span class="vr-meter" aria-hidden="true">
96
+ <span class="vr-meter-fill" style=${`width:${Math.round(Math.min(1, level * 1.6) * 100)}%`}></span>
97
+ </span>
98
+ <button type="button" class="btn-primary btn-sm vr-stop" disabled=${state === 'finishing'}
99
+ onClick=${finish} title=${t('voice.stop')} aria-label=${t('voice.stop')}>
100
+ ${state === 'finishing' ? '…' : t('voice.stop')}
101
+ </button>
102
+ <button type="button" class="btn-ghost btn-sm vr-cancel" disabled=${state === 'finishing'}
103
+ onClick=${discard} title=${t('voice.cancel')} aria-label=${t('voice.cancel')}>✕</button>
104
+ ${capped ? html`<span class="vr-note">${t('voice.capped', { n: maxSeconds })}</span>` : null}
105
+ </span>`;
106
+ }
107
+
108
+ return html`
109
+ <span class="vr-wrap">
110
+ <button type="button" class=${`vr-btn ${className}`} disabled=${disabled || state === 'starting'}
111
+ onClick=${begin} title=${t('voice.record')} aria-label=${t('voice.record')}>
112
+ <span class="vr-ico" aria-hidden="true">🎤</span>${label ? html`<span class="vr-label">${label}</span>` : null}
113
+ </button>
114
+ ${error ? html`<span class="vr-error">${error}</span>` : null}
115
+ </span>`;
116
+ }
@@ -0,0 +1,53 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Account connected</title>
7
+ <!--
8
+ Where the provider's redirect lands after a connection is finished (TARGET-057).
9
+
10
+ It is a STATIC file rather than something the node renders, because the backend is protocol-only
11
+ and does not build pages.
12
+
13
+ IT ANNOUNCES OVER BroadcastChannel, not window.opener. This node sets a Cross-Origin-Opener-Policy,
14
+ which severs the opener relationship the moment the pop-up navigates to the provider — so
15
+ window.opener is null here and the page that opened this one cannot read window.closed either.
16
+ The first version relied on exactly that and produced ten "COOP would block the window.closed
17
+ call" errors per connection while appearing to work.
18
+
19
+ BroadcastChannel is same-origin and unaffected by COOP, so this reaches the opener directly. The
20
+ opener also polls the server as a fallback, because a user who closes the pop-up early never
21
+ reaches this page at all.
22
+ -->
23
+ <style>
24
+ html { color-scheme: light dark; }
25
+ body {
26
+ margin: 0; min-height: 100vh; display: grid; place-items: center;
27
+ font: 1rem/1.5 system-ui, sans-serif; padding: 1.5rem; text-align: center;
28
+ }
29
+ p { max-width: 28rem; margin: 0; }
30
+ </style>
31
+ </head>
32
+ <body>
33
+ <p id="msg">Account connected. You can close this window.</p>
34
+ <script>
35
+ try {
36
+ const ch = new BroadcastChannel('aimeat-connect');
37
+ ch.postMessage({ type: 'connected' });
38
+ ch.close();
39
+ } catch (err) {
40
+ // Reported rather than swallowed: without this message the opener falls back to polling, which
41
+ // is slower but still correct, and an operator should be able to tell the two paths apart.
42
+ console.warn('[aimeat-connect] could not announce over BroadcastChannel', err);
43
+ }
44
+ // Attempted, not assumed. A window the script did not open cannot close itself in most
45
+ // browsers, which is what happens when someone reaches this URL directly — and a blank page
46
+ // would then read as a failure when the connection has in fact already succeeded.
47
+ try { window.close(); } catch (err) {
48
+ document.getElementById('msg').textContent =
49
+ 'Account connected. You can close this window. (' + err.message + ')';
50
+ }
51
+ </script>
52
+ </body>
53
+ </html>
@@ -0,0 +1,66 @@
1
+ /**
2
+ * @file voice-recorder.css
3
+ * @description Styles for /components/VoiceRecorder.js — the record button and its active state
4
+ * (pulsing dot, elapsed time, input-level meter, stop and cancel). Shared by the inbox composer and
5
+ * the OpenRouter settings transcription test, so it lives with the component rather than inside a
6
+ * view stylesheet.
7
+ * @structure .vr-wrap/.vr-btn (idle) · .vr-active (recording) · .vr-dot · .vr-time · .vr-meter
8
+ * @version-history
9
+ * v1.0.0 — 2026-08-01 — Initial version (voice messages).
10
+ */
11
+
12
+ .vr-wrap { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; }
13
+
14
+ .vr-btn {
15
+ display: inline-flex; align-items: center; gap: 6px;
16
+ background: none; border: none; cursor: pointer;
17
+ color: var(--text-dim); font-size: 18px; line-height: 1;
18
+ padding: 4px 6px; border-radius: var(--radius, 8px);
19
+ }
20
+ .vr-btn:hover:not(:disabled) { background: var(--bg-dim); color: var(--text); }
21
+ .vr-btn:disabled { opacity: .45; cursor: not-allowed; }
22
+ .vr-label { font-size: 13px; }
23
+
24
+ .vr-active {
25
+ display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap;
26
+ padding: 4px 8px; border-radius: var(--radius, 8px);
27
+ background: var(--bg-dim); border: 1px solid var(--border);
28
+ }
29
+
30
+ .vr-dot {
31
+ width: 9px; height: 9px; border-radius: 50%;
32
+ background: var(--danger);
33
+ animation: vr-pulse 1.4s ease-in-out infinite;
34
+ }
35
+ @keyframes vr-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }
36
+ /* Someone who has asked for less motion still needs to see that recording is live — the colour and
37
+ the running timer carry that; only the pulse stops. */
38
+ @media (prefers-reduced-motion: reduce) {
39
+ .vr-dot { animation: none; }
40
+ }
41
+
42
+ .vr-time {
43
+ font-variant-numeric: tabular-nums;
44
+ font-size: 13px; font-weight: 600; min-width: 3.2em;
45
+ }
46
+ .vr-time--near { color: var(--danger); }
47
+
48
+ .vr-meter {
49
+ width: 56px; height: 6px; border-radius: 3px;
50
+ background: var(--bg-surface); overflow: hidden;
51
+ }
52
+ .vr-meter-fill {
53
+ display: block; height: 100%; width: 0;
54
+ background: var(--success);
55
+ transition: width 80ms linear;
56
+ }
57
+
58
+ .vr-note, .vr-error { font-size: 11px; color: var(--text-muted); }
59
+ .vr-error { color: var(--danger); }
60
+
61
+ @media (max-width: 520px) {
62
+ /* The meter is the first thing to go: on a narrow composer the timer and the two buttons are what
63
+ the user needs, and a decorative bar must never push the stop button out of reach. */
64
+ .vr-meter { display: none; }
65
+ .vr-active { width: 100%; }
66
+ }
@@ -801,3 +801,44 @@
801
801
  .inbox-speak-btn--on { background: var(--accent-subtle); border-color: var(--accent-border); color: var(--accent); }
802
802
  .inbox-speak-ico { font-size: 13px; line-height: 1; }
803
803
  .inbox-speak-stop { flex: none; padding-inline: 8px; }
804
+
805
+ /* ── Voice messages (2026-08-01) ──
806
+ A voice message plays where it sits. It used to fall into the generic attachment chip and open in
807
+ a new browser tab, which is a download, not a conversation. */
808
+ .inbox-attach-audio { display: flex; flex-direction: column; gap: 4px; width: 100%; max-width: 380px; }
809
+ .inbox-audio { width: 100%; height: 34px; }
810
+ .inbox-audio--chip { max-width: 190px; height: 30px; }
811
+ .inbox-audio-dur { font-size: 11px; color: var(--text-muted); font-variant-numeric: tabular-nums; }
812
+
813
+ .inbox-file-chip--voice { align-items: center; gap: 6px; }
814
+ .inbox-chip-voice { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; }
815
+
816
+ /* Transcript: collapsed by default so a thread of voice messages stays scannable, and the label
817
+ always says who produced the text — the sender's own transcript and one this reader paid for are
818
+ not the same claim. */
819
+ .inbox-transcript { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 2px; }
820
+ .inbox-transcript-toggle {
821
+ background: none; border: none; cursor: pointer; padding: 2px 0;
822
+ font-size: 11px; color: var(--text-dim); display: inline-flex; align-items: center; gap: 6px;
823
+ }
824
+ .inbox-transcript-toggle:hover { color: var(--text); }
825
+ .inbox-transcript-src { color: var(--text-muted); }
826
+ .inbox-transcript-text {
827
+ flex-basis: 100%; font-size: 13px; white-space: pre-wrap; word-break: break-word;
828
+ padding: 8px; border-radius: var(--radius, 8px);
829
+ background: var(--bg-dim); border: 1px solid var(--border);
830
+ }
831
+ .inbox-transcript-btn {
832
+ background: none; border: 1px solid var(--border); border-radius: 999px; cursor: pointer;
833
+ font-size: 11px; padding: 2px 10px; color: var(--text-dim);
834
+ }
835
+ .inbox-transcript-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
836
+ .inbox-transcript-btn:disabled { opacity: .6; cursor: progress; }
837
+ .inbox-transcript-hint { font-size: 11px; color: var(--text-muted); }
838
+ .inbox-transcript-err { font-size: 11px; color: var(--danger); }
839
+
840
+ @media (max-width: 520px) {
841
+ /* Full width on a phone: a 380px cap would leave the player floating in a narrow column. */
842
+ .inbox-attach-audio { max-width: 100%; }
843
+ .inbox-audio--chip { max-width: 100%; }
844
+ }