@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,54 @@
1
+ import type { KeyValueStore } from "../room-storage.js";
2
+ /** What a site wears while nothing is known, and what it wears when the
3
+ * answer was no. `off` is the setting; `globe` is the outcome. */
4
+ export type FaviconState = "off" | "loading" | "ready" | "globe";
5
+ export interface FaviconResult {
6
+ /** A `data:` URL, or null in every state but `ready`. */
7
+ readonly src: string | null;
8
+ readonly state: FaviconState;
9
+ }
10
+ /** One room's icons: the setting, and one shared per-site map so eight lines
11
+ * from one site produce one ask. */
12
+ export interface FaviconStore {
13
+ /** Whether this room may ask its host for site icons. */
14
+ enabled(): boolean;
15
+ /** Turn site icons on or off. Turning them OFF forgets what was already
16
+ * fetched, so the room stops showing icons it is no longer allowed to ask
17
+ * for — a setting that only applied to future sites would be a half-truth. */
18
+ setEnabled(on: boolean): void;
19
+ subscribe(listener: () => void): () => void;
20
+ /** Advances whenever the setting changes. */
21
+ generation(): number;
22
+ ask(host: string): void;
23
+ snapshot(host: string | null): FaviconResult;
24
+ }
25
+ /**
26
+ * The store over a host's `favicon` act, or over none: a plain browser tab
27
+ * has no host to ask, which is the globe, permanently, and stated rather than
28
+ * retried.
29
+ */
30
+ export declare function createFaviconStore(fetchIcon: ((host: string) => Promise<string | null>) | null, store: KeyValueStore | null): FaviconStore;
31
+ /** The host a source url belongs to, or null when it names none. Lowercased,
32
+ * because a host is case-insensitive and the cache is not. */
33
+ export declare function hostOf(url: string): string | null;
34
+ /** The setting, live. A toggle binds to this; so does every icon in the
35
+ * room, which is how turning it off empties them in the same frame. */
36
+ export declare function useSiteIconsEnabled(): boolean;
37
+ /** Turn the room's site icons on or off. */
38
+ export declare function useSetSiteIconsEnabled(): (on: boolean) => void;
39
+ /**
40
+ * One host's icon.
41
+ *
42
+ * Returns `off` when the setting is off, `loading` while the host is being
43
+ * asked, `ready` with a `data:` URL when there is one, and `globe` for every
44
+ * no — a host with no icon, a refusal, a 404, a browser tab with no host.
45
+ * A null host is a globe too, which is what a source with no parseable url
46
+ * honestly is.
47
+ *
48
+ * The ask happens during the subscribe, not in an effect: several lines
49
+ * naming one site share the room's entry, so the request is made once
50
+ * regardless of how many of them mount. The setting's generation also keys
51
+ * the subscription: even an off/on pair batched into one render must start
52
+ * a fresh request, since turning off invalidates the previous one.
53
+ */
54
+ export declare function useFavicon(host: string | null): FaviconResult;
@@ -0,0 +1,183 @@
1
+ // Site icons, as the renderer sees them.
2
+ //
3
+ // A search's sources are the one place in this room where identity should
4
+ // carry colour: a site's own icon is content, and it is the fastest way to
5
+ // tell GitHub from MDN from somebody's blog. The icon is not in the protocol
6
+ // — a source carries a url and a title and nothing else — so it has to be
7
+ // fetched, and fetching is a network act the renderer is not allowed to
8
+ // perform. It asks the room's host (`HostPort.favicon`), which validates the
9
+ // host, dials the SITE rather than a favicon service, and caches per host.
10
+ //
11
+ // Because it IS a network act, it is named and it is optional: `fetch site
12
+ // icons`, on by default, off in one call. Off means off — no request is made,
13
+ // and every site wears the globe.
14
+ //
15
+ // This module holds the room's half: one store per room (the setting and one
16
+ // shared per-host map, so eight lines from one site produce one ask), and the
17
+ // hooks the components read. It deliberately holds no component and no markup.
18
+ import { useCallback, useSyncExternalStore } from "react";
19
+ import { useRoom } from "../../room-context.js";
20
+ import { webAddress } from "./web-address.js";
21
+ /** Versioned like `tintSeq.v1`, and stored the same way, under the room's
22
+ * storage prefix: a renderer preference with no home on the daemon side yet. */
23
+ const STORE_KEY = "siteIcons.v1";
24
+ const OFF = { src: null, state: "off" };
25
+ const LOADING = { src: null, state: "loading" };
26
+ const GLOBE = { src: null, state: "globe" };
27
+ /**
28
+ * The store over a host's `favicon` act, or over none: a plain browser tab
29
+ * has no host to ask, which is the globe, permanently, and stated rather than
30
+ * retried.
31
+ */
32
+ export function createFaviconStore(fetchIcon, store) {
33
+ let enabled = null;
34
+ /** Settled answers, by host: a `data:` URL, or null for every honest no.
35
+ * The shell caches across launches; this caches across components, so a
36
+ * ledger of forty web lines makes one ask per site and not one per line. */
37
+ const known = new Map();
38
+ /** One stable `ready` value per host. `useSyncExternalStore` compares
39
+ * snapshots by identity, so a fresh object per read would render forever. */
40
+ const results = new Map();
41
+ /** Hosts currently being asked about. */
42
+ const pending = new Map();
43
+ let generation = 0;
44
+ /** Every mounted `useFavicon`, so a settled answer redraws the lines waiting
45
+ * on it and a changed setting redraws all of them. */
46
+ const listeners = new Set();
47
+ const announce = () => {
48
+ for (const listener of listeners)
49
+ listener();
50
+ };
51
+ function isEnabled() {
52
+ if (enabled === null) {
53
+ try {
54
+ // Anything but an explicit "off" is on, so a blocked or corrupt store
55
+ // lands on the default rather than silently disabling the feature.
56
+ enabled = store?.getItem(STORE_KEY) !== "off";
57
+ }
58
+ catch {
59
+ enabled = true;
60
+ }
61
+ }
62
+ return enabled;
63
+ }
64
+ function resultFor(host, src) {
65
+ const existing = results.get(host);
66
+ if (existing !== undefined && existing.src === src)
67
+ return existing;
68
+ const made = { src, state: "ready" };
69
+ results.set(host, made);
70
+ return made;
71
+ }
72
+ return {
73
+ enabled: isEnabled,
74
+ setEnabled(on) {
75
+ if (isEnabled() === on)
76
+ return;
77
+ enabled = on;
78
+ generation += 1;
79
+ try {
80
+ store?.setItem(STORE_KEY, on ? "on" : "off");
81
+ }
82
+ catch {
83
+ // storage refused; the choice holds for this run
84
+ }
85
+ if (!on) {
86
+ known.clear();
87
+ results.clear();
88
+ pending.clear();
89
+ }
90
+ announce();
91
+ },
92
+ subscribe(listener) {
93
+ listeners.add(listener);
94
+ return () => {
95
+ listeners.delete(listener);
96
+ };
97
+ },
98
+ generation: () => generation,
99
+ ask(host) {
100
+ if (known.has(host) || pending.has(host))
101
+ return;
102
+ if (fetchIcon === null) {
103
+ known.set(host, null);
104
+ return;
105
+ }
106
+ const requestGeneration = generation;
107
+ pending.set(host, requestGeneration);
108
+ fetchIcon(host)
109
+ .then((src) => {
110
+ if (generation !== requestGeneration)
111
+ return;
112
+ known.set(host, typeof src === "string" && src !== "" ? src : null);
113
+ })
114
+ .catch(() => {
115
+ if (generation !== requestGeneration)
116
+ return;
117
+ // The host refused or went away. Same fact to a reader: the globe.
118
+ known.set(host, null);
119
+ })
120
+ .finally(() => {
121
+ if (pending.get(host) === requestGeneration) {
122
+ pending.delete(host);
123
+ announce();
124
+ }
125
+ });
126
+ },
127
+ snapshot(host) {
128
+ if (!isEnabled())
129
+ return OFF;
130
+ if (host === null)
131
+ return GLOBE;
132
+ const src = known.get(host);
133
+ if (src === undefined)
134
+ return LOADING;
135
+ // Frozen results, not fresh objects: `useSyncExternalStore` compares by
136
+ // identity and a new object every read is an infinite render.
137
+ return src === null ? GLOBE : resultFor(host, src);
138
+ },
139
+ };
140
+ }
141
+ /** The host a source url belongs to, or null when it names none. Lowercased,
142
+ * because a host is case-insensitive and the cache is not. */
143
+ export function hostOf(url) {
144
+ return webAddress(url)?.host ?? null;
145
+ }
146
+ // ── the hooks ─────────────────────────────────────────────────────────────
147
+ /** The setting, live. A toggle binds to this; so does every icon in the
148
+ * room, which is how turning it off empties them in the same frame. */
149
+ export function useSiteIconsEnabled() {
150
+ const { favicons } = useRoom();
151
+ return useSyncExternalStore(favicons.subscribe, favicons.enabled, favicons.enabled);
152
+ }
153
+ /** Turn the room's site icons on or off. */
154
+ export function useSetSiteIconsEnabled() {
155
+ return useRoom().favicons.setEnabled;
156
+ }
157
+ /**
158
+ * One host's icon.
159
+ *
160
+ * Returns `off` when the setting is off, `loading` while the host is being
161
+ * asked, `ready` with a `data:` URL when there is one, and `globe` for every
162
+ * no — a host with no icon, a refusal, a 404, a browser tab with no host.
163
+ * A null host is a globe too, which is what a source with no parseable url
164
+ * honestly is.
165
+ *
166
+ * The ask happens during the subscribe, not in an effect: several lines
167
+ * naming one site share the room's entry, so the request is made once
168
+ * regardless of how many of them mount. The setting's generation also keys
169
+ * the subscription: even an off/on pair batched into one render must start
170
+ * a fresh request, since turning off invalidates the previous one.
171
+ */
172
+ export function useFavicon(host) {
173
+ const { favicons } = useRoom();
174
+ const revision = useSyncExternalStore(favicons.subscribe, favicons.generation, favicons.generation);
175
+ const on = favicons.enabled();
176
+ const subscribeToHost = useCallback((listener) => {
177
+ if (host !== null && on)
178
+ favicons.ask(host);
179
+ return favicons.subscribe(listener);
180
+ }, [favicons, host, on, revision]);
181
+ const snapshot = useCallback(() => favicons.snapshot(host), [favicons, host, on]);
182
+ return useSyncExternalStore(subscribeToHost, snapshot, snapshot);
183
+ }
@@ -0,0 +1,27 @@
1
+ import type { TimelineFeed } from "../hooks.js";
2
+ export interface HeadOfDocumentOptions {
3
+ /** Upward travel that spends the bar. Default 120px. */
4
+ readonly threshold?: number;
5
+ /** Stillness that decays the accumulator. Default 500ms. */
6
+ readonly decayMs?: number;
7
+ /** The scroll depth `--frost` reaches 1 at. Default 24px. */
8
+ readonly frostRange?: number;
9
+ /** How long the receipt pill stands. Default 1800ms, the record's clock. */
10
+ readonly receiptMs?: number;
11
+ /** Set false to leave the listeners unbound (a search view, an empty room). */
12
+ readonly enabled?: boolean;
13
+ }
14
+ export interface HeadOfDocument {
15
+ /** The accumulator as the bar reads it, 0..1. */
16
+ readonly pull: number;
17
+ /** `--frost`, 0..1 — also written onto the scroller for the CSS to consume. */
18
+ readonly frost: number;
19
+ /** The last landing while its receipt stands, then null. */
20
+ readonly receipt: {
21
+ readonly count: number;
22
+ readonly seq: number;
23
+ } | null;
24
+ }
25
+ export declare function useHeadOfDocument(scrollerRef: {
26
+ current: HTMLElement | null;
27
+ }, feed: TimelineFeed, options?: HeadOfDocumentOptions): HeadOfDocument;
@@ -0,0 +1,274 @@
1
+ // §08 — the head of the document, wired to a scroller.
2
+ //
3
+ // Four jobs, all of them listeners over `head-state.ts`'s arithmetic:
4
+ // approach — within one viewport of the top, fetch the page above and HOLD it
5
+ // the pull — 120px of upward travel at scrollTop 0 spends the bar
6
+ // landing — the page lands above the viewport, which does not move
7
+ // the fade — `--frost` rises 0 → 1 over the first 24px of scroll
8
+ //
9
+ // CONTRACT WITH THE ROW COMPONENTS: every top-level row/turn element inside the
10
+ // scroller carries `data-timeline-id="<timeline_id>"`. Landing finds the first
11
+ // old row by that attribute, measures it before and after the prepend, and adds
12
+ // the difference to scrollTop — Chromium suppresses scroll anchoring at scroll
13
+ // position zero, which is precisely where this fires. Without the attribute the
14
+ // hook falls back to a scrollHeight delta, which is correct but coarser.
15
+ import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
16
+ import { advancePull, frostOf, FROST_RANGE_PX, initialPull, PULL_DECAY_MS, PULL_THRESHOLD_PX, pullProgress, RECEIPT_MS, } from "./head-state.js";
17
+ /** A box the reader can see. A line folded away inside a closed run, or an
18
+ * element not laid out, measures 0×0 at the viewport's origin — anchoring on
19
+ * it would hold nothing still and land the reader at the top instead. */
20
+ function visibleBox(element) {
21
+ const box = element.getBoundingClientRect();
22
+ if (box.height > 0 || box.width > 0)
23
+ return box;
24
+ // A closed activity run is still the visible location of its events. After
25
+ // an earlier page joins it, anchor to that same run instead of the next chat.
26
+ const group = element.closest(".tr-activity__list[hidden]")?.parentElement;
27
+ if (group !== null && group !== undefined) {
28
+ const groupBox = group.getBoundingClientRect();
29
+ if (groupBox.height > 0)
30
+ return groupBox;
31
+ }
32
+ return null;
33
+ }
34
+ /** Where the row with this id is drawn now, skipping any hidden twin: one
35
+ * timeline id may be stamped on a turn wrapper AND on the line inside it, and
36
+ * after a prepend the turns re-split, so the first match in DOM order can be
37
+ * the folded one. */
38
+ function rowTop(scroller, id) {
39
+ const twins = scroller.querySelectorAll(`[data-timeline-id="${CSS.escape(id)}"]`);
40
+ for (const twin of twins) {
41
+ const box = visibleBox(twin);
42
+ if (box !== null)
43
+ return box.top;
44
+ }
45
+ return null;
46
+ }
47
+ function measureAnchor(scroller) {
48
+ const edge = scroller.getBoundingClientRect().top;
49
+ let id = null;
50
+ let top = 0;
51
+ // The first VISIBLE row whose bottom has not scrolled past the top edge:
52
+ // that is the one under the reader's eye, whatever the DOM order says.
53
+ for (const row of scroller.querySelectorAll("[data-timeline-id]")) {
54
+ const box = visibleBox(row);
55
+ if (box === null || box.bottom < edge)
56
+ continue;
57
+ id = row.dataset.timelineId ?? null;
58
+ top = box.top;
59
+ break;
60
+ }
61
+ return { id, top, scrollHeight: scroller.scrollHeight };
62
+ }
63
+ export function useHeadOfDocument(scrollerRef, feed, options = {}) {
64
+ const threshold = options.threshold ?? PULL_THRESHOLD_PX;
65
+ const decayMs = options.decayMs ?? PULL_DECAY_MS;
66
+ const frostRange = options.frostRange ?? FROST_RANGE_PX;
67
+ const receiptMs = options.receiptMs ?? RECEIPT_MS;
68
+ const enabled = options.enabled ?? true;
69
+ const [pull, setPull] = useState(0);
70
+ const [frost, setFrost] = useState(0);
71
+ const [receipt, setReceipt] = useState(null);
72
+ const pullRef = useRef(initialPull);
73
+ const anchorRef = useRef(null);
74
+ const frameRef = useRef(null);
75
+ const decayRef = useRef(null);
76
+ // The listeners fire between commits; they must never close over a stale
77
+ // feed. One ref, refreshed every render, is the whole trick.
78
+ const feedRef = useRef(feed);
79
+ feedRef.current = feed;
80
+ const setPullModel = useCallback((next) => {
81
+ pullRef.current = next;
82
+ setPull(pullProgress(next, threshold));
83
+ }, [threshold]);
84
+ // ── the landing ───────────────────────────────────────────────────────────
85
+ // Declared FIRST so it reads the anchor cached by the commit BEFORE the
86
+ // prepend; the refresher below then overwrites it for the next landing.
87
+ const landSeq = feed.landed?.seq ?? null;
88
+ useLayoutEffect(() => {
89
+ if (landSeq === null)
90
+ return;
91
+ const scroller = scrollerRef.current;
92
+ const before = anchorRef.current;
93
+ if (scroller === null || before === null)
94
+ return;
95
+ const top = before.id === null ? null : rowTop(scroller, before.id);
96
+ // The row that was under the reader's eye stays exactly where it was.
97
+ // Chromium suppresses scroll anchoring at scroll position zero — precisely
98
+ // where this fires — so the prepend adds the difference itself.
99
+ const delta = top !== null ? top - before.top : scroller.scrollHeight - before.scrollHeight;
100
+ if (delta !== 0)
101
+ scroller.scrollTop += delta;
102
+ anchorRef.current = measureAnchor(scroller);
103
+ }, [landSeq, scrollerRef]);
104
+ // Keep the anchor fresh: every commit, and every scroll frame below.
105
+ useLayoutEffect(() => {
106
+ const scroller = scrollerRef.current;
107
+ if (scroller !== null)
108
+ anchorRef.current = measureAnchor(scroller);
109
+ });
110
+ // ── the receipt ───────────────────────────────────────────────────────────
111
+ const landed = feed.landed;
112
+ useEffect(() => {
113
+ if (landed === null || landed.count <= 0) {
114
+ // A session change drops the landing; a stale receipt must go with it.
115
+ setReceipt(null);
116
+ return;
117
+ }
118
+ setReceipt({ count: landed.count, seq: landed.seq });
119
+ const timer = window.setTimeout(() => setReceipt(null), receiptMs);
120
+ return () => window.clearTimeout(timer);
121
+ }, [landed, receiptMs]);
122
+ // ── the listeners ─────────────────────────────────────────────────────────
123
+ useEffect(() => {
124
+ const scroller = scrollerRef.current;
125
+ if (scroller === null || !enabled)
126
+ return;
127
+ const clearDecay = () => {
128
+ if (decayRef.current !== null)
129
+ window.clearTimeout(decayRef.current);
130
+ decayRef.current = null;
131
+ };
132
+ const armDecay = () => {
133
+ clearDecay();
134
+ decayRef.current = window.setTimeout(() => {
135
+ decayRef.current = null;
136
+ setPullModel(initialPull);
137
+ }, decayMs);
138
+ };
139
+ /** The bar is spent. A held page lands now; a fetch in flight lands on
140
+ * arrival; and a pull that outran the approach starts the fetch itself. */
141
+ const spendBar = () => {
142
+ clearDecay();
143
+ setPullModel(initialPull);
144
+ feedRef.current.holdEarlier();
145
+ feedRef.current.revealEarlier();
146
+ };
147
+ /** Upward travel at the top: bank it, and fire at the threshold. */
148
+ const travel = (px) => {
149
+ const kind = feedRef.current.earlier.kind;
150
+ // A pull at the start does nothing, and the frame does not bounce.
151
+ if (kind === "none")
152
+ return;
153
+ const result = advancePull(pullRef.current, { travel: px, atTop: scroller.scrollTop <= 0, now: Date.now() }, { threshold, decayMs });
154
+ setPullModel(result.model);
155
+ if (result.fire) {
156
+ spendBar();
157
+ }
158
+ else if (result.model.travel > 0) {
159
+ armDecay();
160
+ }
161
+ else {
162
+ clearDecay();
163
+ }
164
+ };
165
+ const readScroll = () => {
166
+ frameRef.current = null;
167
+ const next = frostOf(scroller.scrollTop, frostRange);
168
+ scroller.style.setProperty("--frost", String(next));
169
+ setFrost(next);
170
+ anchorRef.current = measureAnchor(scroller);
171
+ if (scroller.scrollTop > 0 && pullRef.current.travel > 0) {
172
+ clearDecay();
173
+ setPullModel(initialPull);
174
+ }
175
+ // Within one viewport of the top the next page is requested and held.
176
+ // Nothing is prepended on approach.
177
+ if (scroller.scrollTop <= scroller.clientHeight &&
178
+ feedRef.current.earlier.kind === "idle") {
179
+ feedRef.current.holdEarlier();
180
+ }
181
+ };
182
+ const onScroll = () => {
183
+ if (frameRef.current !== null)
184
+ return;
185
+ frameRef.current = window.requestAnimationFrame(readScroll);
186
+ };
187
+ const onWheel = (event) => {
188
+ if (scroller.scrollTop > 0) {
189
+ if (pullRef.current.travel > 0) {
190
+ clearDecay();
191
+ setPullModel(initialPull);
192
+ }
193
+ return;
194
+ }
195
+ if (event.deltaY < 0) {
196
+ // Not passive: the frame contains its own overscroll, so the window
197
+ // never rubber-bands while the reader spends the bar (§08).
198
+ event.preventDefault();
199
+ travel(-event.deltaY);
200
+ }
201
+ else if (pullRef.current.travel > 0) {
202
+ clearDecay();
203
+ setPullModel(initialPull);
204
+ }
205
+ };
206
+ let touchY = null;
207
+ const onTouchStart = (event) => {
208
+ touchY = event.touches[0]?.clientY ?? null;
209
+ };
210
+ const onTouchMove = (event) => {
211
+ const y = event.touches[0]?.clientY;
212
+ if (touchY === null || y === undefined || scroller.scrollTop > 0)
213
+ return;
214
+ const dy = y - touchY;
215
+ touchY = y;
216
+ if (dy > 0)
217
+ travel(dy);
218
+ else if (dy < 0 && pullRef.current.travel > 0) {
219
+ clearDecay();
220
+ setPullModel(initialPull);
221
+ }
222
+ };
223
+ const onTouchEnd = () => {
224
+ touchY = null;
225
+ };
226
+ // Home, PageUp and ↑ at the top reveal on one press (§08).
227
+ const onKeyDown = (event) => {
228
+ if (event.key !== "Home" && event.key !== "PageUp" && event.key !== "ArrowUp")
229
+ return;
230
+ if (scroller.scrollTop > 0)
231
+ return;
232
+ const kind = feedRef.current.earlier.kind;
233
+ if (kind === "none" || kind === "revealing")
234
+ return;
235
+ event.preventDefault();
236
+ spendBar();
237
+ };
238
+ scroller.addEventListener("scroll", onScroll, { passive: true });
239
+ scroller.addEventListener("wheel", onWheel, { passive: false });
240
+ scroller.addEventListener("touchstart", onTouchStart, { passive: true });
241
+ scroller.addEventListener("touchmove", onTouchMove, { passive: true });
242
+ scroller.addEventListener("touchend", onTouchEnd, { passive: true });
243
+ scroller.addEventListener("touchcancel", onTouchEnd, { passive: true });
244
+ scroller.addEventListener("keydown", onKeyDown);
245
+ readScroll();
246
+ return () => {
247
+ scroller.removeEventListener("scroll", onScroll);
248
+ scroller.removeEventListener("wheel", onWheel);
249
+ scroller.removeEventListener("touchstart", onTouchStart);
250
+ scroller.removeEventListener("touchmove", onTouchMove);
251
+ scroller.removeEventListener("touchend", onTouchEnd);
252
+ scroller.removeEventListener("touchcancel", onTouchEnd);
253
+ scroller.removeEventListener("keydown", onKeyDown);
254
+ if (frameRef.current !== null)
255
+ window.cancelAnimationFrame(frameRef.current);
256
+ frameRef.current = null;
257
+ clearDecay();
258
+ scroller.style.removeProperty("--frost");
259
+ };
260
+ }, [scrollerRef, enabled, threshold, decayMs, frostRange, setPullModel]);
261
+ // After a landing the reader may still be within a viewport of the top: the
262
+ // record re-approaches immediately so the next page is already held.
263
+ const earlierKind = feed.earlier.kind;
264
+ useEffect(() => {
265
+ if (!enabled || earlierKind !== "idle")
266
+ return;
267
+ const scroller = scrollerRef.current;
268
+ if (scroller === null)
269
+ return;
270
+ if (scroller.scrollTop <= scroller.clientHeight)
271
+ feedRef.current.holdEarlier();
272
+ }, [enabled, earlierKind, landSeq, scrollerRef]);
273
+ return { pull, frost, receipt };
274
+ }