aimeat 2.0.0 → 2.1.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 (412) hide show
  1. package/dist/locales/en.json +35 -1
  2. package/dist/locales/fi.json +35 -1
  3. package/dist/public/components/LinkPreview.js +101 -0
  4. package/dist/public/components/NotificationBell.js +18 -1
  5. package/dist/public/css/theme.css +57 -7
  6. package/dist/public/css/views/admin.css +8 -1
  7. package/dist/public/css/views/app-grant.css +18 -0
  8. package/dist/public/css/views/inbox.css +46 -0
  9. package/dist/public/css/views/portfolio.css +46 -0
  10. package/dist/public/css/views/profile.css +29 -4
  11. package/dist/public/js/services/apps.js +19 -3
  12. package/dist/public/js/services/messages-ai-prompts.js +38 -0
  13. package/dist/public/js/services/messages.js +26 -4
  14. package/dist/public/js/services/notebook.js +76 -0
  15. package/dist/public/js/services/unfurl.js +43 -0
  16. package/dist/public/lib/samples/LICENSE.md +11 -0
  17. package/dist/public/lib/samples/bass/A1.mp3 +0 -0
  18. package/dist/public/lib/samples/bass/B2.mp3 +0 -0
  19. package/dist/public/lib/samples/bass/D2.mp3 +0 -0
  20. package/dist/public/lib/samples/bass/E1.mp3 +0 -0
  21. package/dist/public/lib/samples/bass/E3.mp3 +0 -0
  22. package/dist/public/lib/samples/bass/G2.mp3 +0 -0
  23. package/dist/public/lib/samples/epiano/C2.mp3 +0 -0
  24. package/dist/public/lib/samples/epiano/C3.mp3 +0 -0
  25. package/dist/public/lib/samples/epiano/C4.mp3 +0 -0
  26. package/dist/public/lib/samples/epiano/C5.mp3 +0 -0
  27. package/dist/public/lib/samples/epiano/C6.mp3 +0 -0
  28. package/dist/public/lib/samples/epiano/G2.mp3 +0 -0
  29. package/dist/public/lib/samples/epiano/G3.mp3 +0 -0
  30. package/dist/public/lib/samples/epiano/G4.mp3 +0 -0
  31. package/dist/public/lib/samples/epiano/G5.mp3 +0 -0
  32. package/dist/public/lib/samples/guitar-steel/A2.mp3 +0 -0
  33. package/dist/public/lib/samples/guitar-steel/A4.mp3 +0 -0
  34. package/dist/public/lib/samples/guitar-steel/B3.mp3 +0 -0
  35. package/dist/public/lib/samples/guitar-steel/D3.mp3 +0 -0
  36. package/dist/public/lib/samples/guitar-steel/E2.mp3 +0 -0
  37. package/dist/public/lib/samples/guitar-steel/E4.mp3 +0 -0
  38. package/dist/public/lib/samples/guitar-steel/E5.mp3 +0 -0
  39. package/dist/public/lib/samples/guitar-steel/G3.mp3 +0 -0
  40. package/dist/public/lib/samples/organ/C2.mp3 +0 -0
  41. package/dist/public/lib/samples/organ/C3.mp3 +0 -0
  42. package/dist/public/lib/samples/organ/C4.mp3 +0 -0
  43. package/dist/public/lib/samples/organ/C5.mp3 +0 -0
  44. package/dist/public/lib/samples/organ/C6.mp3 +0 -0
  45. package/dist/public/lib/samples/organ/G2.mp3 +0 -0
  46. package/dist/public/lib/samples/organ/G3.mp3 +0 -0
  47. package/dist/public/lib/samples/organ/G4.mp3 +0 -0
  48. package/dist/public/lib/samples/organ/G5.mp3 +0 -0
  49. package/dist/public/lib/samples/strings/C2.mp3 +0 -0
  50. package/dist/public/lib/samples/strings/C3.mp3 +0 -0
  51. package/dist/public/lib/samples/strings/C4.mp3 +0 -0
  52. package/dist/public/lib/samples/strings/C5.mp3 +0 -0
  53. package/dist/public/lib/samples/strings/C6.mp3 +0 -0
  54. package/dist/public/lib/samples/strings/G2.mp3 +0 -0
  55. package/dist/public/lib/samples/strings/G3.mp3 +0 -0
  56. package/dist/public/lib/samples/strings/G4.mp3 +0 -0
  57. package/dist/public/lib/samples/strings/G5.mp3 +0 -0
  58. package/dist/public/lib/samples/trumpet/A3.mp3 +0 -0
  59. package/dist/public/lib/samples/trumpet/A4.mp3 +0 -0
  60. package/dist/public/lib/samples/trumpet/A5.mp3 +0 -0
  61. package/dist/public/lib/samples/trumpet/C4.mp3 +0 -0
  62. package/dist/public/lib/samples/trumpet/C5.mp3 +0 -0
  63. package/dist/public/lib/samples/trumpet/E3.mp3 +0 -0
  64. package/dist/public/lib/samples/trumpet/E4.mp3 +0 -0
  65. package/dist/public/lib/samples/trumpet/E5.mp3 +0 -0
  66. package/dist/public/spa.html +2 -0
  67. package/dist/public/views/app-grant.js +24 -4
  68. package/dist/public/views/portfolio.js +61 -6
  69. package/dist/public/views/profile/apps-tab.js +18 -1
  70. package/dist/public/views/profile/inbox-tab/ai-actions.js +86 -0
  71. package/dist/public/views/profile/inbox-tab/components.js +124 -9
  72. package/dist/public/views/profile/inbox-tab/helpers.js +26 -0
  73. package/dist/public/views/profile/inbox-tab/panels.js +18 -3
  74. package/dist/public/views/profile/inbox-tab/use-thread-ux.js +18 -1
  75. package/dist/public/views/profile/inbox-tab.js +46 -49
  76. package/dist/public/views/profile/landing-page.cards.js +7 -0
  77. package/dist/public/views/profile/landing-page.js +12 -1
  78. package/dist/public/views/profile/skills-tab.js +1 -1
  79. package/dist/public/views/public-workspace-viewer.js +1 -1
  80. package/dist/public/views/start.js +2 -1
  81. package/dist/scripts/build-sdk-libs.d.ts +16 -0
  82. package/dist/scripts/build-sdk-libs.d.ts.map +1 -0
  83. package/dist/scripts/build-sdk-libs.js +112 -0
  84. package/dist/scripts/build-sdk-libs.js.map +1 -0
  85. package/dist/src/cli/connect/auth.d.ts +15 -0
  86. package/dist/src/cli/connect/auth.d.ts.map +1 -1
  87. package/dist/src/cli/connect/auth.js +25 -6
  88. package/dist/src/cli/connect/auth.js.map +1 -1
  89. package/dist/src/cli/connect/keychain.d.ts +9 -0
  90. package/dist/src/cli/connect/keychain.d.ts.map +1 -1
  91. package/dist/src/cli/connect/keychain.js +21 -8
  92. package/dist/src/cli/connect/keychain.js.map +1 -1
  93. package/dist/src/cli/connect/mcp/tools/exchange.d.ts +18 -0
  94. package/dist/src/cli/connect/mcp/tools/exchange.d.ts.map +1 -0
  95. package/dist/src/cli/connect/mcp/tools/exchange.js +170 -0
  96. package/dist/src/cli/connect/mcp/tools/exchange.js.map +1 -0
  97. package/dist/src/cli/connect/mcp/tools/index.d.ts.map +1 -1
  98. package/dist/src/cli/connect/mcp/tools/index.js +2 -0
  99. package/dist/src/cli/connect/mcp/tools/index.js.map +1 -1
  100. package/dist/src/cli/connect/tool-call-defs-exchange.d.ts +3 -0
  101. package/dist/src/cli/connect/tool-call-defs-exchange.d.ts.map +1 -0
  102. package/dist/src/cli/connect/tool-call-defs-exchange.js +146 -0
  103. package/dist/src/cli/connect/tool-call-defs-exchange.js.map +1 -0
  104. package/dist/src/cli/connect/tool-call.d.ts.map +1 -1
  105. package/dist/src/cli/connect/tool-call.js +2 -0
  106. package/dist/src/cli/connect/tool-call.js.map +1 -1
  107. package/dist/src/commerce/session-service.d.ts +14 -0
  108. package/dist/src/commerce/session-service.d.ts.map +1 -1
  109. package/dist/src/commerce/session-service.js +1 -1
  110. package/dist/src/commerce/session-service.js.map +1 -1
  111. package/dist/src/data/app-templates/shells.d.ts +7 -3
  112. package/dist/src/data/app-templates/shells.d.ts.map +1 -1
  113. package/dist/src/data/app-templates/shells.js +10 -6
  114. package/dist/src/data/app-templates/shells.js.map +1 -1
  115. package/dist/src/data/appdev-pitfalls.d.ts +2 -0
  116. package/dist/src/data/appdev-pitfalls.d.ts.map +1 -1
  117. package/dist/src/data/appdev-pitfalls.js +24 -1
  118. package/dist/src/data/appdev-pitfalls.js.map +1 -1
  119. package/dist/src/generated/api-types.d.ts +1565 -9
  120. package/dist/src/generated/api-types.d.ts.map +1 -1
  121. package/dist/src/mcp/annotations.d.ts.map +1 -1
  122. package/dist/src/mcp/annotations.js +20 -0
  123. package/dist/src/mcp/annotations.js.map +1 -1
  124. package/dist/src/mcp/apps.d.ts.map +1 -1
  125. package/dist/src/mcp/apps.js +8 -4
  126. package/dist/src/mcp/apps.js.map +1 -1
  127. package/dist/src/mcp/catalog/definitions/exchange.d.ts +18 -0
  128. package/dist/src/mcp/catalog/definitions/exchange.d.ts.map +1 -0
  129. package/dist/src/mcp/catalog/definitions/exchange.js +182 -0
  130. package/dist/src/mcp/catalog/definitions/exchange.js.map +1 -0
  131. package/dist/src/mcp/catalog/definitions.d.ts.map +1 -1
  132. package/dist/src/mcp/catalog/definitions.js +2 -0
  133. package/dist/src/mcp/catalog/definitions.js.map +1 -1
  134. package/dist/src/mcp/catalog/scopes.d.ts.map +1 -1
  135. package/dist/src/mcp/catalog/scopes.js +22 -0
  136. package/dist/src/mcp/catalog/scopes.js.map +1 -1
  137. package/dist/src/mcp/catalog/surfaces.d.ts.map +1 -1
  138. package/dist/src/mcp/catalog/surfaces.js +14 -0
  139. package/dist/src/mcp/catalog/surfaces.js.map +1 -1
  140. package/dist/src/mcp/exchange-run.d.ts +5 -0
  141. package/dist/src/mcp/exchange-run.d.ts.map +1 -0
  142. package/dist/src/mcp/exchange-run.js +226 -0
  143. package/dist/src/mcp/exchange-run.js.map +1 -0
  144. package/dist/src/mcp/exchange.d.ts +5 -0
  145. package/dist/src/mcp/exchange.d.ts.map +1 -0
  146. package/dist/src/mcp/exchange.js +351 -0
  147. package/dist/src/mcp/exchange.js.map +1 -0
  148. package/dist/src/mcp/extensions.d.ts.map +1 -1
  149. package/dist/src/mcp/extensions.js +11 -0
  150. package/dist/src/mcp/extensions.js.map +1 -1
  151. package/dist/src/mcp/index.d.ts.map +1 -1
  152. package/dist/src/mcp/index.js +6 -2
  153. package/dist/src/mcp/index.js.map +1 -1
  154. package/dist/src/models/app-tool-schemas.d.ts +55 -1
  155. package/dist/src/models/app-tool-schemas.d.ts.map +1 -1
  156. package/dist/src/models/app-tool-schemas.js +20 -1
  157. package/dist/src/models/app-tool-schemas.js.map +1 -1
  158. package/dist/src/models/offer-schemas.d.ts +2 -2
  159. package/dist/src/models/schemas.d.ts +1 -0
  160. package/dist/src/models/schemas.d.ts.map +1 -1
  161. package/dist/src/models/schemas.js +3 -0
  162. package/dist/src/models/schemas.js.map +1 -1
  163. package/dist/src/routes/agents/device-auth.d.ts +4 -0
  164. package/dist/src/routes/agents/device-auth.d.ts.map +1 -1
  165. package/dist/src/routes/agents/device-auth.js +19 -4
  166. package/dist/src/routes/agents/device-auth.js.map +1 -1
  167. package/dist/src/routes/app-grants.d.ts +6 -0
  168. package/dist/src/routes/app-grants.d.ts.map +1 -1
  169. package/dist/src/routes/app-grants.js +35 -2
  170. package/dist/src/routes/app-grants.js.map +1 -1
  171. package/dist/src/routes/apps/fork-manage.d.ts.map +1 -1
  172. package/dist/src/routes/apps/fork-manage.js +24 -1
  173. package/dist/src/routes/apps/fork-manage.js.map +1 -1
  174. package/dist/src/routes/apps/publish.d.ts +2 -0
  175. package/dist/src/routes/apps/publish.d.ts.map +1 -1
  176. package/dist/src/routes/apps/publish.js +27 -1
  177. package/dist/src/routes/apps/publish.js.map +1 -1
  178. package/dist/src/routes/apps-cost.d.ts +25 -0
  179. package/dist/src/routes/apps-cost.d.ts.map +1 -0
  180. package/dist/src/routes/apps-cost.js +107 -0
  181. package/dist/src/routes/apps-cost.js.map +1 -0
  182. package/dist/src/routes/exchange-market.d.ts +28 -0
  183. package/dist/src/routes/exchange-market.d.ts.map +1 -0
  184. package/dist/src/routes/exchange-market.js +454 -0
  185. package/dist/src/routes/exchange-market.js.map +1 -0
  186. package/dist/src/routes/exchange.d.ts +33 -0
  187. package/dist/src/routes/exchange.d.ts.map +1 -0
  188. package/dist/src/routes/exchange.js +395 -0
  189. package/dist/src/routes/exchange.js.map +1 -0
  190. package/dist/src/routes/extensions/entitlement-gate.d.ts +33 -0
  191. package/dist/src/routes/extensions/entitlement-gate.d.ts.map +1 -0
  192. package/dist/src/routes/extensions/entitlement-gate.js +143 -0
  193. package/dist/src/routes/extensions/entitlement-gate.js.map +1 -0
  194. package/dist/src/routes/extensions/manifest.d.ts +9 -0
  195. package/dist/src/routes/extensions/manifest.d.ts.map +1 -1
  196. package/dist/src/routes/extensions/manifest.js +33 -1
  197. package/dist/src/routes/extensions/manifest.js.map +1 -1
  198. package/dist/src/routes/extensions/paywall.d.ts.map +1 -1
  199. package/dist/src/routes/extensions/paywall.js +9 -0
  200. package/dist/src/routes/extensions/paywall.js.map +1 -1
  201. package/dist/src/routes/federation-sync/messaging.d.ts.map +1 -1
  202. package/dist/src/routes/federation-sync/messaging.js +3 -1
  203. package/dist/src/routes/federation-sync/messaging.js.map +1 -1
  204. package/dist/src/routes/ghii/recovery.d.ts +2 -0
  205. package/dist/src/routes/ghii/recovery.d.ts.map +1 -1
  206. package/dist/src/routes/ghii/recovery.js +7 -0
  207. package/dist/src/routes/ghii/recovery.js.map +1 -1
  208. package/dist/src/routes/ghii/register-login.d.ts +4 -0
  209. package/dist/src/routes/ghii/register-login.d.ts.map +1 -1
  210. package/dist/src/routes/ghii/register-login.js +45 -22
  211. package/dist/src/routes/ghii/register-login.js.map +1 -1
  212. package/dist/src/routes/ghii/web-verify.d.ts +5 -0
  213. package/dist/src/routes/ghii/web-verify.d.ts.map +1 -1
  214. package/dist/src/routes/ghii/web-verify.js +24 -3
  215. package/dist/src/routes/ghii/web-verify.js.map +1 -1
  216. package/dist/src/routes/libs/sdk-serve.d.ts +11 -0
  217. package/dist/src/routes/libs/sdk-serve.d.ts.map +1 -0
  218. package/dist/src/routes/libs/sdk-serve.js +53 -0
  219. package/dist/src/routes/libs/sdk-serve.js.map +1 -0
  220. package/dist/src/routes/libs.d.ts +7 -1
  221. package/dist/src/routes/libs.d.ts.map +1 -1
  222. package/dist/src/routes/libs.js +65 -146
  223. package/dist/src/routes/libs.js.map +1 -1
  224. package/dist/src/routes/messages.d.ts +3 -0
  225. package/dist/src/routes/messages.d.ts.map +1 -1
  226. package/dist/src/routes/messages.js +9 -0
  227. package/dist/src/routes/messages.js.map +1 -1
  228. package/dist/src/routes/notifications.d.ts +6 -3
  229. package/dist/src/routes/notifications.d.ts.map +1 -1
  230. package/dist/src/routes/notifications.js +32 -3
  231. package/dist/src/routes/notifications.js.map +1 -1
  232. package/dist/src/routes/organisms/workspace-access.d.ts.map +1 -1
  233. package/dist/src/routes/organisms/workspace-access.js +38 -20
  234. package/dist/src/routes/organisms/workspace-access.js.map +1 -1
  235. package/dist/src/routes/unfurl.d.ts +27 -0
  236. package/dist/src/routes/unfurl.d.ts.map +1 -0
  237. package/dist/src/routes/unfurl.js +180 -0
  238. package/dist/src/routes/unfurl.js.map +1 -0
  239. package/dist/src/routes/upload.d.ts +2 -0
  240. package/dist/src/routes/upload.d.ts.map +1 -1
  241. package/dist/src/routes/upload.js +7 -0
  242. package/dist/src/routes/upload.js.map +1 -1
  243. package/dist/src/routes/webmcp.d.ts +3 -0
  244. package/dist/src/routes/webmcp.d.ts.map +1 -1
  245. package/dist/src/routes/webmcp.js +55 -0
  246. package/dist/src/routes/webmcp.js.map +1 -1
  247. package/dist/src/server-bootstrap/routes-loader.d.ts +1 -0
  248. package/dist/src/server-bootstrap/routes-loader.d.ts.map +1 -1
  249. package/dist/src/server-bootstrap/routes-loader.js +8 -0
  250. package/dist/src/server-bootstrap/routes-loader.js.map +1 -1
  251. package/dist/src/services/agent-face.d.ts +10 -2
  252. package/dist/src/services/agent-face.d.ts.map +1 -1
  253. package/dist/src/services/agent-face.js +10 -4
  254. package/dist/src/services/agent-face.js.map +1 -1
  255. package/dist/src/services/app-tool-interfaces.d.ts +37 -0
  256. package/dist/src/services/app-tool-interfaces.d.ts.map +1 -0
  257. package/dist/src/services/app-tool-interfaces.js +101 -0
  258. package/dist/src/services/app-tool-interfaces.js.map +1 -0
  259. package/dist/src/services/build-app-prompt.d.ts +3 -0
  260. package/dist/src/services/build-app-prompt.d.ts.map +1 -1
  261. package/dist/src/services/build-app-prompt.js +12 -1
  262. package/dist/src/services/build-app-prompt.js.map +1 -1
  263. package/dist/src/services/contacts.d.ts +25 -1
  264. package/dist/src/services/contacts.d.ts.map +1 -1
  265. package/dist/src/services/contacts.js +30 -0
  266. package/dist/src/services/contacts.js.map +1 -1
  267. package/dist/src/services/entitlement-money.d.ts +55 -0
  268. package/dist/src/services/entitlement-money.d.ts.map +1 -0
  269. package/dist/src/services/entitlement-money.js +68 -0
  270. package/dist/src/services/entitlement-money.js.map +1 -0
  271. package/dist/src/services/exchange-market.d.ts +247 -0
  272. package/dist/src/services/exchange-market.d.ts.map +1 -0
  273. package/dist/src/services/exchange-market.js +254 -0
  274. package/dist/src/services/exchange-market.js.map +1 -0
  275. package/dist/src/services/exchange-proposals.d.ts +47 -0
  276. package/dist/src/services/exchange-proposals.d.ts.map +1 -0
  277. package/dist/src/services/exchange-proposals.js +71 -0
  278. package/dist/src/services/exchange-proposals.js.map +1 -0
  279. package/dist/src/services/exchange-work.d.ts +30 -0
  280. package/dist/src/services/exchange-work.d.ts.map +1 -0
  281. package/dist/src/services/exchange-work.js +43 -0
  282. package/dist/src/services/exchange-work.js.map +1 -0
  283. package/dist/src/services/message-send.d.ts.map +1 -1
  284. package/dist/src/services/message-send.js +4 -1
  285. package/dist/src/services/message-send.js.map +1 -1
  286. package/dist/src/services/metered-entitlements.d.ts +203 -0
  287. package/dist/src/services/metered-entitlements.d.ts.map +1 -0
  288. package/dist/src/services/metered-entitlements.js +264 -0
  289. package/dist/src/services/metered-entitlements.js.map +1 -0
  290. package/dist/src/services/notify.d.ts +7 -0
  291. package/dist/src/services/notify.d.ts.map +1 -1
  292. package/dist/src/services/notify.js +35 -0
  293. package/dist/src/services/notify.js.map +1 -1
  294. package/dist/src/services/owner-memory.d.ts +16 -0
  295. package/dist/src/services/owner-memory.d.ts.map +1 -1
  296. package/dist/src/services/owner-memory.js +23 -0
  297. package/dist/src/services/owner-memory.js.map +1 -1
  298. package/dist/src/services/owner-provisioning.d.ts +5 -0
  299. package/dist/src/services/owner-provisioning.d.ts.map +1 -1
  300. package/dist/src/services/owner-provisioning.js +20 -2
  301. package/dist/src/services/owner-provisioning.js.map +1 -1
  302. package/dist/src/storage/providers/postgres-kysely/methods/apps.d.ts +1 -0
  303. package/dist/src/storage/providers/postgres-kysely/methods/apps.d.ts.map +1 -1
  304. package/dist/src/storage/providers/postgres-kysely/methods/apps.js +2 -0
  305. package/dist/src/storage/providers/postgres-kysely/methods/apps.js.map +1 -1
  306. package/dist/src/storage/providers/postgres-kysely/methods/identity.d.ts +1 -0
  307. package/dist/src/storage/providers/postgres-kysely/methods/identity.d.ts.map +1 -1
  308. package/dist/src/storage/providers/postgres-kysely/methods/identity.js +4 -0
  309. package/dist/src/storage/providers/postgres-kysely/methods/identity.js.map +1 -1
  310. package/dist/src/storage/providers/postgres-kysely/migrations/0011_unique_verified_email.sql +25 -0
  311. package/dist/src/storage/providers/sqlite/methods/apps.d.ts +1 -0
  312. package/dist/src/storage/providers/sqlite/methods/apps.d.ts.map +1 -1
  313. package/dist/src/storage/providers/sqlite/methods/apps.js +2 -0
  314. package/dist/src/storage/providers/sqlite/methods/apps.js.map +1 -1
  315. package/dist/src/storage/providers/sqlite/methods/identity-nodes.d.ts +1 -0
  316. package/dist/src/storage/providers/sqlite/methods/identity-nodes.d.ts.map +1 -1
  317. package/dist/src/storage/providers/sqlite/methods/identity-nodes.js +4 -0
  318. package/dist/src/storage/providers/sqlite/methods/identity-nodes.js.map +1 -1
  319. package/dist/src/storage/providers/sqlite/schema.d.ts.map +1 -1
  320. package/dist/src/storage/providers/sqlite/schema.js +20 -0
  321. package/dist/src/storage/providers/sqlite/schema.js.map +1 -1
  322. package/dist/src/storage/repositories/app.repository.d.ts +0 -0
  323. package/dist/src/storage/repositories/app.repository.d.ts.map +1 -1
  324. package/dist/src/storage/repositories/identity.repository.d.ts +6 -0
  325. package/dist/src/storage/repositories/identity.repository.d.ts.map +1 -1
  326. package/dist/src/storage/types/apps.d.ts +1 -0
  327. package/dist/src/storage/types/apps.d.ts.map +1 -1
  328. package/dist/src/storage/types/organisms-federation.d.ts +21 -0
  329. package/dist/src/storage/types/organisms-federation.d.ts.map +1 -1
  330. package/dist/src/utils/app-mobile-lint.d.ts +17 -0
  331. package/dist/src/utils/app-mobile-lint.d.ts.map +1 -0
  332. package/dist/src/utils/app-mobile-lint.js +40 -0
  333. package/dist/src/utils/app-mobile-lint.js.map +1 -0
  334. package/dist/static/app-catalog/_template.html +12 -77
  335. package/dist/static/app-catalog/js/apps-io.js +51 -165
  336. package/dist/static/app-catalog/js/cost.js +100 -0
  337. package/dist/static/app-catalog/js/db.js +23 -93
  338. package/dist/static/app-catalog/js/detail.js +158 -24
  339. package/dist/static/app-catalog/js/favorites.js +71 -0
  340. package/dist/static/app-catalog/js/i18n-data.js +90 -16
  341. package/dist/static/app-catalog/js/main.js +23 -103
  342. package/dist/static/app-catalog/js/migrate.js +88 -0
  343. package/dist/static/app-catalog/js/monetize.js +1 -1
  344. package/dist/static/app-catalog/js/promote.js +70 -0
  345. package/dist/static/app-catalog/js/render.js +26 -139
  346. package/dist/static/app-catalog/js/server-io.js +126 -223
  347. package/dist/static/app-catalog/js/settings.js +11 -204
  348. package/dist/static/app-catalog/styles/app-catalog.css +40 -0
  349. package/dist/static/app-catalog.html +790 -912
  350. package/dist/static/sdk-libs/_core/config.js +52 -0
  351. package/dist/static/sdk-libs/_core/http.js +52 -0
  352. package/dist/static/sdk-libs/_core/namespace.js +33 -0
  353. package/dist/static/sdk-libs/_core/sdk-globals.d.ts +50 -0
  354. package/dist/static/sdk-libs/_core/session.js +66 -0
  355. package/dist/static/sdk-libs/agentface/index.js +102 -0
  356. package/dist/static/sdk-libs/agents/index.js +267 -0
  357. package/dist/static/sdk-libs/ai/index.js +153 -0
  358. package/dist/static/sdk-libs/audio/core.js +111 -0
  359. package/dist/static/sdk-libs/audio/index.js +380 -0
  360. package/dist/static/sdk-libs/audio/instruments.js +369 -0
  361. package/dist/static/sdk-libs/auth/config.js +46 -0
  362. package/dist/static/sdk-libs/auth/crypto.js +166 -0
  363. package/dist/static/sdk-libs/auth/events.js +27 -0
  364. package/dist/static/sdk-libs/auth/i18n.js +64 -0
  365. package/dist/static/sdk-libs/auth/index.js +37 -0
  366. package/dist/static/sdk-libs/auth/modal.js +545 -0
  367. package/dist/static/sdk-libs/auth/pill.js +167 -0
  368. package/dist/static/sdk-libs/auth/session.js +792 -0
  369. package/dist/static/sdk-libs/auth/signup.js +232 -0
  370. package/dist/static/sdk-libs/auth/theme.js +96 -0
  371. package/dist/static/sdk-libs/capabilities/index.js +179 -0
  372. package/dist/static/sdk-libs/commerce/index.js +243 -0
  373. package/dist/static/sdk-libs/data/index.js +188 -0
  374. package/dist/static/sdk-libs/dist/aimeat-agentface.js +91 -0
  375. package/dist/static/sdk-libs/dist/aimeat-agents.js +315 -0
  376. package/dist/static/sdk-libs/dist/aimeat-ai.js +168 -0
  377. package/dist/static/sdk-libs/dist/aimeat-audio.js +979 -0
  378. package/dist/static/sdk-libs/dist/aimeat-auth.js +1831 -0
  379. package/dist/static/sdk-libs/dist/aimeat-capabilities.js +177 -0
  380. package/dist/static/sdk-libs/dist/aimeat-commerce.js +265 -0
  381. package/dist/static/sdk-libs/dist/aimeat-data.js +205 -0
  382. package/dist/static/sdk-libs/dist/aimeat-editor.js +299 -0
  383. package/dist/static/sdk-libs/dist/aimeat-header.js +270 -0
  384. package/dist/static/sdk-libs/dist/aimeat-intake.js +94 -0
  385. package/dist/static/sdk-libs/dist/aimeat-live.js +265 -0
  386. package/dist/static/sdk-libs/dist/aimeat-markdown.js +602 -0
  387. package/dist/static/sdk-libs/dist/aimeat-organism.js +394 -0
  388. package/dist/static/sdk-libs/dist/aimeat-portfolio-standalone.js +90 -0
  389. package/dist/static/sdk-libs/dist/aimeat-social.js +157 -0
  390. package/dist/static/sdk-libs/dist/aimeat-speech.js +300 -0
  391. package/dist/static/sdk-libs/dist/aimeat-storage.js +210 -0
  392. package/dist/static/sdk-libs/dist/aimeat-tunnel.js +383 -0
  393. package/dist/static/sdk-libs/dist/aimeat-wallet.js +118 -0
  394. package/dist/static/sdk-libs/dist/aimeat-webmcp.js +187 -0
  395. package/dist/static/sdk-libs/dist/aimeat-work.js +213 -0
  396. package/dist/static/sdk-libs/dist/aimeat-workflows.js +147 -0
  397. package/dist/static/sdk-libs/editor/index.js +229 -0
  398. package/dist/static/sdk-libs/header/index.js +249 -0
  399. package/dist/static/sdk-libs/intake/index.js +75 -0
  400. package/dist/static/sdk-libs/live/index.js +156 -0
  401. package/dist/static/sdk-libs/markdown/index.js +522 -0
  402. package/dist/static/sdk-libs/organism/index.js +381 -0
  403. package/dist/static/sdk-libs/portfolio-standalone/index.js +78 -0
  404. package/dist/static/sdk-libs/social/index.js +140 -0
  405. package/dist/static/sdk-libs/speech/index.js +321 -0
  406. package/dist/static/sdk-libs/storage/index.js +189 -0
  407. package/dist/static/sdk-libs/tunnel/index.js +428 -0
  408. package/dist/static/sdk-libs/wallet/index.js +115 -0
  409. package/dist/static/sdk-libs/webmcp/index.js +185 -0
  410. package/dist/static/sdk-libs/work/index.js +191 -0
  411. package/dist/static/sdk-libs/workflows/index.js +113 -0
  412. package/package.json +5 -2
