@vibefield/rosetta-room 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (435) hide show
  1. package/README.md +247 -2
  2. package/dist/HistoryRoom.d.ts +98 -0
  3. package/dist/HistoryRoom.js +440 -0
  4. package/dist/TranscriptReader.d.ts +22 -0
  5. package/dist/TranscriptReader.js +72 -0
  6. package/dist/components/ColdStart.d.ts +4 -0
  7. package/dist/components/ColdStart.js +5 -0
  8. package/dist/components/FilterChips.d.ts +4 -0
  9. package/dist/components/FilterChips.js +5 -0
  10. package/dist/components/HostAct.d.ts +9 -0
  11. package/dist/components/HostAct.js +16 -0
  12. package/dist/components/HostMenu.d.ts +41 -0
  13. package/dist/components/HostMenu.js +216 -0
  14. package/dist/components/IngestStatus.d.ts +4 -0
  15. package/dist/components/IngestStatus.js +20 -0
  16. package/dist/components/ListActivityNotice.d.ts +5 -0
  17. package/dist/components/ListActivityNotice.js +4 -0
  18. package/dist/components/ListSections.d.ts +20 -0
  19. package/dist/components/ListSections.js +39 -0
  20. package/dist/components/RenameHeading.d.ts +9 -0
  21. package/dist/components/RenameHeading.js +75 -0
  22. package/dist/components/SearchStrip.d.ts +5 -0
  23. package/dist/components/SearchStrip.js +8 -0
  24. package/dist/components/SummaryMetrics.d.ts +13 -0
  25. package/dist/components/SummaryMetrics.js +9 -0
  26. package/dist/components/SummaryPanels.d.ts +35 -0
  27. package/dist/components/SummaryPanels.js +117 -0
  28. package/dist/components/Transcript.d.ts +46 -0
  29. package/dist/components/Transcript.js +358 -0
  30. package/dist/components/charts/ActivityChartDetail.d.ts +13 -0
  31. package/dist/components/charts/ActivityChartDetail.js +78 -0
  32. package/dist/components/charts/CalendarHeatmap.d.ts +16 -0
  33. package/dist/components/charts/CalendarHeatmap.js +55 -0
  34. package/dist/components/charts/DayActivityChart.d.ts +8 -0
  35. package/dist/components/charts/DayActivityChart.js +71 -0
  36. package/dist/components/charts/ModelsChartDetail.d.ts +7 -0
  37. package/dist/components/charts/ModelsChartDetail.js +19 -0
  38. package/dist/components/charts/ProjectChartDialog.d.ts +19 -0
  39. package/dist/components/charts/ProjectChartDialog.js +39 -0
  40. package/dist/components/charts/Streamgraph.d.ts +16 -0
  41. package/dist/components/charts/Streamgraph.js +94 -0
  42. package/dist/components/charts/Treemap.d.ts +26 -0
  43. package/dist/components/charts/Treemap.js +81 -0
  44. package/dist/components/charts/index.d.ts +9 -0
  45. package/dist/components/charts/index.js +4 -0
  46. package/dist/components/charts/streamgraph-layout.d.ts +23 -0
  47. package/dist/components/charts/streamgraph-layout.js +114 -0
  48. package/dist/components/charts/treemap-layout.d.ts +32 -0
  49. package/dist/components/charts/treemap-layout.js +140 -0
  50. package/dist/components/charts/types.d.ts +65 -0
  51. package/dist/components/charts/types.js +8 -0
  52. package/dist/components/file-viewer/FileViewerBrowsers.d.ts +10 -0
  53. package/dist/components/file-viewer/FileViewerBrowsers.js +65 -0
  54. package/dist/components/file-viewer/FileViewerDialog.d.ts +33 -0
  55. package/dist/components/file-viewer/FileViewerDialog.js +82 -0
  56. package/dist/components/file-viewer/FileViewerHeader.d.ts +26 -0
  57. package/dist/components/file-viewer/FileViewerHeader.js +17 -0
  58. package/dist/components/file-viewer/ImageGalleryDialog.d.ts +14 -0
  59. package/dist/components/file-viewer/ImageGalleryDialog.js +98 -0
  60. package/dist/components/file-viewer/diff/index.d.ts +2 -0
  61. package/dist/components/file-viewer/diff/index.js +2 -0
  62. package/dist/components/file-viewer/diff/parse.d.ts +2 -0
  63. package/dist/components/file-viewer/diff/parse.js +164 -0
  64. package/dist/components/file-viewer/diff/types.d.ts +44 -0
  65. package/dist/components/file-viewer/diff/types.js +7 -0
  66. package/dist/components/file-viewer/gallery-source.d.ts +11 -0
  67. package/dist/components/file-viewer/gallery-source.js +75 -0
  68. package/dist/components/file-viewer/image-resource.d.ts +19 -0
  69. package/dist/components/file-viewer/image-resource.js +60 -0
  70. package/dist/components/file-viewer/index.d.ts +5 -0
  71. package/dist/components/file-viewer/index.js +8 -0
  72. package/dist/components/file-viewer/markdown/blocks.d.ts +2 -0
  73. package/dist/components/file-viewer/markdown/blocks.js +277 -0
  74. package/dist/components/file-viewer/markdown/index.d.ts +4 -0
  75. package/dist/components/file-viewer/markdown/index.js +35 -0
  76. package/dist/components/file-viewer/markdown/inline.d.ts +10 -0
  77. package/dist/components/file-viewer/markdown/inline.js +178 -0
  78. package/dist/components/file-viewer/markdown/render.d.ts +9 -0
  79. package/dist/components/file-viewer/markdown/render.js +78 -0
  80. package/dist/components/file-viewer/markdown/types.d.ts +57 -0
  81. package/dist/components/file-viewer/markdown/types.js +11 -0
  82. package/dist/components/file-viewer/minimap/CodeMinimap.d.ts +10 -0
  83. package/dist/components/file-viewer/minimap/CodeMinimap.js +296 -0
  84. package/dist/components/file-viewer/minimap/layout.d.ts +60 -0
  85. package/dist/components/file-viewer/minimap/layout.js +117 -0
  86. package/dist/components/file-viewer/minimap/palette.d.ts +2 -0
  87. package/dist/components/file-viewer/minimap/palette.js +73 -0
  88. package/dist/components/file-viewer/minimap/raster.d.ts +19 -0
  89. package/dist/components/file-viewer/minimap/raster.js +122 -0
  90. package/dist/components/file-viewer/registry.d.ts +25 -0
  91. package/dist/components/file-viewer/registry.js +182 -0
  92. package/dist/components/file-viewer/syntax/grammars.d.ts +16 -0
  93. package/dist/components/file-viewer/syntax/grammars.js +221 -0
  94. package/dist/components/file-viewer/syntax/index.d.ts +8 -0
  95. package/dist/components/file-viewer/syntax/index.js +9 -0
  96. package/dist/components/file-viewer/syntax/languages.d.ts +14 -0
  97. package/dist/components/file-viewer/syntax/languages.js +115 -0
  98. package/dist/components/file-viewer/syntax/tokenize-config.d.ts +2 -0
  99. package/dist/components/file-viewer/syntax/tokenize-config.js +132 -0
  100. package/dist/components/file-viewer/syntax/tokenize-css.d.ts +2 -0
  101. package/dist/components/file-viewer/syntax/tokenize-css.js +169 -0
  102. package/dist/components/file-viewer/syntax/tokenize-markup.d.ts +2 -0
  103. package/dist/components/file-viewer/syntax/tokenize-markup.js +124 -0
  104. package/dist/components/file-viewer/syntax/tokenize.d.ts +9 -0
  105. package/dist/components/file-viewer/syntax/tokenize.js +289 -0
  106. package/dist/components/file-viewer/syntax/types.d.ts +74 -0
  107. package/dist/components/file-viewer/syntax/types.js +13 -0
  108. package/dist/components/file-viewer/types.d.ts +111 -0
  109. package/dist/components/file-viewer/types.js +8 -0
  110. package/dist/components/file-viewer/use-file-content.d.ts +26 -0
  111. package/dist/components/file-viewer/use-file-content.js +144 -0
  112. package/dist/components/file-viewer/views/BinaryView.d.ts +2 -0
  113. package/dist/components/file-viewer/views/BinaryView.js +34 -0
  114. package/dist/components/file-viewer/views/CodeView.d.ts +2 -0
  115. package/dist/components/file-viewer/views/CodeView.js +46 -0
  116. package/dist/components/file-viewer/views/DiffBody.d.ts +7 -0
  117. package/dist/components/file-viewer/views/DiffBody.js +77 -0
  118. package/dist/components/file-viewer/views/DiffView.d.ts +2 -0
  119. package/dist/components/file-viewer/views/DiffView.js +11 -0
  120. package/dist/components/file-viewer/views/ImageView.d.ts +9 -0
  121. package/dist/components/file-viewer/views/ImageView.js +28 -0
  122. package/dist/components/file-viewer/views/MarkdownView.d.ts +9 -0
  123. package/dist/components/file-viewer/views/MarkdownView.js +20 -0
  124. package/dist/components/file-viewer/views/SvgView.d.ts +9 -0
  125. package/dist/components/file-viewer/views/SvgView.js +10 -0
  126. package/dist/components/git/ChangeTree.d.ts +20 -0
  127. package/dist/components/git/ChangeTree.js +35 -0
  128. package/dist/components/git/CommitViewer.d.ts +12 -0
  129. package/dist/components/git/CommitViewer.js +85 -0
  130. package/dist/components/git/GitChanges.d.ts +17 -0
  131. package/dist/components/git/GitChanges.js +70 -0
  132. package/dist/components/git/GitHistory.d.ts +9 -0
  133. package/dist/components/git/GitHistory.js +58 -0
  134. package/dist/components/git/co-author.d.ts +14 -0
  135. package/dist/components/git/co-author.js +55 -0
  136. package/dist/components/git/git-file-source.d.ts +18 -0
  137. package/dist/components/git/git-file-source.js +53 -0
  138. package/dist/components/git/git-host.d.ts +24 -0
  139. package/dist/components/git/git-host.js +130 -0
  140. package/dist/components/git/index.d.ts +10 -0
  141. package/dist/components/git/index.js +12 -0
  142. package/dist/components/git/path-tree.d.ts +23 -0
  143. package/dist/components/git/path-tree.js +73 -0
  144. package/dist/components/git/session-files.d.ts +9 -0
  145. package/dist/components/git/session-files.js +50 -0
  146. package/dist/components/git/types.d.ts +92 -0
  147. package/dist/components/git/types.js +9 -0
  148. package/dist/components/projects/ProjectActivitySparkline.d.ts +6 -0
  149. package/dist/components/projects/ProjectActivitySparkline.js +12 -0
  150. package/dist/components/projects/ProjectList.d.ts +25 -0
  151. package/dist/components/projects/ProjectList.js +43 -0
  152. package/dist/components/projects/ProjectListItem.d.ts +19 -0
  153. package/dist/components/projects/ProjectListItem.js +78 -0
  154. package/dist/components/projects/ProjectsColumn.d.ts +19 -0
  155. package/dist/components/projects/ProjectsColumn.js +36 -0
  156. package/dist/components/projects/index.d.ts +8 -0
  157. package/dist/components/projects/index.js +4 -0
  158. package/dist/components/search/MatchStepper.d.ts +9 -0
  159. package/dist/components/search/MatchStepper.js +14 -0
  160. package/dist/components/search/SearchLauncher.d.ts +12 -0
  161. package/dist/components/search/SearchLauncher.js +13 -0
  162. package/dist/components/search/SearchSheet.d.ts +29 -0
  163. package/dist/components/search/SearchSheet.js +230 -0
  164. package/dist/components/search/index.d.ts +8 -0
  165. package/dist/components/search/index.js +4 -0
  166. package/dist/components/search/shortcut.d.ts +25 -0
  167. package/dist/components/search/shortcut.js +32 -0
  168. package/dist/components/sessions/SessionList.d.ts +24 -0
  169. package/dist/components/sessions/SessionList.js +76 -0
  170. package/dist/components/sessions/SessionListItem.d.ts +13 -0
  171. package/dist/components/sessions/SessionListItem.js +58 -0
  172. package/dist/components/sessions/SessionsColumn.d.ts +26 -0
  173. package/dist/components/sessions/SessionsColumn.js +32 -0
  174. package/dist/components/sessions/index.d.ts +9 -0
  175. package/dist/components/sessions/index.js +4 -0
  176. package/dist/components/sessions/session-display.d.ts +8 -0
  177. package/dist/components/sessions/session-display.js +18 -0
  178. package/dist/components/sessions/types.d.ts +7 -0
  179. package/dist/components/sessions/types.js +1 -0
  180. package/dist/components/transcript/Activity.d.ts +45 -0
  181. package/dist/components/transcript/Activity.js +75 -0
  182. package/dist/components/transcript/Anchor.d.ts +8 -0
  183. package/dist/components/transcript/Anchor.js +23 -0
  184. package/dist/components/transcript/Attachments.d.ts +21 -0
  185. package/dist/components/transcript/Attachments.js +138 -0
  186. package/dist/components/transcript/AuthoredMessage.d.ts +19 -0
  187. package/dist/components/transcript/AuthoredMessage.js +17 -0
  188. package/dist/components/transcript/Compaction.d.ts +9 -0
  189. package/dist/components/transcript/Compaction.js +50 -0
  190. package/dist/components/transcript/DayPill.d.ts +38 -0
  191. package/dist/components/transcript/DayPill.js +68 -0
  192. package/dist/components/transcript/Document.d.ts +32 -0
  193. package/dist/components/transcript/Document.js +503 -0
  194. package/dist/components/transcript/Favicon.d.ts +32 -0
  195. package/dist/components/transcript/Favicon.js +71 -0
  196. package/dist/components/transcript/HeadOfDocument.d.ts +19 -0
  197. package/dist/components/transcript/HeadOfDocument.js +44 -0
  198. package/dist/components/transcript/Ledger.d.ts +53 -0
  199. package/dist/components/transcript/Ledger.js +182 -0
  200. package/dist/components/transcript/LineDetail.d.ts +24 -0
  201. package/dist/components/transcript/LineDetail.js +81 -0
  202. package/dist/components/transcript/Marker.d.ts +33 -0
  203. package/dist/components/transcript/Marker.js +21 -0
  204. package/dist/components/transcript/MessagePreview.d.ts +12 -0
  205. package/dist/components/transcript/MessagePreview.js +58 -0
  206. package/dist/components/transcript/Prompt.d.ts +20 -0
  207. package/dist/components/transcript/Prompt.js +55 -0
  208. package/dist/components/transcript/Prose.d.ts +19 -0
  209. package/dist/components/transcript/Prose.js +109 -0
  210. package/dist/components/transcript/ReceiptPill.d.ts +10 -0
  211. package/dist/components/transcript/ReceiptPill.js +10 -0
  212. package/dist/components/transcript/ResponseHead.d.ts +9 -0
  213. package/dist/components/transcript/ResponseHead.js +36 -0
  214. package/dist/components/transcript/Subthread.d.ts +19 -0
  215. package/dist/components/transcript/Subthread.js +96 -0
  216. package/dist/components/transcript/Team.d.ts +53 -0
  217. package/dist/components/transcript/Team.js +185 -0
  218. package/dist/components/transcript/Thinking.d.ts +24 -0
  219. package/dist/components/transcript/Thinking.js +50 -0
  220. package/dist/components/transcript/TimelineMeta.d.ts +9 -0
  221. package/dist/components/transcript/TimelineMeta.js +161 -0
  222. package/dist/components/transcript/TranscriptLoading.d.ts +10 -0
  223. package/dist/components/transcript/TranscriptLoading.js +10 -0
  224. package/dist/components/transcript/WebDetail.d.ts +8 -0
  225. package/dist/components/transcript/WebDetail.js +29 -0
  226. package/dist/components/transcript/Workflow.d.ts +12 -0
  227. package/dist/components/transcript/Workflow.js +208 -0
  228. package/dist/components/transcript/activity-plan.d.ts +84 -0
  229. package/dist/components/transcript/activity-plan.js +209 -0
  230. package/dist/components/transcript/attachment-file-source.d.ts +23 -0
  231. package/dist/components/transcript/attachment-file-source.js +103 -0
  232. package/dist/components/transcript/attachment-gallery.d.ts +12 -0
  233. package/dist/components/transcript/attachment-gallery.js +33 -0
  234. package/dist/components/transcript/detail-parts.d.ts +50 -0
  235. package/dist/components/transcript/detail-parts.js +295 -0
  236. package/dist/components/transcript/disclosure-state.d.ts +6 -0
  237. package/dist/components/transcript/disclosure-state.js +32 -0
  238. package/dist/components/transcript/disclosure-store.d.ts +8 -0
  239. package/dist/components/transcript/disclosure-store.js +31 -0
  240. package/dist/components/transcript/document-model.d.ts +15 -0
  241. package/dist/components/transcript/document-model.js +104 -0
  242. package/dist/components/transcript/document-plan.d.ts +28 -0
  243. package/dist/components/transcript/document-plan.js +145 -0
  244. package/dist/components/transcript/fold-preference.d.ts +17 -0
  245. package/dist/components/transcript/fold-preference.js +63 -0
  246. package/dist/components/transcript/glyphs.d.ts +15 -0
  247. package/dist/components/transcript/glyphs.js +47 -0
  248. package/dist/components/transcript/index.d.ts +23 -0
  249. package/dist/components/transcript/index.js +22 -0
  250. package/dist/components/transcript/message-file-source.d.ts +15 -0
  251. package/dist/components/transcript/message-file-source.js +31 -0
  252. package/dist/components/transcript/message-preview.d.ts +9 -0
  253. package/dist/components/transcript/message-preview.js +46 -0
  254. package/dist/components/transcript/read-file-source.d.ts +7 -0
  255. package/dist/components/transcript/read-file-source.js +34 -0
  256. package/dist/components/transcript/reader-context.d.ts +13 -0
  257. package/dist/components/transcript/reader-context.js +55 -0
  258. package/dist/components/transcript/reading-identity.d.ts +2 -0
  259. package/dist/components/transcript/reading-identity.js +52 -0
  260. package/dist/components/transcript/scroll-to-row.d.ts +2 -0
  261. package/dist/components/transcript/scroll-to-row.js +13 -0
  262. package/dist/components/transcript/thread-index.d.ts +14 -0
  263. package/dist/components/transcript/thread-index.js +40 -0
  264. package/dist/components/transcript/thread-navigation.d.ts +5 -0
  265. package/dist/components/transcript/thread-navigation.js +3 -0
  266. package/dist/components/transcript/timeline-meta-file-source.d.ts +6 -0
  267. package/dist/components/transcript/timeline-meta-file-source.js +55 -0
  268. package/dist/components/transcript/timeline-meta-state.d.ts +12 -0
  269. package/dist/components/transcript/timeline-meta-state.js +24 -0
  270. package/dist/components/ui/ColumnHeading.d.ts +27 -0
  271. package/dist/components/ui/ColumnHeading.js +7 -0
  272. package/dist/components/ui/ConfirmDialog.d.ts +14 -0
  273. package/dist/components/ui/ConfirmDialog.js +22 -0
  274. package/dist/components/ui/DialogShell.d.ts +18 -0
  275. package/dist/components/ui/DialogShell.js +40 -0
  276. package/dist/components/ui/EmptyState.d.ts +10 -0
  277. package/dist/components/ui/EmptyState.js +6 -0
  278. package/dist/components/ui/ListSummaryHeader.d.ts +9 -0
  279. package/dist/components/ui/ListSummaryHeader.js +6 -0
  280. package/dist/components/ui/ListToolbar.d.ts +6 -0
  281. package/dist/components/ui/ListToolbar.js +5 -0
  282. package/dist/components/ui/ResultGroupHeader.d.ts +9 -0
  283. package/dist/components/ui/ResultGroupHeader.js +5 -0
  284. package/dist/components/ui/SegmentedControl.d.ts +16 -0
  285. package/dist/components/ui/SegmentedControl.js +9 -0
  286. package/dist/components/ui/SummaryMetric.d.ts +13 -0
  287. package/dist/components/ui/SummaryMetric.js +10 -0
  288. package/dist/components/ui/Veil.d.ts +7 -0
  289. package/dist/components/ui/Veil.js +5 -0
  290. package/dist/components/ui/index.d.ts +20 -0
  291. package/dist/components/ui/index.js +10 -0
  292. package/dist/components/worktree/WorktreeColumn.d.ts +22 -0
  293. package/dist/components/worktree/WorktreeColumn.js +170 -0
  294. package/dist/components/worktree/WorktreeFileTree.d.ts +19 -0
  295. package/dist/components/worktree/WorktreeFileTree.js +60 -0
  296. package/dist/components/worktree/WorktreeHeader.d.ts +32 -0
  297. package/dist/components/worktree/WorktreeHeader.js +81 -0
  298. package/dist/components/worktree/WorktreeList.d.ts +8 -0
  299. package/dist/components/worktree/WorktreeList.js +12 -0
  300. package/dist/components/worktree/index.d.ts +12 -0
  301. package/dist/components/worktree/index.js +7 -0
  302. package/dist/components/worktree/types.d.ts +17 -0
  303. package/dist/components/worktree/types.js +3 -0
  304. package/dist/components/worktree/use-worktree-guest.d.ts +21 -0
  305. package/dist/components/worktree/use-worktree-guest.js +92 -0
  306. package/dist/components/worktree/worktree-file-commands.d.ts +19 -0
  307. package/dist/components/worktree/worktree-file-commands.js +112 -0
  308. package/dist/components/worktree/worktree-file-source.d.ts +15 -0
  309. package/dist/components/worktree/worktree-file-source.js +75 -0
  310. package/dist/components/worktree/worktree-host.d.ts +31 -0
  311. package/dist/components/worktree/worktree-host.js +242 -0
  312. package/dist/fixtures/index.d.ts +24 -0
  313. package/dist/fixtures/index.js +292 -0
  314. package/dist/host-inputs.d.ts +231 -0
  315. package/dist/host-inputs.js +35 -0
  316. package/dist/index.d.ts +18 -0
  317. package/dist/index.js +14 -0
  318. package/dist/lib/activity-details.d.ts +23 -0
  319. package/dist/lib/activity-details.js +78 -0
  320. package/dist/lib/activity-order.d.ts +32 -0
  321. package/dist/lib/activity-order.js +86 -0
  322. package/dist/lib/agent-paths.d.ts +6 -0
  323. package/dist/lib/agent-paths.js +19 -0
  324. package/dist/lib/agents.d.ts +15 -0
  325. package/dist/lib/agents.js +60 -0
  326. package/dist/lib/catalog-status.d.ts +3 -0
  327. package/dist/lib/catalog-status.js +10 -0
  328. package/dist/lib/chart-data.d.ts +14 -0
  329. package/dist/lib/chart-data.js +151 -0
  330. package/dist/lib/day-activity.d.ts +21 -0
  331. package/dist/lib/day-activity.js +56 -0
  332. package/dist/lib/format.d.ts +24 -0
  333. package/dist/lib/format.js +116 -0
  334. package/dist/lib/hooks.d.ts +118 -0
  335. package/dist/lib/hooks.js +608 -0
  336. package/dist/lib/icons.d.ts +21 -0
  337. package/dist/lib/icons.js +49 -0
  338. package/dist/lib/image-media.d.ts +6 -0
  339. package/dist/lib/image-media.js +30 -0
  340. package/dist/lib/ingest-activity.d.ts +50 -0
  341. package/dist/lib/ingest-activity.js +151 -0
  342. package/dist/lib/ingest.d.ts +25 -0
  343. package/dist/lib/ingest.js +70 -0
  344. package/dist/lib/model-labels.d.ts +5 -0
  345. package/dist/lib/model-labels.js +28 -0
  346. package/dist/lib/model.d.ts +73 -0
  347. package/dist/lib/model.js +141 -0
  348. package/dist/lib/motion.d.ts +13 -0
  349. package/dist/lib/motion.js +80 -0
  350. package/dist/lib/room-storage.d.ts +13 -0
  351. package/dist/lib/room-storage.js +40 -0
  352. package/dist/lib/search-results.d.ts +27 -0
  353. package/dist/lib/search-results.js +83 -0
  354. package/dist/lib/search-sheet.d.ts +119 -0
  355. package/dist/lib/search-sheet.js +282 -0
  356. package/dist/lib/sections.d.ts +19 -0
  357. package/dist/lib/sections.js +36 -0
  358. package/dist/lib/session-activity.d.ts +5 -0
  359. package/dist/lib/session-activity.js +15 -0
  360. package/dist/lib/session-leases.d.ts +15 -0
  361. package/dist/lib/session-leases.js +61 -0
  362. package/dist/lib/spend.d.ts +42 -0
  363. package/dist/lib/spend.js +112 -0
  364. package/dist/lib/tint.d.ts +9 -0
  365. package/dist/lib/tint.js +53 -0
  366. package/dist/lib/transcript/collaboration.d.ts +97 -0
  367. package/dist/lib/transcript/collaboration.js +423 -0
  368. package/dist/lib/transcript/families.d.ts +212 -0
  369. package/dist/lib/transcript/families.js +906 -0
  370. package/dist/lib/transcript/favicons.d.ts +54 -0
  371. package/dist/lib/transcript/favicons.js +183 -0
  372. package/dist/lib/transcript/head-of-document.d.ts +27 -0
  373. package/dist/lib/transcript/head-of-document.js +274 -0
  374. package/dist/lib/transcript/head-state.d.ts +182 -0
  375. package/dist/lib/transcript/head-state.js +305 -0
  376. package/dist/lib/transcript/history-window.d.ts +13 -0
  377. package/dist/lib/transcript/history-window.js +52 -0
  378. package/dist/lib/transcript/images.d.ts +8 -0
  379. package/dist/lib/transcript/images.js +60 -0
  380. package/dist/lib/transcript/index.d.ts +15 -0
  381. package/dist/lib/transcript/index.js +8 -0
  382. package/dist/lib/transcript/ledger.d.ts +143 -0
  383. package/dist/lib/transcript/ledger.js +513 -0
  384. package/dist/lib/transcript/protocol.d.ts +23 -0
  385. package/dist/lib/transcript/protocol.js +1 -0
  386. package/dist/lib/transcript/sittings.d.ts +75 -0
  387. package/dist/lib/transcript/sittings.js +147 -0
  388. package/dist/lib/transcript/tail-scroll.d.ts +36 -0
  389. package/dist/lib/transcript/tail-scroll.js +167 -0
  390. package/dist/lib/transcript/tail-state.d.ts +17 -0
  391. package/dist/lib/transcript/tail-state.js +30 -0
  392. package/dist/lib/transcript/turns.d.ts +192 -0
  393. package/dist/lib/transcript/turns.js +576 -0
  394. package/dist/lib/transcript/use-transcript-opening.d.ts +12 -0
  395. package/dist/lib/transcript/use-transcript-opening.js +31 -0
  396. package/dist/lib/transcript/use-transcript-tail.d.ts +25 -0
  397. package/dist/lib/transcript/use-transcript-tail.js +158 -0
  398. package/dist/lib/transcript/vocabulary.d.ts +119 -0
  399. package/dist/lib/transcript/vocabulary.js +293 -0
  400. package/dist/lib/transcript/web-address.d.ts +6 -0
  401. package/dist/lib/transcript/web-address.js +18 -0
  402. package/dist/lib/transcript/workflow.d.ts +31 -0
  403. package/dist/lib/transcript/workflow.js +133 -0
  404. package/dist/lib/use-activity-order.d.ts +2 -0
  405. package/dist/lib/use-activity-order.js +23 -0
  406. package/dist/lib/use-day-usage.d.ts +8 -0
  407. package/dist/lib/use-day-usage.js +31 -0
  408. package/dist/lib/use-deferred-list-order.d.ts +26 -0
  409. package/dist/lib/use-deferred-list-order.js +142 -0
  410. package/dist/lib/use-ingest-activity.d.ts +2 -0
  411. package/dist/lib/use-ingest-activity.js +49 -0
  412. package/dist/lib/use-list-motion.d.ts +4 -0
  413. package/dist/lib/use-list-motion.js +67 -0
  414. package/dist/ports.d.ts +222 -0
  415. package/dist/ports.js +3 -0
  416. package/dist/room-context.d.ts +60 -0
  417. package/dist/room-context.js +87 -0
  418. package/dist/styles/activity-chart.css +127 -0
  419. package/dist/styles/attachments.css +29 -0
  420. package/dist/styles/favicon.css +73 -0
  421. package/dist/styles/history-room.css +18 -0
  422. package/dist/styles/host-inputs.css +250 -0
  423. package/dist/styles/image-gallery.css +30 -0
  424. package/dist/styles/ingest.css +51 -0
  425. package/dist/styles/message-preview.css +34 -0
  426. package/dist/styles/models-chart.css +66 -0
  427. package/dist/styles/project-chart.css +19 -0
  428. package/dist/styles/reader.css +33 -0
  429. package/dist/styles/room.css +1411 -0
  430. package/dist/styles/search.css +252 -0
  431. package/dist/styles/transcript-activity.css +70 -0
  432. package/dist/styles/transcript-head.css +220 -0
  433. package/dist/styles/transcript-loading.css +57 -0
  434. package/dist/styles/transcript.css +1226 -0
  435. package/package.json +61 -4
