@songsid/agend 2.3.0-beta.8 → 2.3.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 (736) hide show
  1. package/README.md +16 -8
  2. package/README.zh-TW.md +16 -8
  3. package/dist/adapter-world.js.map +1 -1
  4. package/dist/agend-home.d.ts +5 -0
  5. package/dist/agend-home.js +7 -0
  6. package/dist/agend-home.js.map +1 -0
  7. package/dist/agent-cli-instructions.md +9 -2
  8. package/dist/agent-cli.js +44 -0
  9. package/dist/agent-cli.js.map +1 -1
  10. package/dist/agent-endpoint.d.ts +9 -0
  11. package/dist/agent-endpoint.js +23 -0
  12. package/dist/agent-endpoint.js.map +1 -1
  13. package/dist/agent-file-read.js +1 -1
  14. package/dist/agent-file-read.js.map +1 -1
  15. package/dist/agent-whats-new.d.ts +16 -0
  16. package/dist/agent-whats-new.js +54 -11
  17. package/dist/agent-whats-new.js.map +1 -1
  18. package/dist/apply-job.js.map +1 -1
  19. package/dist/atomic-write.js.map +1 -1
  20. package/dist/auth-api.d.ts +12 -0
  21. package/dist/auth-api.js +77 -22
  22. package/dist/auth-api.js.map +1 -1
  23. package/dist/authority/authority-state.d.ts +4 -0
  24. package/dist/authority/authority-state.js +17 -8
  25. package/dist/authority/authority-state.js.map +1 -1
  26. package/dist/authority/session-authorizations.js.map +1 -1
  27. package/dist/authority/web-authority.d.ts +16 -8
  28. package/dist/authority/web-authority.js +19 -5
  29. package/dist/authority/web-authority.js.map +1 -1
  30. package/dist/backend/antigravity.d.ts +2 -2
  31. package/dist/backend/antigravity.js +2 -1
  32. package/dist/backend/antigravity.js.map +1 -1
  33. package/dist/backend/claude-code.js +13 -4
  34. package/dist/backend/claude-code.js.map +1 -1
  35. package/dist/backend/codex-metadata.d.ts +8 -0
  36. package/dist/backend/codex-metadata.js +74 -17
  37. package/dist/backend/codex-metadata.js.map +1 -1
  38. package/dist/backend/codex-package.d.ts +30 -0
  39. package/dist/backend/codex-package.js +133 -0
  40. package/dist/backend/codex-package.js.map +1 -0
  41. package/dist/backend/codex-session-lookup.js.map +1 -1
  42. package/dist/backend/codex.d.ts +2 -0
  43. package/dist/backend/codex.js +25 -5
  44. package/dist/backend/codex.js.map +1 -1
  45. package/dist/backend/credential-profile.d.ts +2 -0
  46. package/dist/backend/credential-profile.js +2 -0
  47. package/dist/backend/credential-profile.js.map +1 -1
  48. package/dist/backend/effort-metadata.js.map +1 -1
  49. package/dist/backend/grok.js +58 -3
  50. package/dist/backend/grok.js.map +1 -1
  51. package/dist/backend/kiro-engine-ledger.js.map +1 -1
  52. package/dist/backend/kiro-kas-store.js.map +1 -1
  53. package/dist/backend/kiro-v2-store.js.map +1 -1
  54. package/dist/backend/kiro-v3-identity.js.map +1 -1
  55. package/dist/backend/kiro.d.ts +9 -1
  56. package/dist/backend/kiro.js +102 -1
  57. package/dist/backend/kiro.js.map +1 -1
  58. package/dist/backend/launch-effort.js.map +1 -1
  59. package/dist/backend/mock.js.map +1 -1
  60. package/dist/backend/muse.js.map +1 -1
  61. package/dist/backend/opencode-permission.js +1 -1
  62. package/dist/backend/opencode-permission.js.map +1 -1
  63. package/dist/backend/opencode.js +2 -1
  64. package/dist/backend/opencode.js.map +1 -1
  65. package/dist/backend/session-signals.js.map +1 -1
  66. package/dist/backend-availability.js.map +1 -1
  67. package/dist/backend-command.d.ts +2 -0
  68. package/dist/backend-command.js +3 -1
  69. package/dist/backend-command.js.map +1 -1
  70. package/dist/backend-outage.js.map +1 -1
  71. package/dist/binary-probe.js +1 -1
  72. package/dist/binary-probe.js.map +1 -1
  73. package/dist/bug-report-cli.d.ts +30 -0
  74. package/dist/bug-report-cli.js +139 -0
  75. package/dist/bug-report-cli.js.map +1 -0
  76. package/dist/bug-report.d.ts +47 -0
  77. package/dist/bug-report.js +137 -0
  78. package/dist/bug-report.js.map +1 -0
  79. package/dist/bundled-skills.d.ts +18 -0
  80. package/dist/bundled-skills.js +38 -0
  81. package/dist/bundled-skills.js.map +1 -0
  82. package/dist/cache-service.js +1 -1
  83. package/dist/cache-service.js.map +1 -1
  84. package/dist/channel/adapters/discord-client-retirement.d.ts +5 -0
  85. package/dist/channel/adapters/discord-client-retirement.js +57 -0
  86. package/dist/channel/adapters/discord-client-retirement.js.map +1 -0
  87. package/dist/channel/adapters/discord.d.ts +4 -0
  88. package/dist/channel/adapters/discord.js +57 -12
  89. package/dist/channel/adapters/discord.js.map +1 -1
  90. package/dist/channel/adapters/telegram.d.ts +6 -0
  91. package/dist/channel/adapters/telegram.js +54 -14
  92. package/dist/channel/adapters/telegram.js.map +1 -1
  93. package/dist/channel/mcp-server.js +21 -4
  94. package/dist/channel/mcp-server.js.map +1 -1
  95. package/dist/channel/mcp-tools.d.ts +3 -1
  96. package/dist/channel/mcp-tools.js +43 -6
  97. package/dist/channel/mcp-tools.js.map +1 -1
  98. package/dist/channel/message-queue.js.map +1 -1
  99. package/dist/channel/sticker-download.js.map +1 -1
  100. package/dist/channel/tool-router.js.map +1 -1
  101. package/dist/child-process.d.ts +21 -0
  102. package/dist/child-process.js +54 -0
  103. package/dist/child-process.js.map +1 -0
  104. package/dist/classic-channel-manager.js +3 -2
  105. package/dist/classic-channel-manager.js.map +1 -1
  106. package/dist/cli-entry.js.map +1 -1
  107. package/dist/cli-flags.js.map +1 -1
  108. package/dist/cli.d.ts +1 -0
  109. package/dist/cli.js +443 -311
  110. package/dist/cli.js.map +1 -1
  111. package/dist/command-table.d.ts +1 -1
  112. package/dist/command-table.js +1 -1
  113. package/dist/command-table.js.map +1 -1
  114. package/dist/compact-watch.js.map +1 -1
  115. package/dist/completion-install.js.map +1 -1
  116. package/dist/completion.js.map +1 -1
  117. package/dist/config-validator.d.ts +6 -0
  118. package/dist/config-validator.js +81 -8
  119. package/dist/config-validator.js.map +1 -1
  120. package/dist/config.d.ts +15 -3
  121. package/dist/config.js +41 -10
  122. package/dist/config.js.map +1 -1
  123. package/dist/connection-secrets.js.map +1 -1
  124. package/dist/context-guardian.js.map +1 -1
  125. package/dist/coredump-filter.js.map +1 -1
  126. package/dist/cpu-profile.js.map +1 -1
  127. package/dist/cross-instance-envelope.js.map +1 -1
  128. package/dist/cross-instance-notice.d.ts +7 -0
  129. package/dist/cross-instance-notice.js +10 -0
  130. package/dist/cross-instance-notice.js.map +1 -1
  131. package/dist/daemon.d.ts +39 -1
  132. package/dist/daemon.js +234 -15
  133. package/dist/daemon.js.map +1 -1
  134. package/dist/daily-summary.js.map +1 -1
  135. package/dist/darwin-memory.js +1 -1
  136. package/dist/darwin-memory.js.map +1 -1
  137. package/dist/dashboard-live.d.ts +8 -0
  138. package/dist/dashboard-live.js +35 -0
  139. package/dist/dashboard-live.js.map +1 -0
  140. package/dist/dashboard-permissions.d.ts +69 -0
  141. package/dist/dashboard-permissions.js +138 -0
  142. package/dist/dashboard-permissions.js.map +1 -0
  143. package/dist/decision-list.d.ts +25 -0
  144. package/dist/decision-list.js +30 -0
  145. package/dist/decision-list.js.map +1 -0
  146. package/dist/delivery-consumed-watch.js.map +1 -1
  147. package/dist/delivery-outbox.js +5 -0
  148. package/dist/delivery-outbox.js.map +1 -1
  149. package/dist/delivery-outbox.test.js.map +1 -1
  150. package/dist/delivery-reconciliation.test.js.map +1 -1
  151. package/dist/diagnostic-redaction.d.ts +10 -0
  152. package/dist/diagnostic-redaction.js +167 -0
  153. package/dist/diagnostic-redaction.js.map +1 -0
  154. package/dist/doctor.d.ts +8 -0
  155. package/dist/doctor.js +52 -0
  156. package/dist/doctor.js.map +1 -1
  157. package/dist/e2e-isolation.js.map +1 -1
  158. package/dist/event-log.js.map +1 -1
  159. package/dist/export-import.js +7 -5
  160. package/dist/export-import.js.map +1 -1
  161. package/dist/fleet-control-audit.js.map +1 -1
  162. package/dist/fleet-label.js.map +1 -1
  163. package/dist/fleet-level-config.d.ts +1 -1
  164. package/dist/fleet-level-config.js +2 -0
  165. package/dist/fleet-level-config.js.map +1 -1
  166. package/dist/fleet-lock.js +3 -2
  167. package/dist/fleet-lock.js.map +1 -1
  168. package/dist/fleet-manager.d.ts +258 -15
  169. package/dist/fleet-manager.js +1605 -304
  170. package/dist/fleet-manager.js.map +1 -1
  171. package/dist/fleet-process-handlers.d.ts +18 -0
  172. package/dist/fleet-process-handlers.js +70 -0
  173. package/dist/fleet-process-handlers.js.map +1 -0
  174. package/dist/fleet-startup.d.ts +14 -0
  175. package/dist/fleet-startup.js +30 -0
  176. package/dist/fleet-startup.js.map +1 -0
  177. package/dist/fleet-traffic.d.ts +36 -0
  178. package/dist/fleet-traffic.js +100 -0
  179. package/dist/fleet-traffic.js.map +1 -1
  180. package/dist/forged-envelope-scan.d.ts +0 -20
  181. package/dist/forged-envelope-scan.js +3 -2
  182. package/dist/forged-envelope-scan.js.map +1 -1
  183. package/dist/full-restart.d.ts +5 -5
  184. package/dist/full-restart.js +5 -5
  185. package/dist/full-restart.js.map +1 -1
  186. package/dist/general-knowledge/skills/bug-report/SKILL.md +31 -0
  187. package/dist/general-knowledge/skills/credential-profiles/SKILL.md +52 -11
  188. package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +5 -1
  189. package/dist/general-knowledge/skills/fleet-config/SKILL.md +3 -1
  190. package/dist/general-knowledge/skills/fleet-restart/SKILL.md +8 -9
  191. package/dist/general-knowledge/skills/multi-channel/SKILL.md +1 -1
  192. package/dist/general-knowledge/skills/persona-emoji/SKILL.md +1 -1
  193. package/dist/general-knowledge/skills/scheduling/SKILL.md +4 -0
  194. package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +321 -0
  195. package/dist/general-knowledge/skills/web-appearance/SKILL.md +28 -4
  196. package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +553 -0
  197. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +170 -35
  198. package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +1 -1
  199. package/dist/general-knowledge/steering/core-rules.md +1 -1
  200. package/dist/host-memory.js.map +1 -1
  201. package/dist/idle-gate-log.d.ts +19 -0
  202. package/dist/idle-gate-log.js +15 -0
  203. package/dist/idle-gate-log.js.map +1 -0
  204. package/dist/instance-config-impact.js +25 -4
  205. package/dist/instance-config-impact.js.map +1 -1
  206. package/dist/instance-lifecycle.d.ts +10 -1
  207. package/dist/instance-lifecycle.js +86 -25
  208. package/dist/instance-lifecycle.js.map +1 -1
  209. package/dist/instructions.d.ts +12 -6
  210. package/dist/instructions.js +19 -10
  211. package/dist/instructions.js.map +1 -1
  212. package/dist/kiro-compact-pane.js.map +1 -1
  213. package/dist/kiro-db-reader.js.map +1 -1
  214. package/dist/kiro-engine-status.js.map +1 -1
  215. package/dist/kiro-log-gate.js.map +1 -1
  216. package/dist/kiro-transcript-lane.js.map +1 -1
  217. package/dist/locale.js +428 -20
  218. package/dist/locale.js.map +1 -1
  219. package/dist/log-service.js.map +1 -1
  220. package/dist/log-steps.d.ts +1 -0
  221. package/dist/log-steps.js +15 -0
  222. package/dist/log-steps.js.map +1 -1
  223. package/dist/logger.js +3 -2
  224. package/dist/logger.js.map +1 -1
  225. package/dist/login-controller.d.ts +45 -0
  226. package/dist/login-controller.js +101 -5
  227. package/dist/login-controller.js.map +1 -1
  228. package/dist/login-flows.js +1 -1
  229. package/dist/login-manager.js.map +1 -1
  230. package/dist/login-shell.js +3 -1
  231. package/dist/login-shell.js.map +1 -1
  232. package/dist/ls-attach-resolve.d.ts +96 -0
  233. package/dist/ls-attach-resolve.js +180 -0
  234. package/dist/ls-attach-resolve.js.map +1 -0
  235. package/dist/ls-rows.d.ts +12 -0
  236. package/dist/ls-rows.js +83 -4
  237. package/dist/ls-rows.js.map +1 -1
  238. package/dist/memory-pressure.js.map +1 -1
  239. package/dist/muse-usage-relay.js.map +1 -1
  240. package/dist/needs-you-hub.d.ts +98 -1
  241. package/dist/needs-you-hub.js +313 -44
  242. package/dist/needs-you-hub.js.map +1 -1
  243. package/dist/needs-you-live.d.ts +5 -2
  244. package/dist/needs-you-live.js +6 -3
  245. package/dist/needs-you-live.js.map +1 -1
  246. package/dist/needs-you.d.ts +68 -3
  247. package/dist/needs-you.js +38 -4
  248. package/dist/needs-you.js.map +1 -1
  249. package/dist/network-family.js.map +1 -1
  250. package/dist/operator-control-paths.js +3 -4
  251. package/dist/operator-control-paths.js.map +1 -1
  252. package/dist/outbound-handlers.d.ts +3 -0
  253. package/dist/outbound-handlers.js +76 -19
  254. package/dist/outbound-handlers.js.map +1 -1
  255. package/dist/outbound-schemas.d.ts +112 -64
  256. package/dist/outbound-schemas.js +39 -12
  257. package/dist/outbound-schemas.js.map +1 -1
  258. package/dist/outbox-open-error.js.map +1 -1
  259. package/dist/pane-context-cache.js.map +1 -1
  260. package/dist/paths.js.map +1 -1
  261. package/dist/private-dir.js.map +1 -1
  262. package/dist/probe-worker-pool.js.map +1 -1
  263. package/dist/profile-control.js.map +1 -1
  264. package/dist/prompt-file-ref.js.map +1 -1
  265. package/dist/provider-probe.js.map +1 -1
  266. package/dist/provider-secret-registry.d.ts +5 -3
  267. package/dist/provider-secret-registry.js +8 -2
  268. package/dist/provider-secret-registry.js.map +1 -1
  269. package/dist/public-link-auto-open.d.ts +32 -0
  270. package/dist/public-link-auto-open.js +86 -0
  271. package/dist/public-link-auto-open.js.map +1 -0
  272. package/dist/public-link-owner.d.ts +10 -0
  273. package/dist/public-link-owner.js +36 -0
  274. package/dist/public-link-owner.js.map +1 -0
  275. package/dist/public-link-progress.d.ts +12 -1
  276. package/dist/public-link-progress.js +5 -10
  277. package/dist/public-link-progress.js.map +1 -1
  278. package/dist/public-preview.js.map +1 -1
  279. package/dist/public-tunnel-config.d.ts +11 -0
  280. package/dist/public-tunnel-config.js +68 -0
  281. package/dist/public-tunnel-config.js.map +1 -0
  282. package/dist/public-web-gateway.d.ts +2 -0
  283. package/dist/public-web-gateway.js +19 -6
  284. package/dist/public-web-gateway.js.map +1 -1
  285. package/dist/public-web-link.d.ts +48 -3
  286. package/dist/public-web-link.js +177 -51
  287. package/dist/public-web-link.js.map +1 -1
  288. package/dist/quickstart-api.d.ts +9 -0
  289. package/dist/quickstart-api.js +82 -0
  290. package/dist/quickstart-api.js.map +1 -1
  291. package/dist/quickstart.d.ts +5 -0
  292. package/dist/quickstart.js +40 -25
  293. package/dist/quickstart.js.map +1 -1
  294. package/dist/reply-buttons-controller.js.map +1 -1
  295. package/dist/reply-dedup.js.map +1 -1
  296. package/dist/resource-report.js +1 -1
  297. package/dist/resource-report.js.map +1 -1
  298. package/dist/restart-guard.js.map +1 -1
  299. package/dist/restart-progress.d.ts +80 -1
  300. package/dist/restart-progress.js +306 -21
  301. package/dist/restart-progress.js.map +1 -1
  302. package/dist/rollout-index.js.map +1 -1
  303. package/dist/runtime-cpu-profile.js.map +1 -1
  304. package/dist/scheduler/db.d.ts +40 -13
  305. package/dist/scheduler/db.js +123 -89
  306. package/dist/scheduler/db.js.map +1 -1
  307. package/dist/scheduler/scheduler.js.map +1 -1
  308. package/dist/scheduler/task-board-text.d.ts +26 -0
  309. package/dist/scheduler/task-board-text.js +140 -0
  310. package/dist/scheduler/task-board-text.js.map +1 -0
  311. package/dist/scheduler/task-board.d.ts +730 -0
  312. package/dist/scheduler/task-board.js +2179 -0
  313. package/dist/scheduler/task-board.js.map +1 -0
  314. package/dist/scheduler/types.d.ts +6 -1
  315. package/dist/sd-notify.js +2 -1
  316. package/dist/sd-notify.js.map +1 -1
  317. package/dist/search/search-docs.d.ts +58 -0
  318. package/dist/search/search-docs.js +63 -0
  319. package/dist/search/search-docs.js.map +1 -0
  320. package/dist/search/search-index.d.ts +141 -0
  321. package/dist/search/search-index.js +440 -0
  322. package/dist/search/search-index.js.map +1 -0
  323. package/dist/search/search-store.d.ts +122 -0
  324. package/dist/search/search-store.js +236 -0
  325. package/dist/search/search-store.js.map +1 -0
  326. package/dist/search/search-worker.d.ts +1 -0
  327. package/dist/search/search-worker.js +163 -0
  328. package/dist/search/search-worker.js.map +1 -0
  329. package/dist/secret-file.js.map +1 -1
  330. package/dist/secret-store.d.ts +2 -0
  331. package/dist/secret-store.js +21 -0
  332. package/dist/secret-store.js.map +1 -1
  333. package/dist/service-installer.d.ts +75 -0
  334. package/dist/service-installer.js +206 -0
  335. package/dist/service-installer.js.map +1 -1
  336. package/dist/service-owner.d.ts +44 -0
  337. package/dist/service-owner.js +48 -0
  338. package/dist/service-owner.js.map +1 -0
  339. package/dist/service-plan.d.ts +39 -2
  340. package/dist/service-plan.js +188 -6
  341. package/dist/service-plan.js.map +1 -1
  342. package/dist/session-oom.d.ts +21 -0
  343. package/dist/session-oom.js +139 -0
  344. package/dist/session-oom.js.map +1 -0
  345. package/dist/settings-api.d.ts +13 -0
  346. package/dist/settings-api.js +73 -12
  347. package/dist/settings-api.js.map +1 -1
  348. package/dist/settings-baseline.js.map +1 -1
  349. package/dist/settings-change.d.ts +5 -0
  350. package/dist/settings-change.js +167 -6
  351. package/dist/settings-change.js.map +1 -1
  352. package/dist/settings-confirmation.d.ts +9 -1
  353. package/dist/settings-confirmation.js +18 -8
  354. package/dist/settings-confirmation.js.map +1 -1
  355. package/dist/settings-control.js +2 -1
  356. package/dist/settings-control.js.map +1 -1
  357. package/dist/settings-effect.js +29 -4
  358. package/dist/settings-effect.js.map +1 -1
  359. package/dist/settings-http-confirmation.d.ts +13 -0
  360. package/dist/settings-http-confirmation.js +15 -3
  361. package/dist/settings-http-confirmation.js.map +1 -1
  362. package/dist/settings-instance-patch.js +13 -1
  363. package/dist/settings-instance-patch.js.map +1 -1
  364. package/dist/settings-request-capability.d.ts +4 -1
  365. package/dist/settings-request-capability.js +11 -3
  366. package/dist/settings-request-capability.js.map +1 -1
  367. package/dist/settings-transaction.js.map +1 -1
  368. package/dist/setup-auth.d.ts +1 -0
  369. package/dist/setup-auth.js +2 -1
  370. package/dist/setup-auth.js.map +1 -1
  371. package/dist/setup-form.d.ts +1 -1
  372. package/dist/setup-form.js +23 -7
  373. package/dist/setup-form.js.map +1 -1
  374. package/dist/setup-host.d.ts +6 -1
  375. package/dist/setup-host.js +15 -4
  376. package/dist/setup-host.js.map +1 -1
  377. package/dist/setup-wizard.js +6 -3
  378. package/dist/setup-wizard.js.map +1 -1
  379. package/dist/spawn-gate.d.ts +5 -0
  380. package/dist/spawn-gate.js +28 -8
  381. package/dist/spawn-gate.js.map +1 -1
  382. package/dist/start-attempt.d.ts +1 -0
  383. package/dist/start-attempt.js +18 -0
  384. package/dist/start-attempt.js.map +1 -0
  385. package/dist/start-failure-notice.d.ts +4 -0
  386. package/dist/start-failure-notice.js +20 -0
  387. package/dist/start-failure-notice.js.map +1 -0
  388. package/dist/start-failure.d.ts +147 -0
  389. package/dist/start-failure.js +281 -0
  390. package/dist/start-failure.js.map +1 -0
  391. package/dist/startup-memory-deadline.d.ts +23 -0
  392. package/dist/startup-memory-deadline.js +55 -0
  393. package/dist/startup-memory-deadline.js.map +1 -0
  394. package/dist/statusline-watcher.js.map +1 -1
  395. package/dist/storm-window.js.map +1 -1
  396. package/dist/systemd-peer-bus.js.map +1 -1
  397. package/dist/target-queue-worker.js.map +1 -1
  398. package/dist/task-board-summary.d.ts +101 -0
  399. package/dist/task-board-summary.js +177 -0
  400. package/dist/task-board-summary.js.map +1 -0
  401. package/dist/task-board-tool.d.ts +25 -0
  402. package/dist/task-board-tool.js +155 -0
  403. package/dist/task-board-tool.js.map +1 -0
  404. package/dist/task-board-waker.d.ts +51 -0
  405. package/dist/task-board-waker.js +141 -0
  406. package/dist/task-board-waker.js.map +1 -0
  407. package/dist/tips.js.map +1 -1
  408. package/dist/tmux-control.js +1 -1
  409. package/dist/tmux-control.js.map +1 -1
  410. package/dist/tmux-exec-helper.d.ts +104 -0
  411. package/dist/tmux-exec-helper.js +412 -0
  412. package/dist/tmux-exec-helper.js.map +1 -0
  413. package/dist/tmux-manager.d.ts +1 -0
  414. package/dist/tmux-manager.js +29 -13
  415. package/dist/tmux-manager.js.map +1 -1
  416. package/dist/tmux-read.js +2 -2
  417. package/dist/tmux-read.js.map +1 -1
  418. package/dist/tool-permissions.js +9 -42
  419. package/dist/tool-permissions.js.map +1 -1
  420. package/dist/tool-progress.js.map +1 -1
  421. package/dist/topic-archiver.js.map +1 -1
  422. package/dist/topic-commands.d.ts +13 -0
  423. package/dist/topic-commands.js +56 -33
  424. package/dist/topic-commands.js.map +1 -1
  425. package/dist/topic-link.js.map +1 -1
  426. package/dist/transcript-delta-reader.js.map +1 -1
  427. package/dist/transcript-jsonl.js.map +1 -1
  428. package/dist/transcript-monitor.js +5 -0
  429. package/dist/transcript-monitor.js.map +1 -1
  430. package/dist/transcript-sources.d.ts +2 -0
  431. package/dist/transcript-sources.js +4 -0
  432. package/dist/transcript-sources.js.map +1 -1
  433. package/dist/tunnel/cloudflared-install.js +3 -1
  434. package/dist/tunnel/cloudflared-install.js.map +1 -1
  435. package/dist/tunnel/cloudflared.d.ts +7 -16
  436. package/dist/tunnel/cloudflared.js +100 -13
  437. package/dist/tunnel/cloudflared.js.map +1 -1
  438. package/dist/tunnel/lease.js +3 -2
  439. package/dist/tunnel/lease.js.map +1 -1
  440. package/dist/tunnel/manager.js.map +1 -1
  441. package/dist/tunnel/named-diagnostics.d.ts +13 -0
  442. package/dist/tunnel/named-diagnostics.js +52 -0
  443. package/dist/tunnel/named-diagnostics.js.map +1 -0
  444. package/dist/tunnel/public-fetch.js.map +1 -1
  445. package/dist/tunnel/purpose-lane.js.map +1 -1
  446. package/dist/tunnel/types.d.ts +4 -1
  447. package/dist/tunnel/types.js.map +1 -1
  448. package/dist/types.d.ts +55 -2
  449. package/dist/ui/app-effects.js +27 -5
  450. package/dist/ui/app.html +2 -0
  451. package/dist/ui/backend-signin.js +297 -0
  452. package/dist/ui/chat-effects.js +15 -7
  453. package/dist/ui/chat-msg-actions.js +176 -0
  454. package/dist/ui/chat-render.js +67 -20
  455. package/dist/ui/chat-store.js +218 -48
  456. package/dist/ui/chat-strings.js +42 -10
  457. package/dist/ui/chat-thread.js +249 -23
  458. package/dist/ui/effect-trial-controller.js +13 -8
  459. package/dist/ui/effect-trial-offer.js +2 -31
  460. package/dist/ui/effects-waker.js +2 -0
  461. package/dist/ui/fleet-canvas-layout.js +13 -4
  462. package/dist/ui/fleet-canvas.js +120 -43
  463. package/dist/ui/fleet-office-editor.js +149 -0
  464. package/dist/ui/fleet-office-model.js +358 -0
  465. package/dist/ui/fleet-office-pack.js +199 -0
  466. package/dist/ui/fleet-office-skin.js +474 -0
  467. package/dist/ui/fleet-office-store.js +49 -0
  468. package/dist/ui/fleet-office-zones.js +177 -0
  469. package/dist/ui/fleet-office.js +434 -0
  470. package/dist/ui/fleet-org.js +21 -8
  471. package/dist/ui/fleet-route-scene.js +272 -0
  472. package/dist/ui/fleet-route-worker.js +21 -0
  473. package/dist/ui/fleet-route.js +88 -0
  474. package/dist/ui/fleet-task-card.js +436 -0
  475. package/dist/ui/fleet-task-common.js +482 -0
  476. package/dist/ui/fleet-task-work.js +107 -0
  477. package/dist/ui/fleet-tasks.js +569 -0
  478. package/dist/ui/fleet-traffic.js +227 -45
  479. package/dist/ui/fleet-usage.js +36 -2
  480. package/dist/ui/guest-refused.html +41 -0
  481. package/dist/ui/icons/icon-192.png +0 -0
  482. package/dist/ui/icons/icon-512.png +0 -0
  483. package/dist/ui/image-lightbox.js +45 -1
  484. package/dist/ui/image-zoom.js +242 -0
  485. package/dist/ui/panel-chat.js +282 -89
  486. package/dist/ui/panel-details.js +2 -0
  487. package/dist/ui/panel-fleet.js +32 -71
  488. package/dist/ui/panel-needs.js +23 -6
  489. package/dist/ui/panel-settings.js +158 -32
  490. package/dist/ui/preview-panel.js +2 -1
  491. package/dist/ui/search-dialog.js +350 -0
  492. package/dist/ui/settings-appearance.js +170 -10
  493. package/dist/ui/settings-apply.js +3 -3
  494. package/dist/ui/settings-confirm.js +7 -2
  495. package/dist/ui/settings-dialogs.js +46 -21
  496. package/dist/ui/settings-discord.js +6 -2
  497. package/dist/ui/settings-guests.js +2 -1
  498. package/dist/ui/settings-learn.js +1 -0
  499. package/dist/ui/settings-model.js +51 -18
  500. package/dist/ui/settings-mods.js +8 -3
  501. package/dist/ui/settings-owner-effects.js +6 -4
  502. package/dist/ui/settings-strings.js +180 -6
  503. package/dist/ui/settings-token.js +3 -1
  504. package/dist/ui/settings-web-authority.js +13 -5
  505. package/dist/ui/settings-wizard.js +13 -7
  506. package/dist/ui/shared/agend-auth.js +3 -1
  507. package/dist/ui/shared/app-i18n.js +12 -5
  508. package/dist/ui/shared/app-needs.js +2 -2
  509. package/dist/ui/shared/app-prefs.js +15 -0
  510. package/dist/ui/shared/app-route.js +18 -0
  511. package/dist/ui/shared/app-shell.js +172 -28
  512. package/dist/ui/shared/app-store.js +3 -2
  513. package/dist/ui/shared/app-stream.js +7 -2
  514. package/dist/ui/shared/app-themes.js +116 -1
  515. package/dist/ui/shared/app.css +681 -54
  516. package/dist/ui/shared/app.js +9 -1
  517. package/dist/ui/shared/card-strings.js +4 -0
  518. package/dist/ui/shared/effect-app-runtime.js +7 -5
  519. package/dist/ui/shared/effect-chat-runtime.js +2 -7
  520. package/dist/ui/shared/effect-cursor.js +3 -2
  521. package/dist/ui/shared/effect-display-metadata.js +39 -0
  522. package/dist/ui/shared/effect-owner-source.js +8 -1
  523. package/dist/ui/shared/effect-policy.js +2 -2
  524. package/dist/ui/shared/effect-registry.js +30 -11
  525. package/dist/ui/shared/effect-runtime-loader.js +3 -1
  526. package/dist/ui/shared/effect-stock-snapshot.js +9 -0
  527. package/dist/ui/shared/effect-stock-trust.js +40 -9
  528. package/dist/ui/shared/effect-trial-source.js +1 -1
  529. package/dist/ui/shared/effect-trial-stage.js +27 -21
  530. package/dist/ui/shared/effect-trial.css +1 -1
  531. package/dist/ui/shared/feature-flags.js +127 -0
  532. package/dist/ui/shared/guest-refused.js +35 -0
  533. package/dist/ui/shared/header-tools.js +2 -2
  534. package/dist/ui/shared/identity-card.js +26 -8
  535. package/dist/ui/shared/identity-color.js +17 -10
  536. package/dist/ui/shared/instance-nav.js +183 -28
  537. package/dist/ui/shared/owner-css.js +30 -1
  538. package/dist/ui/shared/panel-view.js +14 -29
  539. package/dist/ui/shared/text-undo.js +210 -0
  540. package/dist/ui/shared/tokens.css +13 -3
  541. package/dist/ui/shared/ui-dialog.js +5 -4
  542. package/dist/ui/shared/ui-icons.js +4 -0
  543. package/dist/ui/shared/ui-menu.js +20 -7
  544. package/dist/ui/shared/ui-toast.js +16 -3
  545. package/dist/ui/shared/usage-dialog.js +206 -16
  546. package/dist/ui/shared/view-strings.js +68 -4
  547. package/dist/ui/shared/web-authorize.js +50 -1
  548. package/dist/ui/signin.html +2 -0
  549. package/dist/ui/web-terminal/terminal.css +4 -4
  550. package/dist/ui/web-terminal/terminal.html +10 -10
  551. package/dist/ui/web-terminal/terminal.js +58 -12
  552. package/dist/update-check.js.map +1 -1
  553. package/dist/update-dispatch.js +1 -1
  554. package/dist/update-dispatch.js.map +1 -1
  555. package/dist/update-install.js.map +1 -1
  556. package/dist/update-launch.d.ts +10 -1
  557. package/dist/update-launch.js +39 -14
  558. package/dist/update-launch.js.map +1 -1
  559. package/dist/update-marker.js.map +1 -1
  560. package/dist/update-preflight.js +1 -1
  561. package/dist/update-preflight.js.map +1 -1
  562. package/dist/update-progress.d.ts +3 -1
  563. package/dist/update-progress.js +3 -1
  564. package/dist/update-progress.js.map +1 -1
  565. package/dist/usage/format-rich.js +6 -3
  566. package/dist/usage/format-rich.js.map +1 -1
  567. package/dist/usage/i18n-keys.d.ts +1 -1
  568. package/dist/usage/i18n-keys.js +2 -1
  569. package/dist/usage/i18n-keys.js.map +1 -1
  570. package/dist/usage/i18n.d.ts +13 -1
  571. package/dist/usage/i18n.js +29 -0
  572. package/dist/usage/i18n.js.map +1 -1
  573. package/dist/usage/providers.d.ts +89 -2
  574. package/dist/usage/providers.js +247 -41
  575. package/dist/usage/providers.js.map +1 -1
  576. package/dist/usage/statusline-usage.js.map +1 -1
  577. package/dist/usage/usage-api.d.ts +5 -1
  578. package/dist/usage/usage-api.js +185 -58
  579. package/dist/usage/usage-api.js.map +1 -1
  580. package/dist/usage-report.js.map +1 -1
  581. package/dist/usage-rollup.js +1 -1
  582. package/dist/usage-rollup.js.map +1 -1
  583. package/dist/user-systemd.d.ts +24 -0
  584. package/dist/user-systemd.js +75 -0
  585. package/dist/user-systemd.js.map +1 -0
  586. package/dist/view-api.d.ts +7 -0
  587. package/dist/view-api.js +16 -4
  588. package/dist/view-api.js.map +1 -1
  589. package/dist/wake-coordinator.js.map +1 -1
  590. package/dist/web-ambient.js +9 -2
  591. package/dist/web-ambient.js.map +1 -1
  592. package/dist/web-api.d.ts +58 -10
  593. package/dist/web-api.js +1104 -71
  594. package/dist/web-api.js.map +1 -1
  595. package/dist/web-app-icon-worker.d.ts +1 -0
  596. package/dist/web-app-icon-worker.js +16 -0
  597. package/dist/web-app-icon-worker.js.map +1 -0
  598. package/dist/web-app-icon.d.ts +109 -0
  599. package/dist/web-app-icon.js +257 -0
  600. package/dist/web-app-icon.js.map +1 -0
  601. package/dist/web-appearance-policy.js.map +1 -1
  602. package/dist/web-appearance-tools.d.ts +1 -0
  603. package/dist/web-appearance-tools.js +7 -3
  604. package/dist/web-appearance-tools.js.map +1 -1
  605. package/dist/web-appearance-view.js.map +1 -1
  606. package/dist/web-appearance.d.ts +4 -10
  607. package/dist/web-appearance.js +7 -4
  608. package/dist/web-appearance.js.map +1 -1
  609. package/dist/web-auth.d.ts +8 -4
  610. package/dist/web-auth.js +33 -21
  611. package/dist/web-auth.js.map +1 -1
  612. package/dist/web-backend-login.d.ts +130 -0
  613. package/dist/web-backend-login.js +225 -0
  614. package/dist/web-backend-login.js.map +1 -0
  615. package/dist/web-card.js.map +1 -1
  616. package/dist/web-channel-echo.js.map +1 -1
  617. package/dist/web-chat-history.d.ts +118 -8
  618. package/dist/web-chat-history.js +220 -25
  619. package/dist/web-chat-history.js.map +1 -1
  620. package/dist/web-commands.d.ts +5 -1
  621. package/dist/web-commands.js +1 -1
  622. package/dist/web-commands.js.map +1 -1
  623. package/dist/web-cookie-names.d.ts +5 -0
  624. package/dist/web-cookie-names.js +12 -0
  625. package/dist/web-cookie-names.js.map +1 -0
  626. package/dist/web-effect-bundle.d.ts +2 -2
  627. package/dist/web-effect-library.js.map +1 -1
  628. package/dist/web-effect-playback.js.map +1 -1
  629. package/dist/web-effect-profiles.d.ts +16 -16
  630. package/dist/web-effect-profiles.js +3 -2
  631. package/dist/web-effect-profiles.js.map +1 -1
  632. package/dist/web-effect-service.js.map +1 -1
  633. package/dist/web-effect-tools.js.map +1 -1
  634. package/dist/web-effect-trials.js.map +1 -1
  635. package/dist/web-effect-upload.js.map +1 -1
  636. package/dist/web-effect.d.ts +6 -6
  637. package/dist/web-effect.js.map +1 -1
  638. package/dist/web-experimental-http.d.ts +2 -0
  639. package/dist/web-experimental-http.js +12 -0
  640. package/dist/web-experimental-http.js.map +1 -0
  641. package/dist/web-experimental.d.ts +11 -0
  642. package/dist/web-experimental.js +49 -0
  643. package/dist/web-experimental.js.map +1 -0
  644. package/dist/web-guest-access.d.ts +4 -2
  645. package/dist/web-guest-access.js +22 -10
  646. package/dist/web-guest-access.js.map +1 -1
  647. package/dist/web-guest-rooms.js.map +1 -1
  648. package/dist/web-host-guard.d.ts +11 -0
  649. package/dist/web-host-guard.js +11 -0
  650. package/dist/web-host-guard.js.map +1 -1
  651. package/dist/web-icons.d.ts +33 -1
  652. package/dist/web-icons.js +83 -11
  653. package/dist/web-icons.js.map +1 -1
  654. package/dist/web-identity-color.js.map +1 -1
  655. package/dist/web-image-meta.d.ts +13 -0
  656. package/dist/web-image-meta.js +62 -0
  657. package/dist/web-image-meta.js.map +1 -1
  658. package/dist/web-images.js.map +1 -1
  659. package/dist/web-libavoid.d.ts +14 -0
  660. package/dist/web-libavoid.js +46 -0
  661. package/dist/web-libavoid.js.map +1 -0
  662. package/dist/web-login.js.map +1 -1
  663. package/dist/web-mentions.js.map +1 -1
  664. package/dist/web-mod-agent.js.map +1 -1
  665. package/dist/web-mod-store.d.ts +2 -0
  666. package/dist/web-mod-store.js +10 -0
  667. package/dist/web-mod-store.js.map +1 -1
  668. package/dist/web-office-store.d.ts +104 -0
  669. package/dist/web-office-store.js +236 -0
  670. package/dist/web-office-store.js.map +1 -0
  671. package/dist/web-owner-css-store.d.ts +44 -1
  672. package/dist/web-owner-css-store.js +70 -9
  673. package/dist/web-owner-css-store.js.map +1 -1
  674. package/dist/web-owner-css.js.map +1 -1
  675. package/dist/web-png.d.ts +33 -0
  676. package/dist/web-png.js +329 -0
  677. package/dist/web-png.js.map +1 -0
  678. package/dist/web-prefs.d.ts +6 -0
  679. package/dist/web-prefs.js +29 -3
  680. package/dist/web-prefs.js.map +1 -1
  681. package/dist/web-preview.d.ts +2 -0
  682. package/dist/web-preview.js +2 -0
  683. package/dist/web-preview.js.map +1 -1
  684. package/dist/web-profile.js.map +1 -1
  685. package/dist/web-reactions.d.ts +35 -0
  686. package/dist/web-reactions.js +47 -0
  687. package/dist/web-reactions.js.map +1 -0
  688. package/dist/web-request-context.d.ts +5 -0
  689. package/dist/web-request-context.js.map +1 -1
  690. package/dist/web-returning-device.d.ts +4 -2
  691. package/dist/web-returning-device.js +13 -6
  692. package/dist/web-returning-device.js.map +1 -1
  693. package/dist/web-schedules.js.map +1 -1
  694. package/dist/web-send-dedupe.d.ts +36 -0
  695. package/dist/web-send-dedupe.js +69 -0
  696. package/dist/web-send-dedupe.js.map +1 -0
  697. package/dist/web-session-authorization.js.map +1 -1
  698. package/dist/web-session.js +1 -1
  699. package/dist/web-session.js.map +1 -1
  700. package/dist/web-settings-patch.d.ts +3 -1
  701. package/dist/web-settings-patch.js +11 -5
  702. package/dist/web-settings-patch.js.map +1 -1
  703. package/dist/web-settings-tools.d.ts +6 -0
  704. package/dist/web-settings-tools.js +52 -17
  705. package/dist/web-settings-tools.js.map +1 -1
  706. package/dist/web-terminal-http.js +15 -2
  707. package/dist/web-terminal-http.js.map +1 -1
  708. package/dist/web-terminal.js +1 -1
  709. package/dist/web-terminal.js.map +1 -1
  710. package/dist/web-theme-store.d.ts +2 -2
  711. package/dist/web-theme-store.js +6 -3
  712. package/dist/web-theme-store.js.map +1 -1
  713. package/dist/web-theme.d.ts +29 -0
  714. package/dist/web-theme.js +89 -1
  715. package/dist/web-theme.js.map +1 -1
  716. package/dist/web-upload.d.ts +18 -1
  717. package/dist/web-upload.js +45 -7
  718. package/dist/web-upload.js.map +1 -1
  719. package/dist/web-widget.js.map +1 -1
  720. package/dist/webhook-emitter.js.map +1 -1
  721. package/dist/windows-shortcuts.d.ts +78 -0
  722. package/dist/windows-shortcuts.js +203 -0
  723. package/dist/windows-shortcuts.js.map +1 -0
  724. package/dist/workflow-templates/default.md +4 -1
  725. package/dist/ws-server.js.map +1 -1
  726. package/dist/yaml-shared-nodes.d.ts +25 -0
  727. package/dist/yaml-shared-nodes.js +185 -0
  728. package/dist/yaml-shared-nodes.js.map +1 -0
  729. package/launcher/agend-install.cjs +828 -0
  730. package/launcher/install-admission.cjs +4 -39
  731. package/launcher/postinstall.cjs +14 -12
  732. package/launcher/private-home.cjs +43 -0
  733. package/package.json +16 -13
  734. package/scripts/preinstall-guard.cjs +2 -5
  735. package/templates/windows-shortcuts.ps1 +181 -0
  736. package/launcher/old-updater-note.cjs +0 -48