@@ -25,14 +25,14 @@ export const I18N = {
25
25
  'action.settings': 'Settings',
26
26
  'action.help': 'Help',
27
27
  'help.title': '❓ How this catalog works',
28
- 'help.modesTitle': 'Shared vs Mine (browser-local apps)',
29
- 'help.modes': 'The header toggle picks which BROWSER-LOCAL apps you see it is NOT about server visibility. "Shared" = local apps visible to every account signed in on this browser; "Mine" = local apps private to your account (needs you to be signed in). Your published/server apps are unaffected either way.',
28
+ 'help.modesTitle': 'Creating an app',
29
+ 'help.modes': 'Add app lets you paste HTML or upload an HTML/ZIP file (or Generate with AI). It publishes straight to your account as UNLISTED on the server but not yet public so you can review it before listing. Nothing is stored only in your browser.',
30
30
  'help.whereTitle': 'Where your apps live',
31
- 'help.where': 'Local apps live only in this browser (IndexedDB)they don\'t follow you to another browser or device. Publish an app to put it on the server, then you can manage it from anywhere. Each app shows as ONE card with a badge saying where it lives.',
31
+ 'help.where': 'Every app lives on the server under your account the catalog is server-only, so nothing is kept in this browser. Your apps follow you to any browser or device automatically.',
32
32
  'help.sectionsTitle': 'The two views',
33
- 'help.sections': 'Two tabs. Library = your apps, each as one card whose badge shows its publication state — Listed (published, public in the catalogue), Unlisted (on the server but hidden from the catalogue, still usable by you and anyone with the link), or Local (a draft in this browser, not on the server yet). A purple Staging pill marks an app that also has an unpublished draft waiting. Community = apps other people have published.',
33
+ 'help.sections': 'Two tabs. Library = your apps, each as one card whose badge shows its publication state — Listed (public in the catalogue) or Unlisted (on the server but hidden from the catalogue, still usable by you and anyone with the link). A purple Staging pill marks an app that also has an unpublished draft waiting. Community = apps other people have published.',
34
34
  'help.backupTitle': '"Backup" means two different things',
35
- 'help.backup': 'The 💾 menu (top bar) exports/imports your SERVER apps + extensions as a .zip. Settings Export/Import JSON backs up this browser\'s LOCAL catalog. They are separate.',
35
+ 'help.backup': 'The 💾 menu (top bar) exports/imports your SERVER apps + extensions as a .zip a full server-side backup you can restore selectively.',
36
36
  'help.standaloneTitle': 'Which node this talks to',
37
37
  'help.standalone': 'This catalog is served by its node and always talks to that node (same-origin) — the Server URL in Settings is fixed to it. To use your own, open /app-catalog.html on your node: localhost, aimeat-desktop, and federated nodes each serve their OWN catalog.',
38
38
  'action.theme': 'Toggle light / dark',
@@ -67,8 +67,11 @@ export const I18N = {
67
67
  'sec.parkedBlurb': 'Only you can see and use these — they stay usable but are hidden from the public catalogue. Press Publish to move one back to Published Apps.',
68
68
  'sec.recent': '🕒 Recently Opened',
69
69
  'sec.local': '📚 Your apps',
70
- 'sec.localNote': 'One card per app. The badge shows its publication state — Listed (published, public in the catalogue), Unlisted (on the server but hidden from the public catalogue), or Local (a draft in this browser, not on the server yet). A purple Staging pill marks an app that also has an unpublished draft.',
71
- 'sec.localHint': '💡 Publish a local app to put it on the server then you can manage it from anywhere.',
70
+ 'sec.localNote': 'One card per app. The badge shows its publication state — Listed (public in the catalogue) or Unlisted (on the server but hidden from the public catalogue). A purple Staging pill marks an app that also has an unpublished draft.',
71
+ 'sec.localHint': '💡 Add app publishes to your account as unlistedlist it when it\'s ready to be public.',
72
+ 'fav.title': '⭐ Favourites',
73
+ 'fav.add': 'Add to favourites',
74
+ 'fav.remove': 'Remove from favourites',
72
75
  'stats.apps': 'apps',
73
76
  'stats.stored': 'stored',
74
77
  'addModal.title': 'Add App',
@@ -134,6 +137,8 @@ export const I18N = {
134
137
  'publish.accessCodeHint': '4-64 chars. If set, only people with the code can download.',
135
138
  'publish.submit': 'Publish',
136
139
  'publish.loginRequired': 'Sign in to publish to AIMEAT. Anonymous publishing is disabled — use the Sign in button in the top bar.',
140
+ 'publish.savedUnlisted': 'Saved — your app is on the server as unlisted. Open it and press List when you want it public.',
141
+ 'migrate.found': 'You have {n} app(s) saved only in this browser from the old local catalog. The catalog is now server-only, so they no longer appear here. Download them as a JSON backup now? You can re-publish any you still want.',
137
142
  'auth.signIn': '❤️ Sign In',
138
143
  'auth.loggedIn': 'logged in',
139
144
  'auth.logout': 'Logout',
@@ -262,6 +267,24 @@ export const I18N = {
262
267
  'detail.iconLabel': 'Icon (emoji)',
263
268
  'detail.tagsLabel': 'Tags (comma separated)',
264
269
  'detail.descLabel': 'Description',
270
+ 'detail.descEn': 'Description (English)',
271
+ 'detail.descFi': 'Description (Finnish)',
272
+ 'detail.translateEnFi': 'Translate EN → FI',
273
+ 'detail.translateFiEn': 'Translate FI → EN',
274
+ 'detail.translating': 'Translating…',
275
+ 'detail.trDone': 'Translated',
276
+ 'detail.trEmpty': 'The AI returned nothing — try again.',
277
+ 'detail.trNeedSource': 'Write the source-language description first.',
278
+ 'promote.title': 'Promote',
279
+ 'promote.hint': 'Showcase this app on your public profile with a short pitch. Fill English and/or Finnish; visitors see the one matching their language.',
280
+ 'promote.en': 'Promotion text (English)',
281
+ 'promote.fi': 'Promotion text (Finnish)',
282
+ 'promote.save': 'Save promotion',
283
+ 'promote.remove': 'Stop promoting',
284
+ 'promote.on': 'Promoted',
285
+ 'promote.saving': 'Saving…',
286
+ 'promote.saved': 'Promoted on your profile.',
287
+ 'promote.removed': 'No longer promoted.',
265
288
  'detail.renameHint': 'The app link stays the same — only the displayed name and description change.',
266
289
  'detail.nameRequired': 'Name cannot be empty.',
267
290
  'detail.descRequired': 'Description cannot be empty.',
@@ -336,6 +359,20 @@ export const I18N = {
336
359
  'monetize.saved': 'Tool manifest saved.',
337
360
  'monetize.saveFailed': 'Saving the tool manifest failed',
338
361
  'monetize.needLogin': 'Sign in to edit the tool manifest.',
362
+ 'cost.title': '📊 Cost & contracts — what this app sources',
363
+ 'cost.hint': 'This app’s EXCHANGE contracts: durable metered entitlements it calls, with live spend against each budget and the platform rake. The per-call price comes from the provider; you set only the budget cap.',
364
+ 'cost.empty': 'No EXCHANGE contracts yet — this app sources nothing priced.',
365
+ 'cost.summary': '{n} contract(s) · {spent} morsels spent · {calls} call(s)',
366
+ 'cost.capabilityCol': 'Capability',
367
+ 'cost.providerCol': 'Provider',
368
+ 'cost.priceCol': 'Price / call',
369
+ 'cost.rakeCol': 'Rake',
370
+ 'cost.budgetCol': 'Budget (spent / cap)',
371
+ 'cost.stateCol': 'State',
372
+ 'cost.uncapped': 'uncapped',
373
+ 'cost.remaining': '{n} left',
374
+ 'cost.needLogin': 'Sign in to view this app’s cost & contracts.',
375
+ 'cost.loadFailed': 'Could not load cost & contracts',
339
376
  'detail.editAccess': 'Access code',
340
377
  'detail.accessCodeHint': 'Set or remove an optional code required to open this app.',
341
378
  'detail.accessCodeLabel': 'Access code',
@@ -432,7 +469,7 @@ export const I18N = {
432
469
  'tag.favorites': '⭐ Favorites',
433
470
  'empty.noApps': 'No apps yet',
434
471
  'empty.noAppsDesc': 'Add your first app using the + button above',
435
- 'empty.formats': 'Supports: HTML files, URLs, ZIP packages, AIMEAT imports',
472
+ 'empty.formats': 'Supports: HTML files and ZIP packages',
436
473
  'empty.noMatch': 'No matches',
437
474
  'empty.noMatchDesc': 'No apps match the current filter. Try a different search term or tag.'
438
475
  },
@@ -455,14 +492,14 @@ export const I18N = {
455
492
  'action.settings': 'Asetukset',
456
493
  'action.help': 'Ohje',
457
494
  'help.title': '❓ Miten tämä katalogi toimii',
458
- 'help.modesTitle': 'Jaetut vs Omat (selain-paikalliset appit)',
459
- 'help.modes': 'Yläpalkin valitsin päättää mitkä SELAIN-PAIKALLISET appit näet se EI liity server-näkyvyyteen. "Jaetut" = paikalliset appit näkyvät kaikille tähän selaimeen kirjautuneille tileille; "Omat" = paikalliset appit yksityisiä tilillesi (vaatii sisäänkirjautumisen). Julkaistut/server-appisi eivät muutu kummassakaan.',
495
+ 'help.modesTitle': 'Sovelluksen luominen',
496
+ 'help.modes': 'Lisää sovellus antaa liittää HTML:ää tai ladata HTML/ZIP-tiedoston (tai luoda sen tekoälyllä). Se julkaistaan suoraan tilillesi EI LISTALLA palvelimelle mutta ei vielä julkiseksi joten voit tarkistaa sen ennen listaamista. Mitään ei tallenneta vain selaimeesi.',
460
497
  'help.whereTitle': 'Missä appisi asuvat',
461
- 'help.where': 'Paikalliset appit asuvat vain tässä selaimessa (IndexedDB) ne eivät seuraa toiseen selaimeen tai laitteeseen. Julkaise appi siirtääksesi sen palvelimelle, minkä jälkeen hallitset sitä mistä tahansa. Jokainen appi näkyy YHTENÄ korttina, jonka merkki kertoo missä se sijaitsee.',
498
+ 'help.where': 'Jokainen sovellus sijaitsee palvelimella tililläsikatalogi on vain palvelinpohjainen, joten mitään ei säilytetä tässä selaimessa. Sovelluksesi seuraavat sinua mihin tahansa selaimeen tai laitteeseen automaattisesti.',
462
499
  'help.sectionsTitle': 'Kaksi näkymää',
463
- 'help.sections': 'Kaksi välilehteä. Kirjasto = sinun appisi, jokainen yhtenä korttina jonka merkki kertoo julkaisutilan — Listalla (julkaistu, näkyy katalogissa), Ei listalla (palvelimella mutta piilossa katalogista, sinä ja kuka tahansa linkillä voitte yhä käyttää), tai Paikallinen (luonnos tässä selaimessa, ei vielä palvelimella). Violetti Staging-merkki kertoo että apilla on myös julkaisematon luonnos odottamassa. Yhteisö = muiden julkaisemat appit.',
500
+ 'help.sections': 'Kaksi välilehteä. Kirjasto = sinun appisi, jokainen yhtenä korttina jonka merkki kertoo julkaisutilan — Listalla (näkyy julkisessa katalogissa) tai Ei listalla (palvelimella mutta piilossa katalogista, sinä ja kuka tahansa linkillä voitte yhä käyttää). Violetti Staging-merkki kertoo että apilla on myös julkaisematon luonnos odottamassa. Yhteisö = muiden julkaisemat appit.',
464
501
  'help.backupTitle': '"Varmuuskopio" tarkoittaa kahta eri asiaa',
465
- 'help.backup': '💾-valikko (yläpalkki) vie/tuo PALVELIN-appisi + laajennukset .zip-tiedostona. Asetukset Vie/Tuo JSON varmuuskopioi tämän selaimen PAIKALLISEN katalogin. Ne ovat erillisiä.',
502
+ 'help.backup': '💾-valikko (yläpalkki) vie/tuo PALVELIN-appisi + laajennukset .zip-tiedostona täysi palvelinpuolen varmuuskopio, jonka voit palauttaa valikoiden.',
466
503
  'help.standaloneTitle': 'Mille nodelle tämä puhuu',
467
504
  'help.standalone': 'Tämän katalogin tarjoilee sen node, ja se puhuu aina sille nodelle (same-origin) — Asetusten Palvelin-URL on kiinnitetty siihen. Käyttääksesi omaa: avaa /app-catalog.html omalla nodellasi. localhost, aimeat-desktop ja federoidut nodet tarjoilevat kukin OMAN kataloginsa.',
468
505
  'action.theme': 'Vaihda vaalea / tumma',
@@ -497,8 +534,11 @@ export const I18N = {
497
534
  'sec.parkedBlurb': 'Vain sinä näet ja voit käyttää näitä — ne pysyvät käytettävissä mutta eivät näy julkisessa katalogissa. Paina Julkaise siirtääksesi takaisin julkaistuihin.',
498
535
  'sec.recent': '🕒 Viimeksi avatut',
499
536
  'sec.local': '📚 Sinun sovelluksesi',
500
- 'sec.localNote': 'Yksi kortti per sovellus. Merkki kertoo julkaisutilan — Listalla (julkaistu, näkyy katalogissa), Ei listalla (palvelimella mutta piilossa julkisesta katalogista) tai Paikallinen (luonnos tässä selaimessa, ei vielä palvelimella). Violetti Staging-merkki kertoo että apilla on myös julkaisematon luonnos.',
501
- 'sec.localHint': '💡 Julkaise paikallinen sovellus viedäksesi sen palvelimellesen jälkeen voit hallita sitä mistä tahansa.',
537
+ 'sec.localNote': 'Yksi kortti per sovellus. Merkki kertoo julkaisutilan — Listalla (näkyy julkisessa katalogissa) tai Ei listalla (palvelimella mutta piilossa julkisesta katalogista). Violetti Staging-merkki kertoo että apilla on myös julkaisematon luonnos.',
538
+ 'sec.localHint': '💡 Lisää sovellus julkaisee tilillesi ei-listallalistaa se kun se on valmis julkiseksi.',
539
+ 'fav.title': '⭐ Suosikit',
540
+ 'fav.add': 'Lisää suosikkeihin',
541
+ 'fav.remove': 'Poista suosikeista',
502
542
  'stats.apps': 'sovellusta',
503
543
  'stats.stored': 'tallennettu',
504
544
  'addModal.title': 'Lisää sovellus',
@@ -564,6 +604,8 @@ export const I18N = {
564
604
  'publish.accessCodeHint': '4–64 merkkiä. Jos asetettu, vain koodin tietävät voivat ladata.',
565
605
  'publish.submit': 'Julkaise',
566
606
  'publish.loginRequired': 'Kirjaudu sisään julkaistaksesi AIMEATiin. Anonyymi julkaisu on poistettu käytöstä — käytä yläpalkin Kirjaudu-painiketta.',
607
+ 'publish.savedUnlisted': 'Tallennettu — sovelluksesi on palvelimella ei-listalla. Avaa se ja paina Listaa kun haluat sen julkiseksi.',
608
+ 'migrate.found': 'Sinulla on {n} sovellus(ta) tallennettuna vain tähän selaimeen vanhasta paikallisesta katalogista. Katalogi on nyt vain palvelinpohjainen, joten ne eivät enää näy täällä. Ladataanko ne nyt JSON-varmuuskopiona? Voit julkaista uudelleen ne jotka haluat säilyttää.',
567
609
  'auth.signIn': '❤️ Kirjaudu',
568
610
  'auth.loggedIn': 'kirjautunut',
569
611
  'auth.logout': 'Kirjaudu ulos',
@@ -692,6 +734,24 @@ export const I18N = {
692
734
  'detail.iconLabel': 'Kuvake (emoji)',
693
735
  'detail.tagsLabel': 'Tunnisteet (pilkulla eroteltuna)',
694
736
  'detail.descLabel': 'Kuvaus',
737
+ 'detail.descEn': 'Kuvaus (englanti)',
738
+ 'detail.descFi': 'Kuvaus (suomi)',
739
+ 'detail.translateEnFi': 'Käännä EN → FI',
740
+ 'detail.translateFiEn': 'Käännä FI → EN',
741
+ 'detail.translating': 'Käännetään…',
742
+ 'detail.trDone': 'Käännetty',
743
+ 'detail.trEmpty': 'Tekoäly ei palauttanut mitään — yritä uudelleen.',
744
+ 'detail.trNeedSource': 'Kirjoita ensin lähdekielinen kuvaus.',
745
+ 'promote.title': 'Nosto',
746
+ 'promote.hint': 'Nosta tämä sovellus julkiseen profiiliisi lyhyellä myyntipuheella. Täytä englanti ja/tai suomi; vierailijat näkevät kieltään vastaavan.',
747
+ 'promote.en': 'Nostoteksti (englanti)',
748
+ 'promote.fi': 'Nostoteksti (suomi)',
749
+ 'promote.save': 'Tallenna nosto',
750
+ 'promote.remove': 'Lopeta nostaminen',
751
+ 'promote.on': 'Nostettu',
752
+ 'promote.saving': 'Tallennetaan…',
753
+ 'promote.saved': 'Nostettu profiiliisi.',
754
+ 'promote.removed': 'Ei enää nostettu.',
695
755
  'detail.renameHint': 'Sovelluksen linkki pysyy samana — vain näytettävä nimi ja kuvaus muuttuvat.',
696
756
  'detail.nameRequired': 'Nimi ei voi olla tyhjä.',
697
757
  'detail.descRequired': 'Kuvaus ei voi olla tyhjä.',
@@ -764,6 +824,20 @@ export const I18N = {
764
824
  'monetize.saved': 'Työkalumanifesti tallennettu.',
765
825
  'monetize.saveFailed': 'Työkalumanifestin tallennus epäonnistui',
766
826
  'monetize.needLogin': 'Kirjaudu sisään muokataksesi työkalumanifestia.',
827
+ 'cost.title': '📊 Kulut & sopimukset — mitä tämä appi hankkii',
828
+ 'cost.hint': 'Tämän appin EXCHANGE-sopimukset: pysyvät mitatut käyttöoikeudet joita se kutsuu, live-kulutus kutakin budjettia vasten ja alustan siivu. Kutsukohtainen hinta tulee tarjoajalta; sinä asetat vain budjettikaton.',
829
+ 'cost.empty': 'Ei vielä EXCHANGE-sopimuksia — tämä appi ei hanki mitään hinnoiteltua.',
830
+ 'cost.summary': '{n} sopimus(ta) · {spent} morselia käytetty · {calls} kutsu(a)',
831
+ 'cost.capabilityCol': 'Kyvykkyys',
832
+ 'cost.providerCol': 'Tarjoaja',
833
+ 'cost.priceCol': 'Hinta / kutsu',
834
+ 'cost.rakeCol': 'Siivu',
835
+ 'cost.budgetCol': 'Budjetti (käytetty / katto)',
836
+ 'cost.stateCol': 'Tila',
837
+ 'cost.uncapped': 'ei kattoa',
838
+ 'cost.remaining': '{n} jäljellä',
839
+ 'cost.needLogin': 'Kirjaudu sisään nähdäksesi tämän appin kulut & sopimukset.',
840
+ 'cost.loadFailed': 'Kulujen & sopimusten lataus epäonnistui',
767
841
  'detail.skillDetach': 'Irrota',
768
842
  'detail.skillAttachError': 'Taidon liittäminen epäonnistui',
769
843
  'detail.editAccess': 'Pääsykoodi',
@@ -862,7 +936,7 @@ export const I18N = {
862
936
  'tag.favorites': '⭐ Suosikit',
863
937
  'empty.noApps': 'Ei vielä sovelluksia',
864
938
  'empty.noAppsDesc': 'Lisää ensimmäinen sovellus yllä olevalla +-painikkeella',
865
- 'empty.formats': 'Tukee: HTML-tiedostot, URL-osoitteet, ZIP-paketit, AIMEAT-tuonnit',
939
+ 'empty.formats': 'Tukee: HTML-tiedostot ja ZIP-paketit',
866
940
  'empty.noMatch': 'Ei osumia',
867
941
  'empty.noMatchDesc': 'Mikään sovellus ei vastaa nykyistä suodatinta. Kokeile eri hakusanaa tai tunnistetta.'
868
942
  }
@@ -4,23 +4,28 @@
4
4
  * served src/static/app-catalog.html). Holds catalog state, rendering, actions, and the
5
5
  * window._launcher handler surface consumed by inline onclick in the markup. Carved from
6
6
  * the former single inline <script>; further modules are split out incrementally.
7
- * @structure imports (i18n data) → state → db → api → render → actions → window._launcher → init
7
+ * @structure imports (i18n data) → state → api → render → actions → window._launcher → init
8
8
  * @usage built, not loaded raw: pnpm build:app-catalog
9
+ * @version-history
10
+ * v2.0.0 — 2026-07-20 — Server-only cutover: drop the IndexedDB/Shared-Mine plumbing; wire the
11
+ * create-flow publish (initAppsIo showPublishModal) and the one-time legacy-local migration.
9
12
  */
10
13
  import { t, getLang, setLang, applyI18n } from './i18n.js';
11
14
  import { escapeHtml, jsArg, sourceLabel, sourceLabelText, bareOwnerName, sameOwner, filterAttr, isSameOriginUrl, currentOwnerName, generateId, readFileAsText } from './util.js';
12
- import { getAllApps, saveApp, deleteApp, openDB, getDbName, getDbMode, setDbMode, closeDbInstance } from './db.js';
15
+ import { getAllApps, saveApp, deleteApp } from './db.js';
13
16
  import { showConfirm, closeConfirm, showNotice, dismissNotice, dtlBtn } from './ui.js';
14
17
  import { loadConfig, saveConfig } from './config.js';
15
18
  import { extractZip, bundleZip } from './zip.js';
16
- import { initDetail, refreshServerMgmt, openDetailView, editAppDetails, closeDetailView, detailLaunch, mountLoginPill, detailAboutEdit, detailAboutCancel, detailAboutSave, detailToggleFavorite, detailAccessCodeEdit, detailAccessCodeCancel, detailAccessCodeSave, detailSkillAttachToggle, detailSkillAttach, detailSkillDetach, detailSetScreenshot, detailRefreshScreenshot, detailAiRun, detailAiTest, detailAiKeep, detailAiDiscard, detailTestDraftLive, detailPublishTestedDraft, openStagingPreview, sourceTestDraftLive, sourcePublishTested, detailEditSource, detailImproveExternal, detailSharePrompt, detailPublish, detailDelete, openPublishedDetail, fetchAppContentBase64, showLineageModal, showProtectionModal, saveProtection, showVersionsModal, restoreVersion, forkVersion } from './detail.js';
19
+ import { initDetail, refreshServerMgmt, openDetailView, editAppDetails, closeDetailView, detailLaunch, mountLoginPill, detailAboutEdit, detailAboutCancel, detailAboutSave, detailTranslateDesc, detailPromoteSave, detailPromoteClear, detailToggleFavorite, detailAccessCodeEdit, detailAccessCodeCancel, detailAccessCodeSave, detailSkillAttachToggle, detailSkillAttach, detailSkillDetach, detailSetScreenshot, detailRefreshScreenshot, detailAiRun, detailAiTest, detailAiKeep, detailAiDiscard, detailTestDraftLive, detailPublishTestedDraft, openStagingPreview, sourceTestDraftLive, sourcePublishTested, detailEditSource, detailImproveExternal, detailSharePrompt, detailPublish, detailDelete, openPublishedDetail, fetchAppContentBase64, showLineageModal, showProtectionModal, saveProtection, showVersionsModal, restoreVersion, forkVersion } from './detail.js';
17
20
  import { monetizeAddTool, monetizeEditTool, monetizeCancelEdit, monetizeSaveTool, monetizeDeleteTool } from './monetize.js';
18
21
  import { loadCortexExtensions, showCortexPopup, cortexCopy, getCortexOwnerToken, openCortexEditor, cortexEditorAddLib, cortexEditorSave, cortexEditorExport, closeCortexEditor, openPromptBuilder, closePbPanel, buildPromptFromBuilder, updatePbPreview } from './cortex.js';
19
- import { initSettings, applyTheme, updateThemeToggle, toggleTheme, getThemePref, openSettings, saveSettings, syncConfigToServer, loadConfigFromServer, closeSettings, openHelp, closeHelp, exportBackup, handleImportBackup, jsonImportSelectAll, submitJsonImport, removeDuplicateApps, clearAllData } from './settings.js';
20
- import { initAppsIo, setEditingAppId, addAppFromZip, addAppFromUrl, addAppFromFile, addAppFromSource, showModal, requireSignInThen, parseAppMeta, closeModal, switchTab, handleFileDrop, handleSave } from './apps-io.js';
21
- import { initServerIo, isOperatorSession, importFromAimeat, processAimeatImport, showPublishModal, submitPublish, toggleCommunity, switchView, showSubdomainModal, submitSubdomainAssign, unassignSubdomain, closeConsents, openConsents, revokeConsent, toggleBackupMenu, toggleCreateMenu, closeCreateMenu, toggleCortexBar, exportBackupZip, importBackupPick, importBackupFile, backupUpdateSummary, backupSelectAll, submitBackupRestore, loadPublishedApps, applyServerFilter, unpublishApp, toggleParkApp, toggleForkApp, deleteServerApp } from './server-io.js';
22
- import { initRender, setServerManifests, setOwnServerApps, setIframeUrl, serverStateByFilename, serverAppManifests, ownAppProtection, ownServerApps, currentIframeUrl, renderTags, filterByTag, launchApp, launchInTab, viewPublished, launchInIframe, renderApps, renderRecentlyOpened, closeIframe, openExternal, showContextMenu, hideContextMenu, handleContextAction, viewSource, generateSharePrompt, generateHomepagePrompt, onCardDragStart, onCardDragEnd, onCardDragOver, onCardDrop } from './render.js';
22
+ import { initSettings, applyTheme, updateThemeToggle, toggleTheme, getThemePref, openSettings, saveSettings, syncConfigToServer, loadConfigFromServer, closeSettings, openHelp, closeHelp } from './settings.js';
23
+ import { initAppsIo, setEditingAppId, showModal, requireSignInThen, parseAppMeta, closeModal, switchTab, handleFileDrop, handleSave } from './apps-io.js';
24
+ import { initServerIo, isOperatorSession, showPublishModal, submitPublish, toggleCommunity, switchView, showSubdomainModal, submitSubdomainAssign, unassignSubdomain, closeConsents, openConsents, revokeConsent, toggleBackupMenu, toggleCreateMenu, closeCreateMenu, toggleCortexBar, exportBackupZip, importBackupPick, importBackupFile, backupUpdateSummary, backupSelectAll, submitBackupRestore, loadPublishedApps, refreshFavoritesUI, applyServerFilter, unpublishApp, toggleParkApp, toggleForkApp, deleteServerApp } from './server-io.js';
25
+ import { initRender, setServerManifests, setOwnServerApps, setIframeUrl, serverStateByFilename, serverAppManifests, ownAppProtection, ownServerApps, currentIframeUrl, renderTags, filterByTag, launchApp, launchInTab, viewPublished, launchInIframe, renderApps, closeIframe, openExternal, showContextMenu, hideContextMenu, handleContextAction, viewSource, generateSharePrompt, generateHomepagePrompt } from './render.js';
23
26
  import { initAppAgents, showAppAgentsModal, agentsDeploy, agentsUndeploy } from './app-agents.js';
27
+ import { checkLegacyLocalApps } from './migrate.js';
28
+ import { toggleFavorite } from './favorites.js';
24
29
 
25
30
 
26
31
  // ── i18n (en / fi) ─────────────────────────────────
@@ -38,39 +43,10 @@ import { initAppAgents, showAppAgentsModal, agentsDeploy, agentsUndeploy } from
38
43
  try { renderApps(); } catch (e) {}
39
44
  try { renderTags(); } catch (e) {}
40
45
  try { loadPublishedApps(); } catch (e) {}
41
- try { renderRecentlyOpened(); } catch (e) {}
42
46
  try { loadCortexExtensions(); } catch (e) {}
43
47
  try { mountLoginPill(); } catch (e) {} // re-render the golden pill in the new language
44
48
  }
45
49
 
46
- function switchDbMode(mode) {
47
- if (mode === getDbMode()) return;
48
- // Personal mode is per-account and meaningless without a session — it would silently show the
49
- // shared Global DB (the "my apps vanished when I changed browser" confusion). Require sign-in
50
- // first, keeping the toggle on the current mode until the user actually signs in.
51
- if (mode === 'personal' && !currentOwnerName()) {
52
- updateModeToggle();
53
- requireSignInThen(function () { switchDbMode('personal'); });
54
- return;
55
- }
56
- setDbMode(mode);
57
- closeDbInstance();
58
- updateModeToggle();
59
- refreshAll();
60
- }
61
-
62
- function updateModeToggle() {
63
- var globalBtn = document.getElementById('mode-global');
64
- var personalBtn = document.getElementById('mode-personal');
65
- if (!globalBtn || !personalBtn) return;
66
- globalBtn.classList.toggle('active', getDbMode() === 'global');
67
- personalBtn.classList.toggle('active', getDbMode() === 'personal');
68
- // Personal needs a session — dim it and explain via the title when logged out.
69
- var signedIn = !!currentOwnerName();
70
- personalBtn.classList.toggle('mode-btn-locked', !signedIn);
71
- personalBtn.title = signedIn ? t('mode.personal.title') : t('mode.personal.needsLogin');
72
- }
73
-
74
50
  // ── Config → config.js (loadConfig / saveConfig, imported above) ──
75
51
 
76
52
  // ── generateId / readFileAsText → util.js (imported above) ──
@@ -105,7 +81,6 @@ import { initAppAgents, showAppAgentsModal, agentsDeploy, agentsUndeploy } from
105
81
  // ── Public API ────────────────────────────────────
106
82
 
107
83
  window._launcher = {
108
- openDB: openDB,
109
84
  getAllApps: getAllApps,
110
85
  saveApp: saveApp,
111
86
  deleteApp: deleteApp,
@@ -126,10 +101,6 @@ import { initAppAgents, showAppAgentsModal, agentsDeploy, agentsUndeploy } from
126
101
  handleContextAction: handleContextAction,
127
102
  generateId: generateId,
128
103
  readFileAsText: readFileAsText,
129
- addAppFromUrl: addAppFromUrl,
130
- addAppFromFile: addAppFromFile,
131
- addAppFromSource: addAppFromSource,
132
- addAppFromZip: addAppFromZip,
133
104
  showModal: showModal,
134
105
  closeModal: closeModal,
135
106
  openSettings: openSettings,
@@ -138,18 +109,15 @@ import { initAppAgents, showAppAgentsModal, agentsDeploy, agentsUndeploy } from
138
109
  saveSettings: saveSettings,
139
110
  closeSettings: closeSettings,
140
111
  applyTheme: applyTheme,
141
- exportBackup: exportBackup,
142
- clearAllData: clearAllData,
143
- importFromAimeat: importFromAimeat,
144
- processAimeatImport: processAimeatImport,
145
112
  viewSource: viewSource,
146
113
  generateHomepagePrompt: generateHomepagePrompt,
147
114
  showPublishModal: showPublishModal,
148
115
  submitPublish: submitPublish,
149
116
  loadPublishedApps: loadPublishedApps,
117
+ // Toggle a server-backed favourite (ref = "owner/filename"), then re-render the ⭐ group + stars.
118
+ toggleFavorite: function (ref) { toggleFavorite(ref).then(function () { try { refreshFavoritesUI(); } catch (e) {} }); },
150
119
  toggleCommunity: toggleCommunity,
151
120
  switchView: switchView,
152
- switchDbMode: switchDbMode,
153
121
  unpublishApp: unpublishApp,
154
122
  deleteServerApp: deleteServerApp,
155
123
  toggleParkApp: toggleParkApp,
@@ -157,6 +125,9 @@ import { initAppAgents, showAppAgentsModal, agentsDeploy, agentsUndeploy } from
157
125
  detailAboutEdit: detailAboutEdit,
158
126
  detailAboutCancel: detailAboutCancel,
159
127
  detailAboutSave: detailAboutSave,
128
+ detailTranslateDesc: detailTranslateDesc,
129
+ detailPromoteSave: detailPromoteSave,
130
+ detailPromoteClear: detailPromoteClear,
160
131
  detailToggleFavorite: detailToggleFavorite,
161
132
  detailAccessCodeEdit: detailAccessCodeEdit,
162
133
  detailAccessCodeCancel: detailAccessCodeCancel,
@@ -176,9 +147,6 @@ import { initAppAgents, showAppAgentsModal, agentsDeploy, agentsUndeploy } from
176
147
  revokeConsent: revokeConsent,
177
148
  submitSubdomainAssign: submitSubdomainAssign,
178
149
  unassignSubdomain: unassignSubdomain,
179
- jsonImportSelectAll: jsonImportSelectAll,
180
- submitJsonImport: submitJsonImport,
181
- removeDuplicateApps: removeDuplicateApps,
182
150
  toggleBackupMenu: toggleBackupMenu,
183
151
  toggleCreateMenu: toggleCreateMenu,
184
152
  toggleCortexBar: toggleCortexBar,
@@ -214,7 +182,6 @@ import { initAppAgents, showAppAgentsModal, agentsDeploy, agentsUndeploy } from
214
182
  detailSetScreenshot: detailSetScreenshot,
215
183
  detailRefreshScreenshot: detailRefreshScreenshot,
216
184
  openPublishedDetail: openPublishedDetail,
217
- renderRecentlyOpened: renderRecentlyOpened,
218
185
  loadCortexExtensions: loadCortexExtensions,
219
186
  showCortexPopup: showCortexPopup,
220
187
  cortexCopy: cortexCopy,
@@ -225,10 +192,6 @@ import { initAppAgents, showAppAgentsModal, agentsDeploy, agentsUndeploy } from
225
192
  cortexEditorExport: cortexEditorExport,
226
193
  openPromptBuilder: openPromptBuilder,
227
194
  closePbPanel: closePbPanel,
228
- onCardDragStart: onCardDragStart,
229
- onCardDragEnd: onCardDragEnd,
230
- onCardDragOver: onCardDragOver,
231
- onCardDrop: onCardDrop,
232
195
  syncConfigToServer: syncConfigToServer,
233
196
  loadConfigFromServer: loadConfigFromServer,
234
197
  setLanguage: setLanguage,
@@ -244,7 +207,6 @@ import { initAppAgents, showAppAgentsModal, agentsDeploy, agentsUndeploy } from
244
207
  allApps = [];
245
208
  renderApps();
246
209
  loadPublishedApps();
247
- renderRecentlyOpened();
248
210
  }
249
211
 
250
212
  // ── Bootstrap ─────────────────────────────────────
@@ -254,7 +216,7 @@ import { initAppAgents, showAppAgentsModal, agentsDeploy, agentsUndeploy } from
254
216
  // can open the detail view or mount the login pill (whose onAuthChanged uses these).
255
217
  initDetail({
256
218
  refreshAll: refreshAll, loadPublishedApps: loadPublishedApps, renderApps: renderApps,
257
- updateModeToggle: updateModeToggle, getCortexOwnerToken: getCortexOwnerToken, launchApp: launchApp,
219
+ updateModeToggle: function () {}, getCortexOwnerToken: getCortexOwnerToken, launchApp: launchApp,
258
220
  viewPublished: viewPublished, viewSource: viewSource, generateId: generateId,
259
221
  generateSharePrompt: generateSharePrompt, openPromptBuilder: openPromptBuilder,
260
222
  showPublishModal: showPublishModal,
@@ -266,7 +228,7 @@ import { initAppAgents, showAppAgentsModal, agentsDeploy, agentsUndeploy } from
266
228
  });
267
229
  initAppAgents({ getServerManifests: function () { return serverAppManifests; } });
268
230
  initSettings({ generateId: generateId, renderApps: renderApps, loadPublishedApps: loadPublishedApps });
269
- initAppsIo({ generateId: generateId, readFileAsText: readFileAsText, renderApps: renderApps, getMainApps: function () { return allApps; } });
231
+ initAppsIo({ generateId: generateId, readFileAsText: readFileAsText, renderApps: renderApps, getMainApps: function () { return allApps; }, showPublishModal: showPublishModal });
270
232
  initServerIo({
271
233
  getMainApps: function () { return allApps; },
272
234
  getServerState: function () { return serverStateByFilename; },
@@ -300,7 +262,6 @@ import { initAppAgents, showAppAgentsModal, agentsDeploy, agentsUndeploy } from
300
262
  ? _urlLang
301
263
  : ((loadConfig().language === 'fi') ? 'fi' : 'en'));
302
264
  applyI18n();
303
- updateModeToggle();
304
265
  // Restore the last-used view (Kirjasto / Yhteisö); defaults to Library.
305
266
  var _savedView = 'library';
306
267
  try { _savedView = localStorage.getItem('appCatalogView') || 'library'; } catch (e) { /* private mode */ }
@@ -328,6 +289,10 @@ import { initAppAgents, showAppAgentsModal, agentsDeploy, agentsUndeploy } from
328
289
  if (e.target === this) closeConfirm(false);
329
290
  });
330
291
 
292
+ // One-time migration off the retired browser-local catalog: offer a JSON export of any apps
293
+ // left in the old IndexedDB store, so the server-only cutover never silently loses them.
294
+ try { checkLegacyLocalApps(); } catch (e) { /* migration is best-effort */ }
295
+
331
296
  // Deep link ?create=1 — open the AI prompt builder ("Build an app") right away.
332
297
  if (_params.get('create') === '1') {
333
298
  try { openPromptBuilder(null); } catch (e) { /* prompt builder optional */ }
@@ -374,21 +339,6 @@ import { initAppAgents, showAppAgentsModal, agentsDeploy, agentsUndeploy } from
374
339
  }
375
340
  });
376
341
 
377
- // ── Import backup file input ────────────────────
378
- document.getElementById('import-file-input').addEventListener('change', function () {
379
- if (this.files && this.files.length > 0) {
380
- handleImportBackup(this.files[0]);
381
- this.value = ''; // Reset for re-import
382
- }
383
- });
384
-
385
- // ── Click AIMEAT import overlay to close ────────
386
- document.getElementById('aimeat-import-overlay').addEventListener('click', function (e) {
387
- if (e.target === this) {
388
- this.hidden = true;
389
- }
390
- });
391
-
392
342
  // ── Click publish overlay to close ──────────────
393
343
  document.getElementById('publish-overlay').addEventListener('click', function (e) {
394
344
  if (e.target === this) {
@@ -420,11 +370,6 @@ import { initAppAgents, showAppAgentsModal, agentsDeploy, agentsUndeploy } from
420
370
  this.hidden = true;
421
371
  }
422
372
  });
423
- document.getElementById('json-import-overlay').addEventListener('click', function (e) {
424
- if (e.target === this) {
425
- this.hidden = true;
426
- }
427
- });
428
373
  document.addEventListener('click', function (e) {
429
374
  var menu = document.getElementById('backup-menu');
430
375
  if (!menu.hidden && !menu.contains(e.target) && e.target.id !== 'backup-btn') {
@@ -592,16 +537,12 @@ import { initAppAgents, showAppAgentsModal, agentsDeploy, agentsUndeploy } from
592
537
  document.getElementById('source-overlay').hidden = true;
593
538
  } else if (!document.getElementById('context-menu').hidden) {
594
539
  hideContextMenu();
595
- } else if (!document.getElementById('aimeat-import-overlay').hidden) {
596
- document.getElementById('aimeat-import-overlay').hidden = true;
597
540
  } else if (!document.getElementById('publish-overlay').hidden) {
598
541
  document.getElementById('publish-overlay').hidden = true;
599
542
  } else if (!document.getElementById('subdomain-overlay').hidden) {
600
543
  document.getElementById('subdomain-overlay').hidden = true;
601
544
  } else if (!document.getElementById('backup-overlay').hidden) {
602
545
  document.getElementById('backup-overlay').hidden = true;
603
- } else if (!document.getElementById('json-import-overlay').hidden) {
604
- document.getElementById('json-import-overlay').hidden = true;
605
546
  } else if (!document.getElementById('settings-overlay').hidden) {
606
547
  closeSettings();
607
548
  } else if (!document.getElementById('modal-overlay').hidden) {
@@ -624,27 +565,6 @@ import { initAppAgents, showAppAgentsModal, agentsDeploy, agentsUndeploy } from
624
565
  }
625
566
  });
626
567
 
627
- // ── Long-press for touch devices ─────────────────
628
- document.getElementById('app-grid').addEventListener('touchstart', function(e) {
629
- var card = e.target.closest('.app-card');
630
- if (!card) return;
631
- var appId = card.getAttribute('data-id');
632
- card._longPressTimer = setTimeout(function() {
633
- e.preventDefault();
634
- window._launcher.showContextMenu(e.touches[0], appId);
635
- }, 500);
636
- }, { passive: false });
637
-
638
- document.getElementById('app-grid').addEventListener('touchend', function(e) {
639
- var card = e.target.closest('.app-card');
640
- if (card && card._longPressTimer) clearTimeout(card._longPressTimer);
641
- });
642
-
643
- document.getElementById('app-grid').addEventListener('touchmove', function(e) {
644
- var card = e.target.closest('.app-card');
645
- if (card && card._longPressTimer) clearTimeout(card._longPressTimer);
646
- });
647
-
648
568
  // ── Copy Source button ───────────────────────────
649
569
  document.getElementById('copy-source-btn').addEventListener('click', function() {
650
570
  var code = document.getElementById('source-code').value;
@@ -0,0 +1,88 @@
1
+ /**
2
+ * @file migrate.js
3
+ * @description One-time migration off the retired browser-local catalog. The server-only cutover
4
+ * dropped IndexedDB persistence; a user may still have apps in the old `AppLauncherDB[_owner]`
5
+ * store from before. On first load after the change this reads those apps (read-only, never
6
+ * deletes the DB) and offers a one-time JSON download so nothing is silently lost, then sets a
7
+ * "done" flag so it never nags again.
8
+ * @usage import { checkLegacyLocalApps } from './migrate.js'; checkLegacyLocalApps();
9
+ * @version-history
10
+ * v1.0.0 — 2026-07-20 — Added with the server-only catalog cutover.
11
+ */
12
+ import { showConfirm } from './ui.js';
13
+ import { t } from './i18n.js';
14
+
15
+ const DONE_KEY = 'appCatalogLocalMigrated';
16
+
17
+ // Discover every legacy DB name (the shared `AppLauncherDB` plus any per-account
18
+ // `AppLauncherDB_<owner>` from the old "Mine" mode) when the browser supports enumeration.
19
+ async function legacyDbNames() {
20
+ var names = ['AppLauncherDB'];
21
+ try {
22
+ if (indexedDB.databases) {
23
+ var list = await indexedDB.databases();
24
+ list.forEach(function (d) {
25
+ if (d && d.name && d.name.indexOf('AppLauncherDB') === 0 && names.indexOf(d.name) < 0) names.push(d.name);
26
+ });
27
+ }
28
+ } catch (e) { /* enumeration unsupported — fall back to the shared DB only */ }
29
+ return names;
30
+ }
31
+
32
+ function openExisting(name) {
33
+ return new Promise(function (resolve) {
34
+ var req;
35
+ try { req = indexedDB.open(name); } catch (e) { resolve(null); return; }
36
+ req.onsuccess = function () { resolve(req.result); };
37
+ req.onerror = function () { resolve(null); };
38
+ // A brand-new open would upgrade an empty DB; leave it storeless so readApps returns [].
39
+ req.onupgradeneeded = function () { /* no stores */ };
40
+ });
41
+ }
42
+
43
+ function readApps(db) {
44
+ return new Promise(function (resolve) {
45
+ if (!db || !db.objectStoreNames || !db.objectStoreNames.contains('apps')) { resolve([]); return; }
46
+ try {
47
+ var tx = db.transaction('apps', 'readonly');
48
+ var r = tx.objectStore('apps').getAll();
49
+ r.onsuccess = function () { resolve(r.result || []); };
50
+ r.onerror = function () { resolve([]); };
51
+ } catch (e) { resolve([]); }
52
+ });
53
+ }
54
+
55
+ function downloadJson(apps) {
56
+ var json = JSON.stringify({ version: 1, exportedAt: new Date().toISOString(), apps: apps }, null, 2);
57
+ var blob = new Blob([json], { type: 'application/json' });
58
+ var url = URL.createObjectURL(blob);
59
+ var a = document.createElement('a');
60
+ a.href = url;
61
+ a.download = 'app-catalog-local-apps.json';
62
+ document.body.appendChild(a);
63
+ a.click();
64
+ document.body.removeChild(a);
65
+ URL.revokeObjectURL(url);
66
+ }
67
+
68
+ export async function checkLegacyLocalApps() {
69
+ try { if (localStorage.getItem(DONE_KEY)) return; } catch (e) { return; }
70
+ var apps = [];
71
+ try {
72
+ var names = await legacyDbNames();
73
+ for (var i = 0; i < names.length; i++) {
74
+ var db = await openExisting(names[i]);
75
+ var found = await readApps(db);
76
+ if (found && found.length) apps = apps.concat(found);
77
+ try { if (db) db.close(); } catch (e) { /* ignore */ }
78
+ }
79
+ } catch (e) { /* if anything goes wrong, do not block the catalog */ }
80
+
81
+ if (!apps.length) { try { localStorage.setItem(DONE_KEY, '1'); } catch (e) { /* private mode */ } return; }
82
+
83
+ var msg = (t('migrate.found') || 'You have {n} local app(s) from the old catalog. Download them as a backup?')
84
+ .replace('{n}', String(apps.length));
85
+ var wants = await showConfirm(msg);
86
+ if (wants) downloadJson(apps);
87
+ try { localStorage.setItem(DONE_KEY, '1'); } catch (e) { /* private mode */ }
88
+ }
@@ -68,7 +68,7 @@ export function monetizeOnOpen(owner, appId, isOwn) {
68
68
 
69
69
  function priceLabel(tool) {
70
70
  var parts = [];
71
- if (tool.price && tool.price.morsels > 0) parts.push(tool.price.morsels + ' 🥩');
71
+ if (tool.price && tool.price.morsels > 0) parts.push(tool.price.morsels + ' morsels');
72
72
  if (tool.priceMoney && tool.priceMoney.amount > 0) {
73
73
  parts.push((tool.priceMoney.amount / MONEY_UNIT) + ' ' + tool.priceMoney.currency);
74
74
  }