@@ -0,0 +1,80 @@
1
+ /** Shared timing for small, interruptible movements in the reading surfaces. */
2
+ export const SETTLE_EASING = "cubic-bezier(0.22, 1, 0.36, 1)";
3
+ export const REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
4
+ export function prefersReducedMotion() {
5
+ return window.matchMedia(REDUCED_MOTION_QUERY).matches;
6
+ }
7
+ /** FLIP in scroll-content coordinates, so ordinary scrolling never looks like
8
+ * a reorder. Only rows crossing the viewport acquire animation layers. */
9
+ export class ListMotion {
10
+ positions = new Map();
11
+ animations = new Map();
12
+ initialized = false;
13
+ update(container, reduced) {
14
+ // Read the current visual displacement before cancelling an interrupted
15
+ // transition. A second sort continues from the frame the reader sees.
16
+ const offsets = new Map();
17
+ for (const [key, { element }] of this.animations) {
18
+ const style = getComputedStyle(element);
19
+ offsets.set(key, {
20
+ y: style.transform === "none" ? 0 : new DOMMatrixReadOnly(style.transform).m42,
21
+ opacity: Number(style.opacity),
22
+ });
23
+ }
24
+ this.cancel();
25
+ const edge = container.getBoundingClientRect().top;
26
+ const top = container.scrollTop;
27
+ const bottom = top + container.clientHeight;
28
+ // Batch every layout read before starting any animations.
29
+ const rows = Array.from(container.querySelectorAll("[data-motion-key]"), (element) => {
30
+ const rect = element.getBoundingClientRect();
31
+ return {
32
+ element,
33
+ key: element.dataset.motionKey,
34
+ position: { top: rect.top - edge + top, height: rect.height },
35
+ };
36
+ });
37
+ for (const { element, key, position } of rows) {
38
+ const previous = this.positions.get(key);
39
+ const oldTop = previous === undefined ? position.top : previous.top + (offsets.get(key)?.y ?? 0);
40
+ const visibleAt = (y) => y + position.height >= top && y <= bottom;
41
+ const crossesViewport = visibleAt(oldTop) || visibleAt(position.top);
42
+ if (!this.initialized || reduced || !crossesViewport || position.height === 0)
43
+ continue;
44
+ // A row coming from thousands of entries away enters at the viewport
45
+ // edge instead of racing across an entire archive in a single frame.
46
+ const travel = container.clientHeight + position.height;
47
+ const delta = Math.max(-travel, Math.min(travel, oldTop - position.top));
48
+ const opacity = offsets.get(key)?.opacity ?? 1;
49
+ const frames = previous === undefined
50
+ ? [
51
+ { opacity: 0, transform: "translateY(6px)" },
52
+ { opacity: 1, transform: "none" },
53
+ ]
54
+ : Math.abs(delta) > 0.5 || opacity < 1
55
+ ? [
56
+ { transform: `translateY(${delta}px)`, opacity },
57
+ { transform: "none", opacity: 1 },
58
+ ]
59
+ : null;
60
+ if (frames === null)
61
+ continue;
62
+ const animation = element.animate(frames, {
63
+ duration: previous === undefined ? 220 : 340,
64
+ easing: SETTLE_EASING,
65
+ });
66
+ this.animations.set(key, { element, animation });
67
+ animation.onfinish = () => {
68
+ if (this.animations.get(key)?.animation === animation)
69
+ this.animations.delete(key);
70
+ };
71
+ }
72
+ this.positions = new Map(rows.map(({ key, position }) => [key, position]));
73
+ this.initialized = true;
74
+ }
75
+ cancel() {
76
+ for (const { animation } of this.animations.values())
77
+ animation.cancel();
78
+ this.animations.clear();
79
+ }
80
+ }
@@ -0,0 +1,13 @@
1
+ /** The two calls the room makes of a store; `localStorage` is one. */
2
+ export interface KeyValueStore {
3
+ getItem(key: string): string | null;
4
+ setItem(key: string, value: string): void;
5
+ }
6
+ /** A host's store as the room uses it: every name under one prefix. */
7
+ export interface RoomStorage extends KeyValueStore {
8
+ readonly prefix: string;
9
+ }
10
+ export declare const DEFAULT_STORAGE_PREFIX = "ah.";
11
+ /** The browser's localStorage, or null where it cannot be reached. */
12
+ export declare function browserStorage(): KeyValueStore | null;
13
+ export declare function createRoomStorage(prefix: string, backend: KeyValueStore | null): RoomStorage;
@@ -0,0 +1,40 @@
1
+ // The room's few remembered preferences — recent searches, dealt project
2
+ // tints, the site-icon setting, the fold — kept in the host's store under the
3
+ // host's prefix. Nothing the room writes lands outside that prefix, and a
4
+ // store that refuses (a private window, a blocked origin) costs the memory,
5
+ // never the room.
6
+ export const DEFAULT_STORAGE_PREFIX = "ah.";
7
+ /** The browser's localStorage, or null where it cannot be reached. */
8
+ export function browserStorage() {
9
+ try {
10
+ return window.localStorage;
11
+ }
12
+ catch {
13
+ return null;
14
+ }
15
+ }
16
+ export function createRoomStorage(prefix, backend) {
17
+ return {
18
+ prefix,
19
+ getItem(key) {
20
+ if (backend === null)
21
+ return null;
22
+ try {
23
+ return backend.getItem(`${prefix}${key}`);
24
+ }
25
+ catch {
26
+ return null;
27
+ }
28
+ },
29
+ setItem(key, value) {
30
+ if (backend === null)
31
+ return;
32
+ try {
33
+ backend.setItem(`${prefix}${key}`, value);
34
+ }
35
+ catch {
36
+ // refused: the preference holds for this run
37
+ }
38
+ },
39
+ };
40
+ }
@@ -0,0 +1,27 @@
1
+ import type { SearchHit, SessionRow } from "@vibefield/rosetta-protocol";
2
+ import type { ProjectViewModel } from "./model.js";
3
+ export interface SessionSearchResult {
4
+ readonly session: SessionRow;
5
+ readonly hits: readonly SearchHit[];
6
+ }
7
+ export interface ProjectSearchResultGroup {
8
+ readonly project: ProjectViewModel;
9
+ readonly sessions: readonly SessionSearchResult[];
10
+ readonly hitCount: number;
11
+ }
12
+ export interface SearchResultCounts {
13
+ readonly matches: number;
14
+ readonly sessions: number;
15
+ readonly projects: number;
16
+ }
17
+ export interface SearchResultsViewModel {
18
+ readonly groups: readonly ProjectSearchResultGroup[];
19
+ readonly hitCountByProject: ReadonlyMap<string, number>;
20
+ readonly counts: SearchResultCounts;
21
+ }
22
+ /**
23
+ * Groups protocol search hits once for every UI consumer. A child-session hit
24
+ * belongs to its root thread in navigation; the hit keeps its own session id
25
+ * so opening it can expand and land inside the embedded subthread.
26
+ */
27
+ export declare function createSearchResultsViewModel(hits: readonly SearchHit[], sessions: readonly SessionRow[], projectsById: ReadonlyMap<string, ProjectViewModel>): SearchResultsViewModel;
@@ -0,0 +1,83 @@
1
+ import { projectKey } from "./model.js";
2
+ import { compareSessionActivity } from "./session-activity.js";
3
+ function compareSessionResults(left, right) {
4
+ return (right.hits.length - left.hits.length ||
5
+ compareSessionActivity(left.session, right.session));
6
+ }
7
+ function compareProjectGroups(left, right) {
8
+ return (right.hitCount - left.hitCount ||
9
+ left.project.row.name.localeCompare(right.project.row.name) ||
10
+ left.project.row.id.localeCompare(right.project.row.id));
11
+ }
12
+ /** Resolve navigation from the authoritative parent chain. `root_session_id`
13
+ * remains a useful fallback for an orphaned retained child, but an immediate
14
+ * parent must never masquerade as a top-level thread for a nested hit. */
15
+ function navigationRoot(session, sessions) {
16
+ let current = session;
17
+ const visited = new Set();
18
+ while (current.parent_session_id !== null && !visited.has(current.id)) {
19
+ visited.add(current.id);
20
+ const parent = sessions.get(current.parent_session_id);
21
+ if (!parent)
22
+ return sessions.get(session.root_session_id) ?? null;
23
+ current = parent;
24
+ }
25
+ return current.parent_session_id === null ? current : null;
26
+ }
27
+ /**
28
+ * Groups protocol search hits once for every UI consumer. A child-session hit
29
+ * belongs to its root thread in navigation; the hit keeps its own session id
30
+ * so opening it can expand and land inside the embedded subthread.
31
+ */
32
+ export function createSearchResultsViewModel(hits, sessions, projectsById) {
33
+ const sessionById = new Map(sessions.map((session) => [session.id, session]));
34
+ const hitsBySessionId = new Map();
35
+ for (const hit of hits) {
36
+ const hitSession = sessionById.get(hit.session_id);
37
+ if (!hitSession)
38
+ continue;
39
+ const rootSession = navigationRoot(hitSession, sessionById);
40
+ if (!rootSession)
41
+ continue;
42
+ const sessionHits = hitsBySessionId.get(rootSession.id);
43
+ if (sessionHits)
44
+ sessionHits.push(hit);
45
+ else
46
+ hitsBySessionId.set(rootSession.id, [hit]);
47
+ }
48
+ const sessionsByProjectId = new Map();
49
+ for (const [sessionId, sessionHits] of hitsBySessionId) {
50
+ const session = sessionById.get(sessionId);
51
+ if (!session)
52
+ continue;
53
+ const projectId = projectKey(session);
54
+ if (!projectsById.has(projectId))
55
+ continue;
56
+ const projectSessions = sessionsByProjectId.get(projectId);
57
+ const result = { session, hits: sessionHits };
58
+ if (projectSessions)
59
+ projectSessions.push(result);
60
+ else
61
+ sessionsByProjectId.set(projectId, [result]);
62
+ }
63
+ const groups = [];
64
+ for (const [projectId, projectSessions] of sessionsByProjectId) {
65
+ const project = projectsById.get(projectId);
66
+ if (!project)
67
+ continue;
68
+ const sortedSessions = projectSessions.sort(compareSessionResults);
69
+ groups.push({
70
+ project,
71
+ sessions: sortedSessions,
72
+ hitCount: sortedSessions.reduce((total, result) => total + result.hits.length, 0),
73
+ });
74
+ }
75
+ groups.sort(compareProjectGroups);
76
+ const hitCountByProject = new Map(groups.map((group) => [group.project.row.id, group.hitCount]));
77
+ const counts = {
78
+ matches: groups.reduce((total, group) => total + group.hitCount, 0),
79
+ sessions: groups.reduce((total, group) => total + group.sessions.length, 0),
80
+ projects: groups.length,
81
+ };
82
+ return { groups, hitCountByProject, counts };
83
+ }
@@ -0,0 +1,119 @@
1
+ import type { SearchHit, SearchReadiness, SessionRow } from "@vibefield/rosetta-protocol";
2
+ import type { ProjectViewModel } from "./model.js";
3
+ import type { SearchResultCounts, SearchResultsViewModel, SessionSearchResult } from "./search-results.js";
4
+ /** The canonical wire kinds the sheet lets the reader narrow to. `any` sends
5
+ * no `kind`; the two named ones are exactly what search indexes (prompts and
6
+ * replies, decided 2026-09-09) — the vocabulary a reader thinks in: what I
7
+ * asked, what it said. */
8
+ export type SearchKindFilter = "any" | "user" | "agent";
9
+ export type SearchScopeFilter = "everywhere" | "project";
10
+ export interface SearchFilters {
11
+ readonly scope: SearchScopeFilter;
12
+ readonly kind: SearchKindFilter;
13
+ /** A runtime family (`claude-code`, `codex`, …), or null for every agent. */
14
+ readonly agent: string | null;
15
+ }
16
+ export declare const DEFAULT_SEARCH_FILTERS: SearchFilters;
17
+ export declare const KIND_FILTER_OPTIONS: readonly {
18
+ value: SearchKindFilter;
19
+ label: string;
20
+ }[];
21
+ export interface SheetHead {
22
+ readonly type: "head";
23
+ readonly key: string;
24
+ readonly project: ProjectViewModel;
25
+ readonly session: SessionRow;
26
+ readonly hitCount: number;
27
+ }
28
+ export interface SheetOption {
29
+ readonly type: "option";
30
+ readonly key: string;
31
+ /** The DOM id `aria-activedescendant` points at. */
32
+ readonly id: string;
33
+ /** The option's position among options only — heads are not walkable. */
34
+ readonly index: number;
35
+ readonly project: ProjectViewModel;
36
+ /** The navigable root session (a child hit lands inside its parent). */
37
+ readonly session: SessionRow;
38
+ readonly hit: SearchHit;
39
+ }
40
+ export type SheetLine = SheetHead | SheetOption;
41
+ export interface SheetLines {
42
+ readonly lines: readonly SheetLine[];
43
+ readonly options: readonly SheetOption[];
44
+ }
45
+ export declare function optionDomId(hit: SearchHit): string;
46
+ /** One session's hits in reading order: by time when the source reported one,
47
+ * then by id, so the stepper walks the transcript downward. */
48
+ export declare function hitsInReadingOrder(hits: readonly SearchHit[]): SearchHit[];
49
+ /**
50
+ * Flattens the grouped result model into the lines the sheet draws: one
51
+ * sticky head per session, its hits beneath in reading order. Project is
52
+ * folded into the head's breadcrumb rather than being a level of its own —
53
+ * three levels of nesting in a 640px sheet is a tree, and a tree is not
54
+ * something you scan.
55
+ */
56
+ export declare function buildSheetLines(model: SearchResultsViewModel | null): SheetLines;
57
+ /** Find the session result that owns a hit, for the stepper. */
58
+ export declare function sessionResultOf(model: SearchResultsViewModel, rootSessionId: string): SessionSearchResult | null;
59
+ export type CursorKey = "ArrowDown" | "ArrowUp" | "Home" | "End" | "PageDown" | "PageUp";
60
+ export declare function isCursorKey(key: string): key is CursorKey;
61
+ /** APG listbox arithmetic: no wrap, clamped at both ends, a page of eight. */
62
+ export declare function moveCursor(active: number, key: CursorKey, count: number, page?: number): number;
63
+ /** Jump to the first option of the next session (or of the previous one):
64
+ * VS Code's sticky separators and Raycast's sections both give the keyboard a
65
+ * way past a long group without holding an arrow. Clamps at the ends. */
66
+ export declare function moveSection(active: number, direction: 1 | -1, options: readonly SheetOption[]): number;
67
+ /**
68
+ * The snippet as the sheet prints it: whitespace collapsed to one line of
69
+ * prose, and every home-rooted path shortened to `~/…` — the ledger row's own
70
+ * rule, so a path reads the same in a result as it does in the transcript.
71
+ * The `[` `]` match markers survive untouched for `parseSnippet`.
72
+ */
73
+ export declare function cleanSnippet(snippet: string, home: string | null): string;
74
+ /** Who said it, in the room's own words. */
75
+ export declare function kindWord(kind: string, agentLabel: string): string;
76
+ /** When the matched message was written: the clock alone inside today, the
77
+ * day and clock inside the year, the day and year beyond it; a dash when the
78
+ * source never said. Every part is tabular by the stylesheet. */
79
+ export declare function hitWhen(at: number | null, nowMs: number): string;
80
+ /** A session's name for a breadcrumb: its title, its summary, or — for a
81
+ * session nobody named — the first eight characters of its native id, which
82
+ * the caller sets in mono. `mono` says which happened. */
83
+ export declare function sessionCrumb(session: SessionRow): {
84
+ text: string;
85
+ mono: boolean;
86
+ };
87
+ /** `43 matches · 12 sessions · 4 projects` — the numbers, never the word
88
+ * "results", which hides its own reach (design record §06). */
89
+ export declare function countSentence(counts: SearchResultCounts): string;
90
+ export interface SheetStateInput {
91
+ readonly query: string;
92
+ readonly error: string | null;
93
+ readonly readiness: SearchReadiness | null;
94
+ readonly progress: {
95
+ documents_done: number;
96
+ documents_total: number;
97
+ } | null;
98
+ readonly canonicalReady: boolean | null;
99
+ readonly ingestPaused: boolean;
100
+ readonly counts: SearchResultCounts | null;
101
+ readonly limit: number;
102
+ }
103
+ /**
104
+ * The one sentence in the sheet's foot. Degraded is Tuesday here (design
105
+ * record §09): a building index says so with a number, an error says what
106
+ * happened, and a full page says it is the first page.
107
+ */
108
+ export declare function stateSentence(input: SheetStateInput): string;
109
+ /** Under the room's storage prefix. */
110
+ export declare const RECENT_QUERIES_KEY = "search.recent";
111
+ export declare const RECENT_QUERIES_CAP = 8;
112
+ export interface RecentStore {
113
+ getItem(key: string): string | null;
114
+ setItem(key: string, value: string): void;
115
+ }
116
+ export declare function readRecentQueries(store: RecentStore | null): string[];
117
+ /** Most recent first, exact duplicates folded, capped. */
118
+ export declare function rememberQuery(store: RecentStore | null, query: string): string[];
119
+ export declare function forgetQuery(store: RecentStore | null, query: string): string[];
@@ -0,0 +1,282 @@
1
+ export const DEFAULT_SEARCH_FILTERS = {
2
+ scope: "everywhere",
3
+ kind: "any",
4
+ agent: null,
5
+ };
6
+ export const KIND_FILTER_OPTIONS = [
7
+ { value: "any", label: "prompts and replies" },
8
+ { value: "user", label: "prompts" },
9
+ { value: "agent", label: "replies" },
10
+ ];
11
+ export function optionDomId(hit) {
12
+ return `ah-sheet-opt-${hit.session_id}-${hit.timeline_id}`;
13
+ }
14
+ /** One session's hits in reading order: by time when the source reported one,
15
+ * then by id, so the stepper walks the transcript downward. */
16
+ export function hitsInReadingOrder(hits) {
17
+ return [...hits].sort((a, b) => (a.at ?? Number.MAX_SAFE_INTEGER) - (b.at ?? Number.MAX_SAFE_INTEGER) ||
18
+ a.timeline_id.localeCompare(b.timeline_id));
19
+ }
20
+ /**
21
+ * Flattens the grouped result model into the lines the sheet draws: one
22
+ * sticky head per session, its hits beneath in reading order. Project is
23
+ * folded into the head's breadcrumb rather than being a level of its own —
24
+ * three levels of nesting in a 640px sheet is a tree, and a tree is not
25
+ * something you scan.
26
+ */
27
+ export function buildSheetLines(model) {
28
+ if (model === null)
29
+ return { lines: [], options: [] };
30
+ const lines = [];
31
+ const options = [];
32
+ for (const group of model.groups) {
33
+ for (const result of group.sessions) {
34
+ lines.push({
35
+ type: "head",
36
+ key: `head:${result.session.id}`,
37
+ project: group.project,
38
+ session: result.session,
39
+ hitCount: result.hits.length,
40
+ });
41
+ for (const hit of hitsInReadingOrder(result.hits)) {
42
+ const option = {
43
+ type: "option",
44
+ key: `opt:${hit.session_id}:${hit.timeline_id}`,
45
+ id: optionDomId(hit),
46
+ index: options.length,
47
+ project: group.project,
48
+ session: result.session,
49
+ hit,
50
+ };
51
+ options.push(option);
52
+ lines.push(option);
53
+ }
54
+ }
55
+ }
56
+ return { lines, options };
57
+ }
58
+ /** Find the session result that owns a hit, for the stepper. */
59
+ export function sessionResultOf(model, rootSessionId) {
60
+ for (const group of model.groups) {
61
+ for (const result of group.sessions) {
62
+ if (result.session.id === rootSessionId)
63
+ return result;
64
+ }
65
+ }
66
+ return null;
67
+ }
68
+ export function isCursorKey(key) {
69
+ return (key === "ArrowDown" ||
70
+ key === "ArrowUp" ||
71
+ key === "Home" ||
72
+ key === "End" ||
73
+ key === "PageDown" ||
74
+ key === "PageUp");
75
+ }
76
+ /** APG listbox arithmetic: no wrap, clamped at both ends, a page of eight. */
77
+ export function moveCursor(active, key, count, page = 8) {
78
+ if (count <= 0)
79
+ return -1;
80
+ const last = count - 1;
81
+ const from = active < 0 ? -1 : Math.min(active, last);
82
+ switch (key) {
83
+ case "ArrowDown":
84
+ return Math.min(last, from + 1);
85
+ case "ArrowUp":
86
+ return Math.max(0, from - 1);
87
+ case "Home":
88
+ return 0;
89
+ case "End":
90
+ return last;
91
+ case "PageDown":
92
+ return Math.min(last, Math.max(0, from) + page);
93
+ case "PageUp":
94
+ return Math.max(0, from - page);
95
+ }
96
+ }
97
+ /** Jump to the first option of the next session (or of the previous one):
98
+ * VS Code's sticky separators and Raycast's sections both give the keyboard a
99
+ * way past a long group without holding an arrow. Clamps at the ends. */
100
+ export function moveSection(active, direction, options) {
101
+ if (options.length === 0)
102
+ return -1;
103
+ const from = active < 0 ? 0 : Math.min(active, options.length - 1);
104
+ // Every index read here stays inside options: from is clamped to it, the
105
+ // walk down stops above 0, and the walk up below options.length.
106
+ const sessionAt = (index) => options[index].session.id;
107
+ const current = sessionAt(from);
108
+ if (direction === 1) {
109
+ for (let i = from + 1; i < options.length; i += 1) {
110
+ if (sessionAt(i) !== current)
111
+ return i;
112
+ }
113
+ return options.length - 1;
114
+ }
115
+ // Up: the head of this session if we are not already on it, else the
116
+ // head of the session before.
117
+ let head = from;
118
+ while (head > 0 && sessionAt(head - 1) === current)
119
+ head -= 1;
120
+ if (head !== from)
121
+ return head;
122
+ if (head === 0)
123
+ return 0;
124
+ const previous = sessionAt(head - 1);
125
+ let start = head - 1;
126
+ while (start > 0 && sessionAt(start - 1) === previous)
127
+ start -= 1;
128
+ return start;
129
+ }
130
+ /* ── the words on a row ────────────────────────────────────────────────── */
131
+ /**
132
+ * The snippet as the sheet prints it: whitespace collapsed to one line of
133
+ * prose, and every home-rooted path shortened to `~/…` — the ledger row's own
134
+ * rule, so a path reads the same in a result as it does in the transcript.
135
+ * The `[` `]` match markers survive untouched for `parseSnippet`.
136
+ */
137
+ export function cleanSnippet(snippet, home) {
138
+ let text = snippet.replace(/\s+/g, " ").trim();
139
+ if (home !== null && home !== "" && home !== "/") {
140
+ const escaped = home.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
141
+ text = text.replace(new RegExp(`(^|[^\\w~])${escaped}(?=/|\\b)`, "g"), "$1~");
142
+ }
143
+ return text;
144
+ }
145
+ /** Who said it, in the room's own words. */
146
+ export function kindWord(kind, agentLabel) {
147
+ switch (kind) {
148
+ case "user":
149
+ return "you";
150
+ case "agent":
151
+ return agentLabel;
152
+ case "tool_use":
153
+ return "activity";
154
+ case "thinking":
155
+ return "thinking";
156
+ case "summary":
157
+ return "summary";
158
+ case "notice":
159
+ return "notice";
160
+ case "collaboration":
161
+ return "team";
162
+ default:
163
+ return kind;
164
+ }
165
+ }
166
+ const DAY_MS = 86_400_000;
167
+ function startOfDay(ms) {
168
+ const d = new Date(ms);
169
+ d.setHours(0, 0, 0, 0);
170
+ return d.getTime();
171
+ }
172
+ /** When the matched message was written: the clock alone inside today, the
173
+ * day and clock inside the year, the day and year beyond it; a dash when the
174
+ * source never said. Every part is tabular by the stylesheet. */
175
+ export function hitWhen(at, nowMs) {
176
+ if (at === null)
177
+ return "—";
178
+ const d = new Date(at);
179
+ const p = (v) => String(v).padStart(2, "0");
180
+ const clock = `${p(d.getHours())}:${p(d.getMinutes())}`;
181
+ const today = startOfDay(nowMs);
182
+ if (at >= today && at < today + DAY_MS)
183
+ return clock;
184
+ const day = d.toLocaleDateString("en-GB", { day: "numeric", month: "short" });
185
+ if (d.getFullYear() === new Date(nowMs).getFullYear())
186
+ return `${day} · ${clock}`;
187
+ return `${day} ${d.getFullYear()}`;
188
+ }
189
+ /** A session's name for a breadcrumb: its title, its summary, or — for a
190
+ * session nobody named — the first eight characters of its native id, which
191
+ * the caller sets in mono. `mono` says which happened. */
192
+ export function sessionCrumb(session) {
193
+ if (session.title !== null)
194
+ return { text: session.title, mono: false };
195
+ if (session.summary !== null)
196
+ return { text: session.summary, mono: false };
197
+ return { text: session.native_session_id.slice(0, 8), mono: true };
198
+ }
199
+ function plural(n, one, many = `${one}s`) {
200
+ return `${n.toLocaleString("en-US")} ${n === 1 ? one : many}`;
201
+ }
202
+ /** `43 matches · 12 sessions · 4 projects` — the numbers, never the word
203
+ * "results", which hides its own reach (design record §06). */
204
+ export function countSentence(counts) {
205
+ return [
206
+ plural(counts.matches, "match", "matches"),
207
+ plural(counts.sessions, "session"),
208
+ plural(counts.projects, "project"),
209
+ ].join(" · ");
210
+ }
211
+ /**
212
+ * The one sentence in the sheet's foot. Degraded is Tuesday here (design
213
+ * record §09): a building index says so with a number, an error says what
214
+ * happened, and a full page says it is the first page.
215
+ */
216
+ export function stateSentence(input) {
217
+ if (input.error !== null)
218
+ return `search failed · ${input.error}`;
219
+ if (input.ingestPaused)
220
+ return "transcript import paused · search covers what was read";
221
+ if (input.readiness !== null && input.readiness !== "ready") {
222
+ if (input.readiness === "failed") {
223
+ return input.canonicalReady ? "search unavailable" : "history is being imported";
224
+ }
225
+ if (input.progress !== null && input.progress.documents_total > 0) {
226
+ const pct = Math.floor((100 * input.progress.documents_done) / input.progress.documents_total);
227
+ return `indexing · ${pct}% · ${input.progress.documents_done.toLocaleString("en-US")} of ${input.progress.documents_total.toLocaleString("en-US")} messages · results are partial`;
228
+ }
229
+ if (input.readiness === "building") {
230
+ return input.canonicalReady ? "search is being prepared" : "history is being imported";
231
+ }
232
+ return `search is ${input.readiness.replace("-", " ")} · results may be partial`;
233
+ }
234
+ if (input.counts === null || input.query === "")
235
+ return "";
236
+ const sentence = countSentence(input.counts);
237
+ return input.counts.matches >= input.limit
238
+ ? `first ${input.limit} matches · narrow the search to see the rest`
239
+ : sentence;
240
+ }
241
+ /* ── what was searched before ──────────────────────────────────────────── */
242
+ /** Under the room's storage prefix. */
243
+ export const RECENT_QUERIES_KEY = "search.recent";
244
+ export const RECENT_QUERIES_CAP = 8;
245
+ export function readRecentQueries(store) {
246
+ if (store === null)
247
+ return [];
248
+ try {
249
+ const raw = store.getItem(RECENT_QUERIES_KEY);
250
+ if (raw === null)
251
+ return [];
252
+ const parsed = JSON.parse(raw);
253
+ if (!Array.isArray(parsed))
254
+ return [];
255
+ return parsed.filter((q) => typeof q === "string" && q.trim() !== "");
256
+ }
257
+ catch {
258
+ return [];
259
+ }
260
+ }
261
+ function writeRecent(store, queries) {
262
+ if (store !== null) {
263
+ try {
264
+ store.setItem(RECENT_QUERIES_KEY, JSON.stringify(queries));
265
+ }
266
+ catch {
267
+ // A browser that refuses storage refuses the memory, not the search.
268
+ }
269
+ }
270
+ return queries;
271
+ }
272
+ /** Most recent first, exact duplicates folded, capped. */
273
+ export function rememberQuery(store, query) {
274
+ const q = query.trim();
275
+ if (q === "")
276
+ return readRecentQueries(store);
277
+ const rest = readRecentQueries(store).filter((r) => r !== q);
278
+ return writeRecent(store, [q, ...rest].slice(0, RECENT_QUERIES_CAP));
279
+ }
280
+ export function forgetQuery(store, query) {
281
+ return writeRecent(store, readRecentQueries(store).filter((r) => r !== query));
282
+ }
@@ -0,0 +1,19 @@
1
+ import type { RoomSection } from "../host-inputs.js";
2
+ export interface ArrangedSection<T> {
3
+ readonly section: RoomSection;
4
+ /** Its place among the sections given, which the list's motion keys on. */
5
+ readonly index: number;
6
+ /** Its rows, in the order they are drawn. */
7
+ readonly rows: readonly T[];
8
+ /** Whose order they keep: `room` draws a session list's day heads. */
9
+ readonly order: "host" | "room";
10
+ }
11
+ /**
12
+ * The rows each section draws. A row two sections name is drawn in the
13
+ * first; a row no section names goes to the first section without `ids`, or
14
+ * nowhere when every section names its own — so a host filter hides rows by
15
+ * naming fewer. Ids the room does not hold are skipped.
16
+ */
17
+ export declare function arrangeSections<T>(sections: readonly RoomSection[], rowsInRoomOrder: readonly T[], idOf: (row: T) => string): ArrangedSection<T>[];
18
+ /** How many rows a folded section says it holds: the host's figure, or its rows. */
19
+ export declare function foldCount<T>(arranged: ArrangedSection<T>): number;