@@ -3,6 +3,20 @@ import { register } from "/assets/app-i18n.js";
3
3
 
4
4
  register("chat", {
5
5
  en: {
6
+ waking: "Waking {0}…", retrySend: "Retry send",
7
+ // A message being sent (its own bubble at once) and its files' uploads.
8
+ you: "You", sending: "Sending…", uploadingFile: "Uploading {0}", uploadWaiting: "waiting", uploadFailed: "failed",
9
+ uploadNoConnection: "the upload was cut off — check the connection", sendNoConnection: "it did not reach AgEnD — check the connection", notSentBecause: "Not sent: {0}", deleteUnsent: "Delete",
10
+ sendFailure_wake_capacity: "No slot is free to wake this instance. Retry when another instance is idle.",
11
+ sendFailure_wake_credentials: "The instance could not sign in. Check its account on the host, then retry.",
12
+ sendFailure_wake_timeout: "The instance did not become ready in time. Retry, or check its status on the host.",
13
+ sendFailure_instance_stopped: "This instance was stopped or removed. Start it on the host before retrying.",
14
+ sendFailure_delivery_unavailable: "The instance could not receive this message. Check its status on the host, then retry.",
15
+ sendFailure_delivery_changed: "The session or instance changed before this message was sent. Sign in again if needed, then retry.",
16
+ sendFailure_reply_gone: "The message you replied to is no longer in this chat. Your text is still here: send it without the reply, or reply to another message.",
17
+ reply: "Reply", replyingTo: "Replying to {0}", replyCancel: "Cancel the reply (Esc)", replyHidden: "an earlier message", replyPhoto: "[Photo]", replyFile: "[File]",
18
+ replyGone: "That message is no longer in this chat.", replyJump: "Show the message this replies to", copyText: "Copy text", copyLink: "Copy link", msgMenu: "Message",
19
+
6
20
  effectTrialReason_checking: "Checking this session…",
7
21
  effectTrialReason_localAdmin: "Sign in as an admin outside the public link to Try.",
8
22
  effectTrialReason_chatChanged: "Open the chat that offered this trial.",
@@ -21,13 +35,13 @@ register("chat", {
21
35
  effectTrialSettings: "Open playback settings",
22
36
  effectTrialPlayMotion: "Play effects anyway",
23
37
  effectTrial: "Effect trial", effectTrialLocal: "A sample on this page. Nothing is applied or saved.",
24
- effectTrialStart: "Try", effectTrialExpired: "This trial offer has expired.", effectTrialUnavailable: "This trial is no longer available.",
38
+ effectTrialStopped: "Try stopped", effectTrialStart: "Try", effectTrialExpired: "This trial offer has expired.", effectTrialUnavailable: "This trial is no longer available.",
25
39
  cmdConfirm: "Clear", cmdConfirmBackend: "Switch and restart", cmdRunning: "Running /{0}…", cmdNotCommand: "Not a command here — Enter sends it as a message.", cmdDismiss: "Dismiss", cmdCommands: "Commands",
26
40
  compactRunning: "Compacting…", compactDone: "✅ Conversation compacted", compactUnknown: "This backend does not report when compaction ends.",
27
41
  compactTimeout: "No sign that compaction finished within {0} minutes — check the agent's own chat.",
28
42
  compactLost: "Can't follow this compaction: the command did not reach the agent, or the agent stopped or changed backend.",
29
43
  compactNotCompacted: "kiro did not compact the conversation: it was too short, or the compaction was stopped.",
30
- cmdChoose: "Choose", cmdCurrent: "current", chipCompact: "Compact", chipClear: "Clear…", chipContext: "Context {0}% used",
44
+ cmdChoose: "Choose", cmdCurrent: "current", cmdSignIn: "Sign in", cmdSignInTo: "Sign in to {0}", chipCompact: "Compact", chipClear: "Clear…", chipContext: "Context {0}% used",
31
45
  chipModel: "Model: {0} — choose another", chipEffort: "Effort: {0} — choose another",
32
46
  cmd_ctx: "Context use, model and effort", cmd_compact: "Compact the conversation", cmd_clear: "Clear the conversation (asks first)",
33
47
  cmd_model: "Show or switch the model", cmd_backend: "Switch the CLI backend, then model and effort (restarts)", cmd_effort: "Show or set the reasoning effort", cmd_cancel: "Stop the current reply",
@@ -42,7 +56,7 @@ register("chat", {
42
56
  a11yWorking: "{0} started working", a11yIdle: "{0} finished", a11yReplied: "{0} replied", conversationWith: "Conversation with {0}",
43
57
  stopping: "Stopping {0}…", awaiting: "{0} is waiting for your input", approxNote: "Read from the terminal, so approximate", elapsedTitle: "Time since this page saw it start (approximate)",
44
58
  dropHere: "Drop files to attach them", asText: "As text", pasteAttached: "A long paste ({0} characters) was attached as a text file", pasteKeptAsText: "No room for another attachment: the long paste stays as text",
45
- send: "Send", composerPlaceholder: "Message {0}…", composerHint: "Enter to send · Shift+Enter for a new line", composerMore: "/ for commands · Esc stops the reply", guestMore: "Esc stops the reply",
59
+ send: "Send", composerPlaceholder: "Message {0}…", composerHint: "Enter to send · Shift+Enter for a new line", composerHintTouch: "Return adds a line · tap Send to send", composerMore: "/ for commands · Esc stops the reply", guestMore: "Esc stops the reply",
46
60
  guestNote: "You're a guest: the agent sees your messages as a guest's (recognition only).", guestNoCommands: "Guests can't run commands — send it without the leading /.", attachTitle: "Attach files (any type — or paste / drop them)",
47
61
  pvPreview: "Preview", pvStop: "Stop", pvDownload: "Download", pvMenu: "Preview options", pvHtml: "HTML",
48
62
  pvAllowButton: "Allow previews", pvAllow: "Allow HTML previews — they may be able to send data out", pvDisallow: "Stop allowing HTML previews",
@@ -61,6 +75,8 @@ register("chat", {
61
75
  pvPage: "Page", pvCode: "Code", pvReload: "Reload", pvClose: "Close the panel", pvBack: "Back to the chat", pvResize: "Resize the preview panel",
62
76
  pvNewer: "A newer version is in the reply from {0}.", pvNewerShow: "Show it", pvPanelIdle: "Click Preview to run it here.",
63
77
  lbLabel: "Image: {0}", lbLabelNoName: "Image", lbClose: "Close", lbOpenOriginal: "Open original", lbPrev: "Previous image", lbNext: "Next image", lbCount: "{0} / {1}",
78
+ // #2333: zoom in the lightbox.
79
+ lbZoomIn: "Zoom in (+)", lbZoomOut: "Zoom out (−)", lbZoomLevel: "{0}% — click for fit or actual size (0 / 1)", lbFitWidth: "Fit width (W)",
64
80
  // #1554: preview.js's words (its English is the fallback there), and the server's reasons by code.
65
81
  pvWhyOptin: "HTML previews are off. Allow them from this card's ⋯ menu or Settings → Preferences.",
66
82
  pvWhyOptinPanel: "HTML previews are off.",
@@ -94,10 +110,10 @@ register("chat", {
94
110
  previews: "Allow HTML previews",
95
111
  clicked: "Clicked:", clickShow: "Show the message these buttons were on",
96
112
  copy: "Copy", copyMessage: "Copy this reply", copied: "Copied", copyFailed: "Copy failed", wrap: "Wrap", showAll: "Show all {0} lines", showMore: "Show more", showLess: "Show less", jumpLatest: "Latest", newBelow: "{0} new",
97
- disconnected: "disconnected", tick_queued: "Waiting behind another message", tick_processing: "Handed to the agent", tick_delivered: "The agent has it", tick_failed: "Not delivered", tick_cancelled: "Dropped by Stop",
113
+ disconnected: "disconnected", tick_queued: "Waiting behind another message", tick_processing: "Handed to the agent", tick_delivered: "The agent has it", tick_failed: "Not delivered", tick_cancelled: "Dropped by Stop", reactedBy: "{0} reacted",
98
114
  promptAnswering: "Answering…", promptGone: "This prompt is no longer open", promptFor: "{0}: {1}",
99
115
  working: "{0} is working…", stuck: "{0} looks stuck", stopReply: "Stop reply", stopReplyTitle: "Stop the current reply (like Esc) and drop the messages still waiting. The instance keeps running.", stopSent: "Stopped",
100
- file_empty: "empty file", file_too_large: "over {0} MB", file_too_large_public: "over {0} MB — the most the public link carries in one upload", file_too_many: "at most {0} files per message", file_too_much: "the files together would be over {0} MB", removeFile: "Remove", notSent: "Not sent:", putBack: "Put back", discard: "Discard",
116
+ file_empty: "empty file", file_too_large: "over {0} MB", file_too_large_public: "over {0} MB — the most the public link carries in one upload", file_too_many: "at most {0} files per message", file_too_much: "the files together would be over {0} MB", removeFile: "Remove", putBack: "Put back",
101
117
  noMessages: "Type a task below.",
102
118
  pick: "Choose an agent to start chatting.", noAgents: "Create an agent to start chatting.", newAgent: "New agent", notFound: "No instance called “{0}”", notFoundHint: "It may have been deleted or renamed.", backToList: "Choose an agent",
103
119
  details: "Instance details", start: "Start instance", stopInstance: "Stop instance", restart: "Restart instance", delete: "Delete instance…",
@@ -114,6 +130,7 @@ register("chat", {
114
130
  dNoActivity: "No activity in the last hour.", dWorking: "Working…",
115
131
  // #1629: a person's control over an agent's own web theme.
116
132
  themeLock: "Lock theme", themeUnlock: "Unlock theme", themeReset: "Reset theme…", themeResetGo: "Reset",
133
+ pageColours: "Page colours", pageAgentOn: "The agent's choice (on)", pageAgentOff: "The agent's choice (off)", pageWhole: "On the whole page", pageChatOnly: "Only its chat",
117
134
  themeResetConfirm: "Reset {0}'s theme to the default? Its colours, picture and working label go; a lock stays.",
118
135
  themeResetDone: "{0}'s theme was reset.", themeLocked: "{0}'s theme is locked: the agent can't change it.", themeUnlocked: "{0}'s theme is unlocked.",
119
136
  themeFailed: "The theme was not changed ({0}).",
@@ -128,6 +145,19 @@ register("chat", {
128
145
  configured: "(configured)", fleetDefault: "(fleet default)",
129
146
  },
130
147
  "zh-TW": {
148
+ waking: "正在喚醒 {0}…", retrySend: "重試傳送",
149
+ you: "你", sending: "傳送中…", uploadingFile: "正在上傳 {0}", uploadWaiting: "等待中", uploadFailed: "失敗",
150
+ uploadNoConnection: "上傳中斷了,請檢查網路連線", sendNoConnection: "沒有送到 AgEnD,請檢查網路連線", notSentBecause: "沒有送出:{0}", deleteUnsent: "刪除",
151
+ sendFailure_wake_capacity: "目前沒有空位可喚醒這個實例。請等其他實例閒置後重試。",
152
+ sendFailure_wake_credentials: "實例無法登入。請先在主機檢查它的帳號,再重試。",
153
+ sendFailure_wake_timeout: "實例未能及時就緒。請重試,或在主機檢查它的狀態。",
154
+ sendFailure_instance_stopped: "這個實例已停止或移除。請先在主機啟動它,再重試。",
155
+ sendFailure_delivery_unavailable: "實例目前無法接收這則訊息。請在主機檢查它的狀態,再重試。",
156
+ sendFailure_delivery_changed: "訊息送出前,登入或實例狀態已改變。必要時請重新登入,再重試。",
157
+ sendFailure_reply_gone: "你回覆的訊息已不在這個聊天裡。內容還在:可以不帶回覆直接送出,或改回覆其他訊息。",
158
+ reply: "回覆", replyingTo: "回覆 {0}", replyCancel: "取消回覆(Esc)", replyHidden: "之前的一則訊息", replyPhoto: "[圖片]", replyFile: "[檔案]",
159
+ replyGone: "那則訊息已不在這個聊天裡。", replyJump: "顯示被回覆的訊息", copyText: "複製文字", copyLink: "複製連結", msgMenu: "訊息",
160
+
131
161
  effectTrialReason_checking: "正在確認工作階段…",
132
162
  effectTrialReason_localAdmin: "請在公開連結以外的儀表板以管理員登入後試播。",
133
163
  effectTrialReason_chatChanged: "請開啟發出試播邀請的聊天。",
@@ -146,13 +176,13 @@ register("chat", {
146
176
  effectTrialSettings: "開啟播放設定",
147
177
  effectTrialPlayMotion: "仍然播放效果",
148
178
  effectTrial: "效果試播", effectTrialLocal: "僅在本頁試播,不會套用或儲存。",
149
- effectTrialStart: "試播", effectTrialExpired: "試播邀請已過期。", effectTrialUnavailable: "此試播已無法使用。",
179
+ effectTrialStopped: "試播已停止", effectTrialStart: "試播", effectTrialExpired: "試播邀請已過期。", effectTrialUnavailable: "此試播已無法使用。",
150
180
  cmdConfirm: "清空", cmdConfirmBackend: "切換並重新啟動", cmdRunning: "執行 /{0} 中…", cmdNotCommand: "這裡沒有這個指令——按 Enter 會當成一般訊息送出。", cmdDismiss: "關閉", cmdCommands: "指令",
151
181
  compactRunning: "壓縮中…", compactDone: "✅ 對話已壓縮", compactUnknown: "這個後端不會回報壓縮何時完成。",
152
182
  compactTimeout: "{0} 分鐘內沒有看到壓縮完成的紀錄,請到代理自己的對話確認。",
153
183
  compactLost: "無法再追蹤這次壓縮:指令沒有送到代理,或代理已停止、換了後端。",
154
184
  compactNotCompacted: "kiro 沒有壓縮對話:對話太短,或壓縮被中止了。",
155
- cmdChoose: "選擇", cmdCurrent: "目前", chipCompact: "壓縮", chipClear: "清空…", chipContext: "已用 {0}% 的 context",
185
+ cmdChoose: "選擇", cmdCurrent: "目前", cmdSignIn: "登入", cmdSignInTo: "登入 {0}", chipCompact: "壓縮", chipClear: "清空…", chipContext: "已用 {0}% 的 context",
156
186
  chipModel: "模型:{0}——選擇其他", chipEffort: "推理強度:{0}——選擇其他",
157
187
  cmd_ctx: "context 用量、模型與推理強度", cmd_compact: "壓縮對話", cmd_clear: "清空對話(會先確認)",
158
188
  cmd_model: "查看或切換模型", cmd_backend: "切換 CLI backend,再選模型和推理強度(會重新啟動)", cmd_effort: "查看或設定推理強度", cmd_cancel: "停止目前的回覆",
@@ -167,7 +197,7 @@ register("chat", {
167
197
  a11yWorking: "{0} 開始工作", a11yIdle: "{0} 已完成", a11yReplied: "{0} 已回覆", conversationWith: "與 {0} 的對話",
168
198
  stopping: "正在中止 {0}…", awaiting: "{0} 正在等你回應", approxNote: "從終端畫面判讀,僅供參考", elapsedTitle: "從這個頁面看到它開始工作起算(約略)",
169
199
  dropHere: "放開即可附加檔案", asText: "改為文字", pasteAttached: "貼上的長文字({0} 個字元)已改為文字檔附加", pasteKeptAsText: "附件已滿,貼上的長文字保留為一般文字",
170
- send: "送出", composerPlaceholder: "傳訊息給 {0}…", composerHint: "Enter 送出 · Shift+Enter 換行", composerMore: "/ 叫出指令 · Esc 中止回覆", guestMore: "Esc 中止回覆",
200
+ send: "送出", composerPlaceholder: "傳訊息給 {0}…", composerHint: "Enter 送出 · Shift+Enter 換行", composerHintTouch: "Return 換行 · 按「送出」傳送", composerMore: "/ 叫出指令 · Esc 中止回覆", guestMore: "Esc 中止回覆",
171
201
  guestNote: "你是訪客:agent 會把你的訊息視為訪客的訊息(僅供辨識)。", guestNoCommands: "訪客不能執行指令,請去掉開頭的 / 再送出。", attachTitle: "附加檔案(任何類型,也可以貼上或拖曳)",
172
202
  pvPreview: "預覽", pvStop: "停止", pvDownload: "下載", pvMenu: "預覽選項", pvHtml: "HTML",
173
203
  pvAllowButton: "允許預覽", pvAllow: "允許預覽 HTML(預覽可能把資料傳出去)", pvDisallow: "不再允許預覽 HTML",
@@ -186,6 +216,7 @@ register("chat", {
186
216
  pvPage: "頁面", pvCode: "程式碼", pvReload: "重新載入", pvClose: "關閉面板", pvBack: "回到對話", pvResize: "調整預覽面板寬度",
187
217
  pvNewer: "{0} 的回覆裡有較新的版本。", pvNewerShow: "顯示", pvPanelIdle: "按「預覽」在這裡執行。",
188
218
  lbLabel: "圖片:{0}", lbLabelNoName: "圖片", lbClose: "關閉", lbOpenOriginal: "開啟原圖", lbPrev: "上一張", lbNext: "下一張", lbCount: "{0} / {1}",
219
+ lbZoomIn: "放大(+)", lbZoomOut: "縮小(−)", lbZoomLevel: "{0}%:點一下切換「符合視窗」與「實際大小」(0 / 1)", lbFitWidth: "符合寬度(W)",
189
220
  pvWhyOptin: "還沒允許 HTML 預覽。可從這張卡片的 ⋯ 選單或「設定 → 偏好設定」允許。",
190
221
  pvWhyOptinPanel: "還沒允許 HTML 預覽。",
191
222
  pvWhyNever: "這台裝置在這個工作階段已關閉預覽。",
@@ -218,10 +249,10 @@ register("chat", {
218
249
  previews: "允許預覽 HTML",
219
250
  clicked: "點選了:", clickShow: "顯示這些按鈕所在的訊息",
220
251
  copy: "複製", copyMessage: "複製這則回覆", copied: "已複製", copyFailed: "複製失敗", wrap: "換行", showAll: "展開全部 {0} 行", showMore: "顯示更多", showLess: "收合", jumpLatest: "最新", newBelow: "{0} 則新訊息",
221
- disconnected: "已斷線", tick_queued: "排在前一則訊息之後", tick_processing: "已交給 agent", tick_delivered: "agent 已收到", tick_failed: "未送達", tick_cancelled: "已被中止取消",
252
+ disconnected: "已斷線", tick_queued: "排在前一則訊息之後", tick_processing: "已交給 agent", tick_delivered: "agent 已收到", tick_failed: "未送達", tick_cancelled: "已被中止取消", reactedBy: "{0} 的回應",
222
253
  promptAnswering: "回覆中…", promptGone: "這個提示已經結束", promptFor: "{0}:{1}",
223
254
  working: "{0} 處理中…", stuck: "{0} 似乎卡住了", stopReply: "中止回覆", stopReplyTitle: "中止目前的回覆(同 Esc),並取消還在排隊的訊息。instance 會繼續運行。", stopSent: "已中止",
224
- file_empty: "空檔案", file_too_large: "超過 {0} MB", file_too_large_public: "超過 {0} MB,這是公開連結單次上傳的上限", file_too_many: "每則訊息最多 {0} 個檔案", file_too_much: "檔案合計會超過 {0} MB", removeFile: "移除", notSent: "未送出:", putBack: "放回輸入框", discard: "捨棄",
255
+ file_empty: "空檔案", file_too_large: "超過 {0} MB", file_too_large_public: "超過 {0} MB,這是公開連結單次上傳的上限", file_too_many: "每則訊息最多 {0} 個檔案", file_too_much: "檔案合計會超過 {0} MB", removeFile: "移除", putBack: "放回輸入框",
225
256
  noMessages: "直接在下方輸入任務。",
226
257
  pick: "選一個 Agent 開始聊天。", noAgents: "先建立一個 Agent,再開始聊天。", newAgent: "新增 Agent", notFound: "沒有叫「{0}」的 instance", notFoundHint: "它可能已被刪除或改名。", backToList: "選擇 Agent",
227
258
  details: "Instance 詳細資料", start: "啟動 instance", stopInstance: "停止 instance", restart: "重新啟動 instance", delete: "刪除 instance…",
@@ -236,6 +267,7 @@ register("chat", {
236
267
  dEditInSettings: "到設定編輯", dPause: "暫停", dWake: "喚醒", dPaused: "{0} 已暫停", dWoken: "{0} 正在喚醒", dActionFailed: "沒有成功,請再試一次。",
237
268
  dNoActivity: "最近一小時沒有活動。", dWorking: "處理中…",
238
269
  themeLock: "鎖定主題", themeUnlock: "解除主題鎖定", themeReset: "重設主題…", themeResetGo: "重設",
270
+ pageColours: "頁面配色", pageAgentOn: "依 agent 的設定(開)", pageAgentOff: "依 agent 的設定(關)", pageWhole: "整個頁面", pageChatOnly: "只在它的聊天",
239
271
  themeResetConfirm: "要把 {0} 的主題重設為預設嗎?顏色、圖片和處理中文字都會移除;鎖定會保留。",
240
272
  themeResetDone: "已重設 {0} 的主題。", themeLocked: "已鎖定 {0} 的主題:agent 無法變更。", themeUnlocked: "已解除 {0} 的主題鎖定。",
241
273
  themeFailed: "主題沒有變更({0})。",
@@ -8,7 +8,7 @@
8
8
  // Unrelated updates (a new message, another message's tick) stop nothing (#1306 §6.2, #1408 §4).
9
9
  import "./chat-render.js";
10
10
  import "./preview.js";
11
- import { effectTrialMessage } from "./effect-trial-offer.js";
11
+ import { canvasWorkerEnabled, onCanvasWorkerChange, onCanvasMetadataChange, readCanvasTrialMessage } from "/assets/feature-flags.js";
12
12
  import { loadHtmlAttachment } from "./html-attachment.js";
13
13
  import { chatTime, chatTimeFull, onDayChange } from "./chat-time.js";
14
14
  import { textLinkSegments } from "/assets/app-route.js";
@@ -28,6 +28,7 @@ const P = () => globalThis.AgendPreview;
28
28
  /** #1672: a status line longer than this (lines, or characters) is folded until it is opened. */
29
29
  export const STATUS_FOLD_LINES = 6;
30
30
  export const STATUS_FOLD_CHARS = 600;
31
+ const REPLY = '<svg class="icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 14 4 9l5-5"/><path d="M4 9h10.5a5.5 5.5 0 0 1 0 11H11"/></svg>';
31
32
  const COPY = '<svg class="icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>';
32
33
 
33
34
  /** Escapes & " ' < >: safe for text and inside a quoted attribute. */
@@ -39,6 +40,41 @@ export const msgKey = (x) => `${x.boot}-${x.id}`;
39
40
  */
40
41
  export const THREAD_TAIL = 24;
41
42
  export const THREAD_CHUNK = 40;
43
+ /** #2158: a reader's scroll (smooth, momentum) is theirs until it has been this quiet since their last input or scroll. */
44
+ export const READER_QUIET_MS = 250;
45
+ const SCROLL_KEYS = new Set(["PageUp", "PageDown", "ArrowUp", "ArrowDown", "Home", "End", " "]);
46
+
47
+ /**
48
+ * #2053: a message's rendered Markdown, kept across renders and chat switches. A draw compares each message's html with
49
+ * its node's, so every render of a thread rendered every drawn message's Markdown again (~320 calls per 200-message
50
+ * switch). renderMarkdown's output depends only on the text, whether its HTML cards are on (an agent's message), and —
51
+ * for a text that has them — whether the page knows each server-emoji id and the name it has for each mention: the key
52
+ * holds exactly those, so a cached html is always what renderMarkdown would return now. Bounded: least recently used out.
53
+ */
54
+ export const MD_CACHE_MAX = 4000;
55
+ const mdCache = new Map();
56
+ const EMOJI_IDS = /<a?:[A-Za-z0-9_]{2,32}:(\d{15,25})>/g;
57
+ const MENTION_IDS = /<(@!?|@&|#)(\d{15,25})>/g;
58
+ const MENTION_KINDS = { "@": "u", "@!": "u", "@&": "r", "#": "c" };
59
+ export function renderedMarkdown(text, cards, emojis, mentions) {
60
+ const src = typeof text === "string" ? text : String(text == null ? "" : text);
61
+ let sig = cards ? "c" : "-";
62
+ if (src.includes("<")) {
63
+ // Read as renderMarkdown reads it: it drops NUL first (its placeholder mark), so "<@\0id>" is a mention there too.
64
+ const seen = src.includes("\u0000") ? src.replace(/\u0000/g, "") : src;
65
+ for (const m of seen.matchAll(EMOJI_IDS)) sig += `|e${m[1]}${emojis && emojis(m[1]) ? 1 : 0}`;
66
+ for (const m of seen.matchAll(MENTION_IDS)) { const name = mentions ? mentions(MENTION_KINDS[m[1]], m[2]) : null; sig += `|m${m[1]}${m[2]}=${typeof name === "string" ? name.length + ":" + name : "-"}`; }
67
+ }
68
+ const key = `${sig}\u0000${src}`;
69
+ let html = mdCache.get(key);
70
+ if (html === undefined) html = R().renderMarkdown(src, { htmlCards: cards, emojis, mentions });
71
+ else mdCache.delete(key);
72
+ mdCache.set(key, html);
73
+ while (mdCache.size > MD_CACHE_MAX) mdCache.delete(mdCache.keys().next().value);
74
+ return html;
75
+ }
76
+ /** Tests only: forget every rendered Markdown. */
77
+ export function clearRenderedMarkdown() { mdCache.clear(); }
42
78
 
43
79
  /**
44
80
  * Bind a renderer to `list` (the thread element) inside `scroller`. opts: { t, tf, isUser(x), onJump({show, unseen}),
@@ -47,6 +83,17 @@ export const THREAD_CHUNK = 40;
47
83
  */
48
84
  export function createThread(list, scroller, opts) {
49
85
  const nodes = new Map(); // key → { html, node }
86
+ // A server emoji's picture that does not load reads `:name:` (its alt) instead of an empty box (error does not bubble:
87
+ // caught on the way down).
88
+ list.addEventListener("error", (e) => {
89
+ const img = e.target;
90
+ if (!img || img.tagName !== "IMG" || !img.classList || !img.classList.contains("emoji")) return;
91
+ const span = list.ownerDocument.createElement("span");
92
+ span.className = "emoji-text";
93
+ span.textContent = img.getAttribute("alt") || "";
94
+ span.title = img.getAttribute("title") || span.textContent;
95
+ img.replaceWith(span);
96
+ }, true);
50
97
  let msgs = [];
51
98
  let unseen = 0;
52
99
  // The reader is at the newest message. While pinned, content that grows after it was drawn — an image loading, a
@@ -54,12 +101,15 @@ export function createThread(list, scroller, opts) {
54
101
  // Only a move UP unpins: the browser's own scroll anchoring also fires scroll events when content grows (the view
55
102
  // moves down or stays), and those must not count as the reader leaving the bottom. Reaching the bottom pins again.
56
103
  let pinned = true;
57
- let lastTop = 0;
104
+ // Where the view was (and how tall it was) at the last scroll the page saw or made: a move up from there unpins.
105
+ let lastTop = 0, lastView = 0;
106
+ const mark = () => { lastTop = scroller.scrollTop; lastView = scroller.clientHeight; };
58
107
  // The oldest message drawn (by key): everything after it is drawn, so new messages never push the window. Null: none
59
108
  // drawn yet — the next render draws the newest THREAD_TAIL and the rest follows (grow).
60
109
  let firstKey = null;
61
- let growTimer = null, growFrame = null, disposed = false;
110
+ let growTimer = null, growFrame = null, disposed = false, parked = false;
62
111
  const cardRefresh = new WeakMap(); // card node → re-read availability (weak: a card that left takes its source with it)
112
+ const cardDrop = new WeakMap(); // card node → drop the HTML read it is waiting on (the chat left: park)
63
113
  const tr = opts.t, trf = opts.tf;
64
114
  const stopIn = (node) => (P() ? P().stopIn(node) : []);
65
115
  /** #1770: write a message node's time label for now; unchanged text is not touched. */
@@ -82,8 +132,9 @@ export function createThread(list, scroller, opts) {
82
132
  // #1770: the label ("17:38", "Yesterday 17:38", "10/11 17:38") depends on today, so it is not part of the html a node
83
133
  // is kept by: it is written into the node (timeLabels) — crossing midnight relabels in place and reloads no preview.
84
134
  const time = x.ts ? `<span class="time" data-ts="${escAttr(x.ts)}" title="${escAttr(chatTimeFull(x.ts))}"></span>` : `<span class="time"></span>`;
85
- const trial = effectTrialMessage(x);
86
- if (trial) return trialHtml(trial, time, x);
135
+ if (x.outgoing) return outgoingHtml(x);
136
+ const trial = readCanvasTrialMessage(x);
137
+ if (trial) return canvasWorkerEnabled() ? trialHtml(trial, time, x) : "";
87
138
  if (x.role === "status") return statusHtml(x, time);
88
139
  const u = opts.isUser(x);
89
140
  // renderMarkdown escapes the whole text before it adds any tag of its own (chat-render.js).
@@ -92,7 +143,64 @@ export function createThread(list, scroller, opts) {
92
143
  const buttons = x.role === "agent" ? replyButtonsHtml(x.buttons) : "";
93
144
  // A person's click on a reply button shows as the button's label, never the line the agent was given (its value).
94
145
  const clicked = x.role === "user" && x.click && typeof x.click.label === "string";
95
- return `<div class="msg ${u ? "user" : "agent"}"><div class="meta"><span class="sender">${escAttr(x.sender)}</span>${time}${ticks}</div><div class="body">${clicked ? clickHtml(x.click) : `<div class="md">${R().renderMarkdown(x.text, { htmlCards: x.role === "agent", emojis: opts.emojis, mentions: opts.mentions })}</div>`}${R().attachmentsHtml(x.attachments, { htmlCards: x.role === "agent", goneTitle: tr("chat.attGone"), goneLabel: tr("chat.attUnavailable"), inlinePreviews: !PUBLIC_LINK() })}${buttons}</div>${tools}</div>`;
146
+ // #2272: reply to it — a hover strip at its top right (phones: swipe left or long-press; R on a focused message).
147
+ const key = msgKey(x);
148
+ const act = opts.canReply?.() === false ? "" : `<div data-trust="thread-control" class="msg-actions"><button type="button" class="chip-btn icon-only" data-act="reply" data-arg="${escAttr(key)}" title="${escAttr(tr("chat.reply"))}" aria-label="${escAttr(tr("chat.reply"))}">${REPLY}</button></div>`;
149
+ return `<div class="msg ${u ? "user" : "agent"}" data-key="${escAttr(key)}" tabindex="-1">${act}<div class="meta"><span class="sender">${escAttr(x.sender)}</span>${time}${ticks}</div>${quoteHtml(x.replyTo)}<div class="body">${clicked ? clickHtml(x.click) : `<div class="md">${renderedMarkdown(x.text, x.role === "agent", opts.emojis, opts.mentions)}</div>`}${R().attachmentsHtml(x.attachments, { htmlCards: x.role === "agent", goneTitle: tr("chat.attGone"), goneLabel: tr("chat.attUnavailable"), inlinePreviews: !PUBLIC_LINK() })}${buttons}</div>${R().reactionsHtml(x.reactions, tr("chat.reactedBy", x.instance || ""))}${tools}</div>`;
150
+ }
151
+ /**
152
+ * A message this page is sending (chat-store.js): its own bubble at once, dimmed with "Sending…" until the server
153
+ * takes it, then as sent until the server's copy replaces it; failed: marked, with Retry and Delete. Each file shows
154
+ * its upload — a picture as itself (a local preview), anything else by name — with its own bar, moved in place by
155
+ * progress() (the html does not hold the numbers, so a moving bar never redraws the bubble). No data-key: it is no
156
+ * message yet (nothing replies to it, links to it or copies it).
157
+ */
158
+ function outgoingHtml(x) {
159
+ const st = x.outgoing.state, going = st === "queued" || st === "sending";
160
+ const files = (x.files || []).map((f, i) => {
161
+ const pic = f.url ? `<img class="out-pic" src="${escAttr(f.url)}" alt="${escAttr(f.name)}">` : `<span class="out-doc" aria-hidden="true"></span>`;
162
+ return `<div class="out-file" data-i="${escAttr(i)}">${pic}<div class="out-file-info"><span class="out-name">${escAttr(f.name)}</span>`
163
+ + `<span class="out-bar" role="progressbar" aria-label="${escAttr(trf("chat.uploadingFile", f.name))}" aria-valuemin="0" aria-valuemax="100"><span class="out-fill"></span></span>`
164
+ + `<span class="out-pct"></span></div></div>`;
165
+ }).join("");
166
+ const failed = st === "failed" ? `<div data-trust="thread-control" class="out-note lbar" role="alert"><span class="out-error">${escAttr(trf("chat.notSentBecause", x.outgoing.error || ""))}</span>`
167
+ + `<span class="out-actions"><button type="button" class="btn btn-sm btn-primary" data-act="retryOut" data-arg="${escAttr(x.clientId)}">${escAttr(tr("chat.retrySend"))}</button>`
168
+ + `<button type="button" class="btn btn-sm" data-act="putBackOut" data-arg="${escAttr(x.clientId)}">${escAttr(tr("chat.putBack"))}</button>`
169
+ + `<button type="button" class="btn btn-sm" data-act="deleteOut" data-arg="${escAttr(x.clientId)}">${escAttr(tr("chat.deleteUnsent"))}</button></span></div>` : "";
170
+ const state = going ? `<span class="out-state" role="status"><span class="out-spin" aria-hidden="true"></span>${escAttr(tr("chat.sending"))}</span>` : "";
171
+ return `<div class="msg user out out-${st}" data-out="${escAttr(x.clientId)}"${going ? ' aria-busy="true"' : ""} tabindex="-1"><div class="meta"><span class="sender">${escAttr(x.sender)}</span>${state}</div>`
172
+ + `${quoteHtml(x.replyTo)}<div class="body">${x.text ? `<div class="md">${renderedMarkdown(x.text, false, opts.emojis, opts.mentions)}</div>` : ""}`
173
+ + `${files ? `<div class="out-files">${files}</div>` : ""}</div>${failed}</div>`;
174
+ }
175
+ /** The pending bubbles' upload bars and percentages, as their files stand now (in place: CSSOM, no redraw). */
176
+ function progress() {
177
+ for (const v of nodes.values()) {
178
+ const x = v.x;
179
+ if (!x || !x.outgoing || !x.files || !x.files.length) continue;
180
+ for (const el of v.node.querySelectorAll(".out-file[data-i]")) {
181
+ const f = x.files[Number(el.dataset.i)];
182
+ if (!f) continue;
183
+ const pct = f.state === "done" ? 100 : f.total > 0 ? Math.min(100, Math.floor((f.loaded / f.total) * 100)) : 0;
184
+ el.dataset.state = f.state;
185
+ const bar = el.querySelector(".out-bar"), fill = el.querySelector(".out-fill"), label = el.querySelector(".out-pct");
186
+ if (bar) bar.setAttribute("aria-valuenow", String(pct));
187
+ if (fill) fill.style.transform = `scaleX(${pct / 100})`;
188
+ if (label) { const text = f.state === "failed" ? tr("chat.uploadFailed") : f.state === "waiting" ? tr("chat.uploadWaiting") : `${pct}%`; if (label.textContent !== text) label.textContent = text; }
189
+ }
190
+ }
191
+ }
192
+
193
+ /**
194
+ * #2272: what a reply quotes, above its body — the quoted sender and first words (or that it had files), as the server
195
+ * read them when it was sent; a button that shows that message. A guest that may not see it gets only "an earlier
196
+ * message". Text only: everything is escaped.
197
+ */
198
+ function quoteHtml(q) {
199
+ if (!q || typeof q !== "object" || typeof q.boot !== "string" || typeof q.id !== "number") return "";
200
+ const words = q.hidden ? tr("chat.replyHidden")
201
+ : (typeof q.text === "string" && q.text) || (q.media === "photo" ? tr("chat.replyPhoto") : q.media === "document" ? tr("chat.replyFile") : "");
202
+ const who = !q.hidden && typeof q.sender === "string" && q.sender ? `<span class="quote-sender">${escAttr(q.sender)}</span> ` : "";
203
+ return `<button type="button" data-trust="thread-control" class="msg-quote" data-act="jumpQuote" data-arg="${escAttr(`${q.boot}-${q.id}`)}" title="${escAttr(tr("chat.replyJump"))}"><span class="quote-mark">${REPLY}</span> ${who}<span class="quote-text">${escAttr(words)}</span></button>`;
96
204
  }
97
205
  /**
98
206
  * A button click as a chip: 🔘 and the label (escaped: the label is the agent's text). With `of` (live, while its
@@ -113,14 +221,16 @@ export function createThread(list, scroller, opts) {
113
221
  }
114
222
  function trialHtml(offer, time, message) {
115
223
  const reason = trialNow() >= offer.expiresAt ? "expired" : PUBLIC_LINK() ? "localAdmin" : trialStatus(message);
224
+ const rawFailure = opts.effectTrialFailure?.(message), failure = typeof rawFailure === "string" ? rawFailure.replace(/[\u0000-\u001f\u007f]/g, "").slice(0, 80) : null;
116
225
  const available = reason === "ready", fix = !PUBLIC_LINK() && !available && reason !== "expired" && reason !== "checking" && typeof opts.fixEffectTrial === "function";
117
226
  const label = available ? "chat.effectTrialStart" : reason === "busy" ? "chat.effectTrialStopOther" : reason === "localAdmin" ? "chat.effectTrialSignIn" : reason === "reducedMotion" ? "chat.effectTrialPlayMotion" : "chat.effectTrialSettings";
118
227
  const button = available || fix ? `<button type="button" class="btn btn-sm effect-trial-start" data-act="effectTrial" data-arg="${escAttr(msgKey(message))}">${escAttr(tr(label))}</button>` : "";
119
- return `<div class="msg status-line"><div class="meta">${time}</div><div class="body"><div class="effect-trial-offer" data-trust="thread-control"><strong>${escAttr(tr("chat.effectTrial"))}: ${escAttr(offer.title)}</strong><div>${escAttr(tr(available ? "chat.effectTrialLocal" : "chat.effectTrialReason_" + reason))}</div>${button}</div></div></div>`;
228
+ return `<div class="msg status-line"><div class="meta">${time}</div><div class="body"><div class="effect-trial-offer" data-trust="thread-control"><strong>${escAttr(tr("chat.effectTrial"))}: ${escAttr(offer.title)}</strong><div>${escAttr(tr(available ? "chat.effectTrialLocal" : "chat.effectTrialReason_" + reason))}</div>${failure ? `<div class="effect-trial-failure" role="status">${escAttr(tr("chat.effectTrialStopped"))}: ${escAttr(failure)}</div>` : ""}${button}</div></div></div>`;
120
229
  }
121
230
  function armTrials() {
122
231
  if (trialTimer !== null) clearTimeout(trialTimer); trialTimer = null;
123
- const now = trialNow(), next = msgs.map(x => effectTrialMessage(x)).filter(x => x && x.expiresAt > now).reduce((n, x) => Math.min(n, x.expiresAt), Infinity);
232
+ if (!canvasWorkerEnabled()) return;
233
+ const now = trialNow(), next = msgs.map(x => readCanvasTrialMessage(x)).filter(x => x && x.expiresAt > now).reduce((n, x) => Math.min(n, x.expiresAt), Infinity);
124
234
  if (Number.isFinite(next)) trialTimer = setTimeout(() => { trialTimer = null; render(msgs); }, Math.max(1, Math.min(2147483647, next - now)));
125
235
  }
126
236
  /**
@@ -276,7 +386,7 @@ export function createThread(list, scroller, opts) {
276
386
  const note = el("div", "pv-note"); note.setAttribute("data-trust", "thread-control");
277
387
  const body = el("div", "tc-body"); body.hidden = true;
278
388
  ph.append(head, note, body);
279
- let text = null, reading = false, all = false;
389
+ let text = null, reading = 0, reads = 0, all = false;
280
390
  const draw = () => {
281
391
  body.textContent = "";
282
392
  const lines = text.split(/\r?\n/);
@@ -312,12 +422,15 @@ export function createThread(list, scroller, opts) {
312
422
  body.append(more);
313
423
  }
314
424
  };
425
+ // #2053: as an HTML card's read (cardDrop), one still waiting when the chat left is dropped, the card not left Loading.
426
+ cardDrop.set(ph, () => { if (reading) { reading = 0; note.textContent = ""; } });
315
427
  show.onclick = async () => {
316
428
  if (reading) return;
317
429
  if (text !== null) { body.hidden = !body.hidden; show.textContent = tr(body.hidden ? "chat.tcShow" : "chat.tcHide"); return; }
318
- reading = true; note.textContent = tr("chat.pvLoading");
430
+ const mine = reading = ++reads; note.textContent = tr("chat.pvLoading");
319
431
  const r = await loadHtmlAttachment({ id: att.id, name: att.name, size: att.size });
320
- reading = false;
432
+ if (reading !== mine) return; // dropped (its chat left meanwhile): a new click reads again
433
+ reading = 0;
321
434
  if (!ph.isConnected) return; // the card left the page while it was read: nothing to draw
322
435
  if (!r.ok) { note.textContent = r.reason === "over" ? tr("chat.tcOver") : attReason(r.reason); return; }
323
436
  note.textContent = "";
@@ -455,6 +568,10 @@ export function createThread(list, scroller, opts) {
455
568
  optB.onclick = () => { menu.open = false; opts.setPreviewOptIn(!P().optedIn()); };
456
569
  neverB.onclick = () => { menu.open = false; P().setNever(!P().never()); refreshCards(); };
457
570
  cardRefresh.set(ph, refresh);
571
+ // #2053: a kept card is shown again later. A read still waiting when its chat left is dropped then: its answer —
572
+ // whenever it comes — starts nothing (no preview, no panel without a new click), and the card is not left Loading
573
+ // (shown again, it is refreshed: refreshCards).
574
+ cardDrop.set(ph, () => { reading = 0; });
458
575
  refresh(stoppedKeys.includes(key) ? tr("chat.pvChanged") : null);
459
576
  }
460
577
  /** Re-read this device's preview choice on every card in this thread. */
@@ -469,12 +586,45 @@ export function createThread(list, scroller, opts) {
469
586
  // newest bubble clipped behind it, and the next growth past 48 px put the pill over it.
470
587
  const atBottom = () => R().isNearBottom(scroller.scrollTop, scroller.clientHeight, scroller.scrollHeight, 1);
471
588
  function reportJump() { opts.onJump({ show: !nearBottom(), unseen }); }
472
- function toBottom() { scroller.scrollTop = scroller.scrollHeight; pinned = true; lastTop = scroller.scrollTop; }
589
+ function toBottom() { snap(); scroller.scrollTop = scroller.scrollHeight; pinned = true; mark(); }
590
+ /**
591
+ * #2184: a thread's height is rarely a whole number of pixels, and a pinned view's offset is: so each time older messages
592
+ * were drawn above, the whole view moved by the change in that fraction (0.6–0.7 px — the faint jitter of a chat
593
+ * opened cold). A sub-pixel margin above the list keeps the thread's height whole, so the bottom never moves.
594
+ */
595
+ // #2188: one read (the list's box excludes its own margin, so nothing is reset first) and a write only on a change —
596
+ // a reset-then-measure laid a 200-message thread out twice on every pinned draw (2.3 s at 4× CPU on a kept chat).
597
+ let snapped = null;
598
+ function snap() {
599
+ const h = list.getBoundingClientRect().height, f = h - Math.floor(h);
600
+ const want = f > 0.001 && f < 0.999 ? (1 - f) + "px" : "0px";
601
+ if (want !== snapped) { snapped = want; list.style.setProperty("--thread-snap", want); }
602
+ }
473
603
  // Sizes change without a scroll event (layout after the render, the dock under the thread): while pinned, the view
474
604
  // stays flush with the bottom; a reader who scrolled up is never moved.
475
605
  const resized = () => { if (pinned && !atBottom()) toBottom(); reportJump(); };
476
- const ro = typeof ResizeObserver === "function" ? new ResizeObserver(resized) : null;
606
+ let ro = typeof ResizeObserver === "function" ? new ResizeObserver(resized) : null;
477
607
  if (ro) { ro.observe(list); ro.observe(scroller); }
608
+ // #2158: the reader's own hand on the thread — a wheel, a touch, the scrollbar (a pointer held), a scrolling key
609
+ // outside a text field — and the scroll that follows it until it has been quiet READER_QUIET_MS.
610
+ let inputAt = -Infinity, held = false;
611
+ const touched = () => { inputAt = performance.now(); };
612
+ const hold = (on) => () => { held = on; touched(); };
613
+ const onKey = (ev) => {
614
+ const el = ev.target;
615
+ if (SCROLL_KEYS.has(ev.key) && !(el && (el.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(el.tagName || "")))) touched();
616
+ };
617
+ const onScroller = [["wheel", touched], ["touchstart", hold(true)], ["touchend", hold(false)], ["touchcancel", hold(false)], ["pointerdown", hold(true)]];
618
+ const onDocument = [["pointerup", hold(false)], ["pointercancel", hold(false)], ["keydown", onKey]];
619
+ /** Listen for the reader's input on `sc` (and its document: a pointer let go anywhere, a key), or stop. */
620
+ function watchReader(sc, on) {
621
+ const doc = sc.ownerDocument, how = on ? "addEventListener" : "removeEventListener";
622
+ for (const [type, fn] of onScroller) sc[how](type, fn, { passive: true });
623
+ if (doc) for (const [type, fn] of onDocument) doc[how](type, fn, true);
624
+ if (!on) held = false;
625
+ }
626
+ const readerActive = () => held || performance.now() - inputAt < READER_QUIET_MS;
627
+ watchReader(scroller, true);
478
628
 
479
629
  /** Where drawing starts in `msgs`: at the oldest drawn message, or (none yet) the newest THREAD_TAIL. */
480
630
  function startIndex() {
@@ -488,14 +638,14 @@ export function createThread(list, scroller, opts) {
488
638
  /** Draw the next chunk of older messages, keeping the reader where they were; then the next, until all are drawn. */
489
639
  function grow() {
490
640
  growTimer = growFrame = null;
491
- if (disposed) return; // the chat left: nothing more is drawn
641
+ if (disposed || parked) return; // the chat left: nothing more is drawn (a kept one fills in when shown again)
492
642
  const at = startIndex();
493
643
  if (at <= 0) return;
494
644
  firstKey = msgKey(msgs[Math.max(0, at - THREAD_CHUNK)]);
495
645
  const fromBottom = scroller.scrollHeight - scroller.scrollTop;
496
646
  draw({ older: true }); // drawn above the reader: never "new below" (render alone counts those)
497
647
  if (pinned) toBottom();
498
- else { scroller.scrollTop = scroller.scrollHeight - fromBottom; lastTop = scroller.scrollTop; }
648
+ else { scroller.scrollTop = scroller.scrollHeight - fromBottom; mark(); }
499
649
  scheduleGrow();
500
650
  }
501
651
  /** After the next frame has painted (the click's own frame stays light), draw a chunk more. */
@@ -536,14 +686,18 @@ export function createThread(list, scroller, opts) {
536
686
  if ("restore" in renderOpts) {
537
687
  unseen = 0;
538
688
  if (renderOpts.restore == null) toBottom();
539
- else { scroller.scrollTop = renderOpts.restore; pinned = nearBottom(); lastTop = scroller.scrollTop; }
689
+ else { scroller.scrollTop = renderOpts.restore; pinned = nearBottom(); mark(); }
540
690
  } else if (stick) toBottom();
541
691
  else unseen += added;
542
692
  reportJump();
543
693
  }
544
694
  /** Put the messages from startIndex() on in the list (nodes reused when unchanged). Returns how many are new. */
545
695
  function draw(drawOpts) {
546
- const shown = msgs.slice(startIndex());
696
+ const shown = msgs.slice(startIndex()).filter(message => {
697
+ // Volatile host offer records are hidden off and while their on-only parser is loading.
698
+ const hostOffer = message?.role === "status" && message.sender === "agend" && Object.hasOwn(message, "effectTrial") && message.effectTrial != null;
699
+ return !hostOffer || canvasWorkerEnabled() && readCanvasTrialMessage(message) !== undefined;
700
+ });
547
701
  const keep = new Set(shown.map(msgKey));
548
702
  for (const [k, v] of nodes) if (!keep.has(k)) { stopIn(v.node); v.node.remove(); nodes.delete(k); }
549
703
  let prev = null, added = 0;
@@ -566,17 +720,40 @@ export function createThread(list, scroller, opts) {
566
720
  }
567
721
  armDay();
568
722
  armPolls();
723
+ rove();
724
+ progress();
569
725
  return added;
570
726
  }
727
+ /**
728
+ * #2272 (review): one message in the thread is in the Tab order — the last one focused while it is still drawn, else
729
+ * the newest; ↑/↓ move between messages (chat-msg-actions.js). The rest are -1, so Tab passes the thread in one step.
730
+ */
731
+ let roving = null;
732
+ function rove(key) {
733
+ if (key !== undefined) roving = key;
734
+ const at = (roving && nodes.has(roving) ? nodes.get(roving).node : null) || [...list.querySelectorAll(".msg[data-key]")].at(-1) || null;
735
+ for (const el of list.querySelectorAll('.msg[data-key][tabindex="0"]')) if (el !== at) el.setAttribute("tabindex", "-1");
736
+ if (at) at.setAttribute("tabindex", "0");
737
+ }
571
738
 
572
739
  function onScroll() {
740
+ // #2158: the thread got shorter under a pinned view (text settling on a cold load, a card) and the browser clamped
741
+ // scrollTop to its new end — for the view as tall as it was then; the view got shorter too before WebKit sent that
742
+ // clamp's scroll event (it comes before the ResizeObserver). A view resting exactly at the bottom of the view as it
743
+ // was at the last mark, with no hand of the reader's on the thread, was put there by the browser: still pinned, back
744
+ // at the bottom. Geometry alone can't tell (a reader's move up by the dock's growth lands there too): any input of
745
+ // theirs, and the scroll after it, is theirs and unpins as before. The page's own scrolls (reveal) have no input but
746
+ // don't land there.
747
+ const reader = readerActive();
748
+ if (reader) touched(); // the reader's scroll goes on (smooth, momentum): still theirs while it does
749
+ else if (pinned && !atBottom() && Math.abs(scroller.scrollHeight - lastView - scroller.scrollTop) <= 1) toBottom();
573
750
  const bottom = nearBottom(), top = scroller.scrollTop;
574
751
  // #2087: pinned only at the exact bottom — a reader still wheeling through the last 48 px was pinned there, so
575
752
  // the next resize (a picture, a card, the dock) pulled the view the rest of the way down under them. Near the bottom
576
753
  // still counts for a new message (render follows it) and for the pill; nothing else moves a view the reader moves.
577
754
  if (atBottom()) pinned = true;
578
755
  else if (top < lastTop) pinned = false;
579
- lastTop = top;
756
+ mark();
580
757
  if (bottom) unseen = 0;
581
758
  reportJump();
582
759
  return bottom ? null : scroller.scrollTop;
@@ -636,24 +813,73 @@ export function createThread(list, scroller, opts) {
636
813
  if (m && opts.clickReplyButton) opts.clickReplyButton(m[1], Number(m[2]));
637
814
  } else if (act === "effectTrial") {
638
815
  // Only the current host-produced keyed card and a real local click can request admission. No token is read from DOM.
639
- const key = el.dataset.arg, row = msgs.find(x => msgKey(x) === key), owner = nodes.get(key), node = owner?.node, offer = effectTrialMessage(row);
816
+ const key = el.dataset.arg, row = msgs.find(x => msgKey(x) === key), owner = nodes.get(key), node = owner?.node, offer = readCanvasTrialMessage(row);
640
817
  if (PUBLIC_LINK() || e.defaultPrevented || e.isTrusted !== true || owner?.trialButton !== el || el.className !== "btn btn-sm effect-trial-start" || !node?.contains(el) || el.closest(".effect-trial-offer")?.getAttribute("data-trust") !== "thread-control"
641
818
  || !offer || trialNow() >= offer.expiresAt) return;
642
819
  if (trialStatus(row) === "ready" && opts.canTryEffect?.(row) === true) opts.startEffectTrial?.(row);
643
820
  else opts.fixEffectTrial?.(row, trialStatus(row));
644
821
  } else if (act === "showButtons") { const x = msgs.find(y => y.buttons && y.buttons.id === el.dataset.arg); if (x) reveal(msgKey(x)); }
822
+ else if (act === "reply") { const x = msgs.find(y => msgKey(y) === el.dataset.arg); if (x) opts.startReply?.(x); }
823
+ else if (act === "jumpQuote") void jumpTo(el.dataset.arg);
824
+ else if (act === "retryOut") opts.retryOutgoing?.(el.dataset.arg); // a failed send, again
825
+ else if (act === "putBackOut") opts.putBackOutgoing?.(el.dataset.arg); // back in the composer, to change it
826
+ else if (act === "deleteOut") opts.deleteOutgoing?.(el.dataset.arg);
645
827
  }
828
+ /**
829
+ * #2272: show the message a reply quotes, flashed — drawn now, or after the chat reads back the longest history the
830
+ * server keeps; otherwise it says it is no longer there.
831
+ */
832
+ async function jumpTo(key) {
833
+ if (typeof key !== "string" || !key) return;
834
+ if (reveal(key)) return;
835
+ const found = opts.findQuoted ? await opts.findQuoted(key) : false;
836
+ if (!found || !reveal(key)) opts.notice?.(tr("chat.replyGone"));
837
+ }
838
+ /** #2272: a drawn message's row (for the message menu and gestures), or undefined. */
839
+ function messageOf(key) { return msgs.find(y => msgKey(y) === key); }
646
840
 
647
841
  /** The thread goes (the chat left, or another instance opened): every preview in it stops first. */
648
- function dispose() {
649
- disposed = true;
842
+ /**
843
+ * #2053: kept for a later visit — the chat left as it always did (every preview stops: "leave"), but its drawn nodes
844
+ * stay. Nothing of it runs meanwhile: no timer (day labels, polls, effect trials, filling in), no size observer; the
845
+ * caller drops its subscriptions. Shown again: attach, then render — only what changed is drawn.
846
+ */
847
+ function park() {
848
+ parked = true;
849
+ watchReader(scroller, false);
850
+ if (P()) P().stopAll("leave");
851
+ for (const node of list.querySelectorAll(".html-card[data-att], .text-card[data-att]")) { const f = cardDrop.get(node); if (f) f(); }
852
+ if (ro) { ro.disconnect(); ro = null; }
853
+ if (trialTimer !== null) clearTimeout(trialTimer); trialTimer = null;
854
+ if (growFrame !== null && typeof cancelAnimationFrame === "function") cancelAnimationFrame(growFrame);
855
+ if (growTimer !== null) clearTimeout(growTimer);
856
+ growTimer = growFrame = null;
857
+ if (offDay) { offDay(); offDay = null; }
858
+ if (pollTick) { clearInterval(pollTick); pollTick = null; }
859
+ }
860
+ /** In view in `sc` (the view's scroller): sizes watched there from now on; the caller restores the position. */
861
+ function attach(sc) {
862
+ if (!parked) watchReader(scroller, false); // moved from the scroller it was made with
863
+ parked = false;
864
+ scroller = sc;
865
+ watchReader(sc, true);
866
+ if (ro) ro.disconnect();
867
+ ro = typeof ResizeObserver === "function" ? new ResizeObserver(resized) : null;
868
+ if (ro) { ro.observe(list); ro.observe(sc); }
869
+ }
870
+ /** `own`: only this thread's previews stop (a thread let go while another chat is in view); else every one, as leaving. */
871
+ const redrawFeature = () => { if (!disposed && !parked) render(msgs); };
872
+ const featureOff = onCanvasWorkerChange(redrawFeature), metadataOff = onCanvasMetadataChange(redrawFeature);
873
+ function dispose(own = false) {
874
+ disposed = true; featureOff(); metadataOff();
875
+ if (!parked) watchReader(scroller, false);
650
876
  if (trialTimer !== null) clearTimeout(trialTimer); trialTimer = null;
651
877
  if (ro) ro.disconnect();
652
- if (P()) P().stopAll("leave");
878
+ if (P()) { if (own) P().stopIn(list); else P().stopAll("leave"); }
653
879
  nodes.clear();
654
880
  armDay();
655
881
  armPolls();
656
882
  }
657
883
 
658
- return { render, onScroll, onClick, jumpLatest, reveal, refreshCards, dispose, nodes, get unseen() { return unseen; } };
884
+ return { render, progress, onScroll, onClick, jumpLatest, reveal, jumpTo, messageOf, rove, refreshCards, park, attach, dispose, nodes, get unseen() { return unseen; } };
659
885
  }