@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,71 @@
1
+ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useId, useLayoutEffect, useMemo, useRef, useState } from "react";
3
+ import { createDayActivity, createHourlyCurve, hourLabel } from "../../lib/day-activity.js";
4
+ import { tokenText } from "../../lib/format.js";
5
+ const hourX = (hour) => hour / 23 * 1000;
6
+ /** Smooth curves pass through the hourly samples on a shared token scale.
7
+ * Dashed future guides carry no estimated usage or gradient fill. */
8
+ export function DayActivityChart({ rows, lastHour = 23, modelColors }) {
9
+ const activity = useMemo(() => createDayActivity(rows, modelColors), [rows, modelColors]);
10
+ const chartId = useId().replace(/:/g, "");
11
+ const [hoverHour, setHoverHour] = useState(null);
12
+ const [focusedHour, setFocusedHour] = useState(null);
13
+ const [muted, setMuted] = useState(() => new Set());
14
+ const bodyRef = useRef(null);
15
+ const [legendHeight, setLegendHeight] = useState(400);
16
+ useLayoutEffect(() => {
17
+ const node = bodyRef.current;
18
+ if (!node)
19
+ return;
20
+ const measure = () => setLegendHeight(node.clientHeight);
21
+ measure();
22
+ const observer = new ResizeObserver(measure);
23
+ observer.observe(node);
24
+ return () => observer.disconnect();
25
+ }, []);
26
+ const modelsPerPage = Math.max(1, Math.min(6, Math.floor((legendHeight - 46) / 28)));
27
+ const [modelPage, setModelPage] = useState(0);
28
+ const pageCount = Math.max(1, Math.ceil(activity.series.length / modelsPerPage));
29
+ const page = Math.min(modelPage, pageCount - 1);
30
+ const legendModels = activity.series.slice(page * modelsPerPage, (page + 1) * modelsPerPage);
31
+ const futureHour = hoverHour !== null && hoverHour > lastHour ? hoverHour : null;
32
+ const inspectedHour = futureHour === null ? hoverHour ?? focusedHour : null;
33
+ const rawPeak = Math.max(1, ...activity.series.flatMap(model => model.values.slice(0, lastHour + 1)));
34
+ const power = 10 ** Math.floor(Math.log10(rawPeak));
35
+ const maximum = Math.ceil(rawPeak / power / 2) * power * 2;
36
+ const y = (tokens) => 220 - tokens / maximum * 220;
37
+ const paths = useMemo(() => activity.series.map((model, index) => {
38
+ const points = model.values.slice(0, lastHour + 1).map((tokens, hour) => [hourX(hour), 220 - tokens / maximum * 220]);
39
+ const line = createHourlyCurve(points);
40
+ return { ...model, line, gradientId: `${chartId}-model-${index}`,
41
+ area: `${line} L${hourX(lastHour)},220 L0,220 Z`,
42
+ future: `M${hourX(lastHour)},${points.at(-1)[1]} H1000` };
43
+ }), [activity.series, lastHour, maximum, chartId]);
44
+ const visibleSeries = paths.filter(model => !muted.has(model.id));
45
+ return _jsxs("div", { className: "ah-daychart", children: [_jsx("div", { className: "ah-daychart__readout", "aria-live": "polite", "aria-atomic": "true", children: futureHour !== null ? _jsxs(_Fragment, { children: [_jsxs("strong", { children: [hourLabel(futureHour), " \u2013 ", hourLabel((futureHour + 1) % 24)] }), _jsx("span", { children: "Awaiting usage" })] }) : inspectedHour !== null ? _jsxs(_Fragment, { children: [_jsxs("strong", { children: [hourLabel(inspectedHour), " \u2013 ", hourLabel((inspectedHour + 1) % 24)] }), _jsxs("span", { title: "Tokens across all models", children: [tokenText(activity.hours[inspectedHour]), " tokens"] })] })
46
+ : _jsxs(_Fragment, { children: [_jsx("strong", { children: activity.peakHour === null ? "No token activity" : `Busiest hour · ${hourLabel(activity.peakHour)}` }), _jsx("span", { title: "Tokens across all models", children: activity.peakHour === null ? "" : `${tokenText(activity.hours[activity.peakHour])} tokens` })] }) }), _jsxs("div", { className: "ah-daychart__body", ref: bodyRef, children: [_jsxs("div", { className: "ah-daychart__graph", children: [_jsx("span", { className: "ah-daychart__unit", title: "Includes input, output and cache tokens. Usage reported only as session totals appears at its report hour.", children: "Tokens / hour" }), _jsxs("div", { className: "ah-daychart__plot", children: [[0, 1, 2, 3, 4].map(tick => _jsx("div", { className: "ah-daychart__gridline", style: { top: `${tick * 25}%` }, children: _jsx("span", { children: tokenText(maximum * (4 - tick) / 4) }) }, tick)), lastHour < 23 && _jsx("div", { className: "ah-daychart__future", style: { left: `${(lastHour + .5) / 23 * 100}%` } }), activity.peakHour !== null && inspectedHour === null && _jsx("i", { className: "ah-daychart__peak", style: { left: `${activity.peakHour / 23 * 100}%` }, "aria-hidden": "true" }), _jsxs("svg", { viewBox: "0 0 1000 220", preserveAspectRatio: "none", tabIndex: 0, role: "slider", "aria-label": "Inspect hourly model usage. Use left and right arrow keys.", "aria-valuemin": 0, "aria-valuemax": lastHour, "aria-valuenow": inspectedHour ?? 0, "aria-valuetext": `${hourLabel(inspectedHour ?? 0)}: ${tokenText(activity.hours[inspectedHour ?? 0])} tokens`, "data-hour": inspectedHour ?? undefined, "data-future-hour": futureHour ?? undefined, onMouseMove: event => {
47
+ const rect = event.currentTarget.getBoundingClientRect();
48
+ const hour = Math.max(0, Math.min(23, Math.round((event.clientX - rect.left) / rect.width * 23)));
49
+ setHoverHour(hour);
50
+ }, onMouseLeave: () => setHoverHour(null), onFocus: () => setFocusedHour(0), onBlur: () => setFocusedHour(null), onKeyDown: event => {
51
+ if (!["ArrowLeft", "ArrowRight", "Home", "End"].includes(event.key))
52
+ return;
53
+ event.preventDefault();
54
+ setHoverHour(null);
55
+ setFocusedHour(hour => event.key === "Home" ? 0 : event.key === "End" ? lastHour
56
+ : Math.max(0, Math.min(lastHour, (hour ?? 0) + (event.key === "ArrowLeft" ? -1 : 1))));
57
+ }, children: [_jsx("title", { children: "Tokens by model for each hour of the selected day" }), _jsx("defs", { children: paths.map(model => _jsxs("linearGradient", { id: model.gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [_jsx("stop", { offset: "0%", stopColor: model.color, stopOpacity: ".24" }), _jsx("stop", { offset: "55%", stopColor: model.color, stopOpacity: ".09" }), _jsx("stop", { offset: "100%", stopColor: model.color, stopOpacity: "0" })] }, model.id)) }), lastHour > 0 && visibleSeries.map(model => _jsx("path", { "data-model-area": model.id, className: "ah-daychart__area", d: model.area, fill: `url(#${model.gradientId})` }, model.id)), visibleSeries.map(model => _jsx("path", { className: "ah-daychart__glow", d: model.line, fill: "none", stroke: model.color, strokeWidth: "6", vectorEffect: "non-scaling-stroke" }, model.id)), visibleSeries.map(model => _jsx("path", { "data-model-line": model.id, className: "ah-daychart__line", d: model.line, fill: "none", stroke: model.color, strokeWidth: "2.25", vectorEffect: "non-scaling-stroke" }, model.id)), lastHour < 23 && visibleSeries.map(model => _jsx("path", { "data-model-future": model.id, className: "ah-daychart__pending", d: model.future, fill: "none", stroke: model.color, strokeWidth: "1.5", vectorEffect: "non-scaling-stroke", "aria-hidden": "true" }, model.id)), (inspectedHour !== null || futureHour !== null) && _jsx("line", { x1: hourX(inspectedHour ?? futureHour), x2: hourX(inspectedHour ?? futureHour), y1: "0", y2: "220", className: "ah-daychart__crosshair", vectorEffect: "non-scaling-stroke", "aria-hidden": "true" })] }), (inspectedHour !== null || lastHour < 23 && futureHour === null) && visibleSeries.map(model => {
58
+ const hour = inspectedHour ?? lastHour;
59
+ return _jsx("i", { className: "ah-daychart__point", "aria-hidden": "true", style: {
60
+ left: `${hour / 23 * 100}%`, top: `${y(model.values[hour]) / 220 * 100}%`,
61
+ "--model-color": model.color,
62
+ } }, model.id);
63
+ }), lastHour < 23 && _jsx("span", { className: "ah-daychart__future-label", children: "Later today \u00B7 awaiting usage" }), _jsx("div", { className: "ah-daychart__axis", "aria-hidden": "true", children: [0, 6, 12, 18, 23].map(hour => _jsx("span", { style: { left: `${hour / 23 * 100}%` }, children: hourLabel(hour) }, hour)) })] })] }), _jsxs("div", { className: "ah-daychart__models", children: [_jsxs("div", { className: "ah-daychart__legendhead", children: [_jsx("span", { title: "Model share of the selected day's tokens. Click a model to show or hide its line.", children: "Models \u00B7 day share" }), _jsx("span", { children: futureHour !== null ? hourLabel(futureHour) : inspectedHour === null ? "Tokens" : hourLabel(inspectedHour) })] }), legendModels.map(model => _jsxs("button", { type: "button", "data-model-toggle": model.id, "aria-pressed": !muted.has(model.id), title: `${model.label}: ${model.total.toLocaleString()} tokens. Click to ${muted.has(model.id) ? "show" : "hide"} its line.`, style: { "--model-color": model.color }, onClick: () => setMuted(current => {
64
+ const next = new Set(current);
65
+ if (next.has(model.id))
66
+ next.delete(model.id);
67
+ else
68
+ next.add(model.id);
69
+ return next;
70
+ }), children: [_jsxs("span", { className: "ah-daychart__modelname", children: [_jsx("i", {}), _jsx("span", { children: model.label })] }), _jsx("b", { children: futureHour !== null ? "—" : tokenText(inspectedHour === null ? model.total : model.values[inspectedHour]) }), _jsx("span", { className: "ah-daychart__share", children: _jsx("i", { style: { width: `${model.total / activity.total * 100}%` } }) }), _jsxs("small", { children: [(model.total / activity.total * 100).toFixed(0), "%"] })] }, model.id)), pageCount > 1 && _jsxs("div", { className: "ah-daychart__modelnav", "aria-label": "Model pages", children: [_jsx("button", { type: "button", "aria-label": "Previous models", disabled: page === 0, onClick: () => setModelPage(page - 1), children: "\u2190" }), _jsxs("span", { children: [page + 1, "/", pageCount] }), _jsx("button", { type: "button", "aria-label": "Next models", disabled: page === pageCount - 1, onClick: () => setModelPage(page + 1), children: "\u2192" })] })] })] })] });
71
+ }
@@ -0,0 +1,7 @@
1
+ import type { UsageResponse } from "@vibefield/rosetta-protocol";
2
+ import "../../styles/models-chart.css";
3
+ export interface ModelsChartDetailProps {
4
+ readonly usage: UsageResponse | null;
5
+ readonly currentTimestamp: number;
6
+ }
7
+ export declare function ModelsChartDetail({ usage, currentTimestamp }: ModelsChartDetailProps): import("react").JSX.Element;
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useMemo } from "react";
3
+ import { tokenText } from "../../lib/format.js";
4
+ import { createModelUsageStreamgraphData } from "../../lib/chart-data.js";
5
+ import { Streamgraph } from "./Streamgraph.js";
6
+ import "../../styles/models-chart.css";
7
+ const dateFormatter = new Intl.DateTimeFormat("en-US", {
8
+ weekday: "short", month: "short", day: "numeric", year: "numeric", timeZone: "UTC",
9
+ });
10
+ function formatUsageDay(point) {
11
+ return dateFormatter.format(new Date(`${point.id}T12:00:00Z`));
12
+ }
13
+ export function ModelsChartDetail({ usage, currentTimestamp }) {
14
+ const data = useMemo(() => usage
15
+ ? createModelUsageStreamgraphData(usage, currentTimestamp, { groupOthers: false })
16
+ : { domain: [], series: [] }, [usage, currentTimestamp]);
17
+ return (_jsx("div", { className: "ah-models", "aria-busy": usage === null, children: usage === null ? _jsx("p", { className: "ah-models__loading", role: "status", children: "Loading model usage\u2026" })
18
+ : _jsx(Streamgraph, { data: data, expanded: true, title: "Tokens per day \u00B7 30 days", note: "no zero \u00B7 thickness is the total", ariaLabel: "Inspect daily tokens by model over 30 days", emptyMessage: "No model-attributed usage in this window", formatValue: tokenText, formatPoint: formatUsageDay, valueLabel: "tokens" }) }));
19
+ }
@@ -0,0 +1,19 @@
1
+ import type { UsageResponse } from "@vibefield/rosetta-protocol";
2
+ import type { ActivityChartDetailProps } from "./ActivityChartDetail.js";
3
+ import "../../styles/project-chart.css";
4
+ declare const VIEWS: readonly [{
5
+ readonly value: "activity";
6
+ readonly label: "Activity";
7
+ }, {
8
+ readonly value: "models";
9
+ readonly label: "Models";
10
+ }];
11
+ export type ProjectChartView = typeof VIEWS[number]["value"];
12
+ export interface ProjectChartDialogProps extends ActivityChartDetailProps {
13
+ readonly initialView: ProjectChartView;
14
+ readonly usage: UsageResponse | null;
15
+ readonly onClose: () => void;
16
+ }
17
+ /** One modal lifetime preserves the opener, fullscreen and both detail views. */
18
+ export declare function ProjectChartDialog({ initialView, usage, onClose, ...activity }: ProjectChartDialogProps): import("react").JSX.Element;
19
+ export {};
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useId, useRef, useState } from "react";
3
+ import { ColumnHeading, DialogShell } from "../ui/index.js";
4
+ import { CloseGlyph, ExitFullscreenGlyph, FullscreenGlyph } from "../../lib/icons.js";
5
+ import { ActivityChartDetail } from "./ActivityChartDetail.js";
6
+ import { ModelsChartDetail } from "./ModelsChartDetail.js";
7
+ import "../../styles/project-chart.css";
8
+ const VIEWS = [
9
+ { value: "activity", label: "Activity" },
10
+ { value: "models", label: "Models" },
11
+ ];
12
+ /** One modal lifetime preserves the opener, fullscreen and both detail views. */
13
+ export function ProjectChartDialog({ initialView, usage, onClose, ...activity }) {
14
+ const [fullscreen, setFullscreen] = useState(false);
15
+ const [view, setView] = useState(initialView);
16
+ const [visited, setVisited] = useState(() => new Set([initialView]));
17
+ const id = useId();
18
+ const tabs = useRef({});
19
+ const selectView = (next) => {
20
+ setView(next);
21
+ setVisited(current => current.has(next) ? current : new Set([...current, next]));
22
+ };
23
+ const navigateTabs = (event, current) => {
24
+ const index = VIEWS.findIndex(option => option.value === current);
25
+ const nextIndex = event.key === "Home" ? 0 : event.key === "End" ? VIEWS.length - 1
26
+ : event.key === "ArrowRight" ? (index + 1) % VIEWS.length
27
+ : event.key === "ArrowLeft" ? (index + VIEWS.length - 1) % VIEWS.length : null;
28
+ if (nextIndex === null)
29
+ return;
30
+ event.preventDefault();
31
+ const next = VIEWS[nextIndex].value;
32
+ selectView(next);
33
+ tabs.current[next]?.focus();
34
+ };
35
+ return _jsxs(DialogShell, { title: `${activity.name} project activity and models`, onClose: onClose, fullscreen: fullscreen, onExitFullscreen: () => setFullscreen(false), children: [_jsxs("header", { className: "ah-fileview__head ah-project-chart__head", children: [_jsx("div", { className: "ah-fileview__title", children: _jsx(ColumnHeading, { name: activity.name, tint: activity.color }) }), _jsx("div", { className: "ah-seg ah-project-chart__tabs", role: "tablist", "aria-label": "Project chart details", children: VIEWS.map(option => _jsx("button", { type: "button", role: "tab", ref: node => { if (node)
36
+ tabs.current[option.value] = node; }, id: `${id}-tab-${option.value}`, "aria-controls": `${id}-panel-${option.value}`, "aria-selected": view === option.value, tabIndex: view === option.value ? 0 : -1, className: view === option.value ? "is-on" : undefined, onClick: () => selectView(option.value), onKeyDown: event => navigateTabs(event, option.value), children: option.label }, option.value)) }), _jsxs("div", { className: "ah-fileview__acts", children: [_jsx("button", { className: "ah-iconbtn", type: "button", "aria-pressed": fullscreen, title: fullscreen ? "Exit full screen (Esc)" : "View full screen", "aria-label": fullscreen ? "Exit full screen" : "View full screen", onClick: () => setFullscreen(current => !current), children: fullscreen ? _jsx(ExitFullscreenGlyph, {}) : _jsx(FullscreenGlyph, {}) }), _jsx("button", { className: "ah-iconbtn", type: "button", title: "Close (Esc)", "aria-label": "Close project charts", onClick: onClose, children: _jsx(CloseGlyph, {}) })] })] }), VIEWS.map(option => _jsx("div", { className: "ah-project-chart__panel", role: "tabpanel", id: `${id}-panel-${option.value}`, "aria-labelledby": `${id}-tab-${option.value}`, hidden: view !== option.value, children: visited.has(option.value) && (option.value === "activity"
37
+ ? _jsx(ActivityChartDetail, { ...activity })
38
+ : _jsx(ModelsChartDetail, { usage: usage, currentTimestamp: activity.currentTimestamp })) }, option.value))] });
39
+ }
@@ -0,0 +1,16 @@
1
+ import type { StreamgraphData, StreamgraphDomainPointData } from "./types.js";
2
+ export interface StreamgraphProps {
3
+ readonly data: StreamgraphData;
4
+ readonly title: string;
5
+ readonly ariaLabel: string;
6
+ readonly note?: string;
7
+ readonly emptyMessage?: string;
8
+ readonly width?: number;
9
+ readonly height?: number;
10
+ /** Enlarges the presentation without changing the data or waveform. */
11
+ readonly expanded?: boolean;
12
+ readonly formatValue?: (value: number) => string;
13
+ readonly formatPoint?: (point: StreamgraphDomainPointData) => string;
14
+ readonly valueLabel?: string;
15
+ }
16
+ export declare function Streamgraph({ data, title, ariaLabel, note, emptyMessage, width, height, expanded, formatValue, formatPoint, valueLabel, }: StreamgraphProps): import("react").JSX.Element;
@@ -0,0 +1,94 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useId, useMemo, useState } from "react";
3
+ import { createStreamgraphLayout } from "./streamgraph-layout.js";
4
+ const DEFAULT_VIEWBOX_WIDTH = 276;
5
+ const DEFAULT_VIEWBOX_HEIGHT = 60;
6
+ function finiteMagnitude(value) {
7
+ return Number.isFinite(value) ? Math.max(0, value ?? 0) : 0;
8
+ }
9
+ function defaultValueFormatter(value) {
10
+ return value.toLocaleString();
11
+ }
12
+ function defaultPointFormatter(point) {
13
+ return point.label ?? point.id;
14
+ }
15
+ function formatShare(value, total) {
16
+ if (total <= 0)
17
+ return "—";
18
+ const share = value / total * 100;
19
+ return value > 0 && share < 0.1 ? "<0.1%" : `${share.toFixed(1)}%`;
20
+ }
21
+ export function Streamgraph({ data, title, ariaLabel, note, emptyMessage = "No data in this period", width = DEFAULT_VIEWBOX_WIDTH, height = DEFAULT_VIEWBOX_HEIGHT, expanded = false, formatValue = defaultValueFormatter, formatPoint = defaultPointFormatter, valueLabel = "value", }) {
22
+ const gradientId = useId().replace(/:/g, "");
23
+ const [hoveredPoint, setHoveredPoint] = useState(null);
24
+ const [focusedPoint, setFocusedPoint] = useState(null);
25
+ const viewBoxWidth = Number.isFinite(width) && width > 0 ? width : DEFAULT_VIEWBOX_WIDTH;
26
+ const viewBoxHeight = Number.isFinite(height) && height > 0 ? height : DEFAULT_VIEWBOX_HEIGHT;
27
+ const visibleSeries = useMemo(() => {
28
+ return data.series
29
+ .map((series) => {
30
+ const values = data.domain.map((_, index) => finiteMagnitude(series.values[index]));
31
+ return {
32
+ ...series,
33
+ values,
34
+ total: values.reduce((total, value) => total + value, 0),
35
+ };
36
+ })
37
+ .filter((series) => series.total > 0);
38
+ }, [data]);
39
+ const layerPaths = useMemo(() => createStreamgraphLayout(visibleSeries, data.domain.length, {
40
+ width: viewBoxWidth,
41
+ height: viewBoxHeight,
42
+ }), [data.domain.length, viewBoxHeight, viewBoxWidth, visibleSeries]);
43
+ const seriesById = useMemo(() => new Map(visibleSeries.map((series) => [series.id, series])), [visibleSeries]);
44
+ const hasData = layerPaths.length > 0;
45
+ const pointIndices = useMemo(() => new Map(data.domain.map((point, index) => [point.id, index])), [data.domain]);
46
+ const inspectedIndex = expanded && hasData
47
+ ? pointIndices.get(hoveredPoint ?? "") ?? pointIndices.get(focusedPoint ?? "") ?? null
48
+ : null;
49
+ const inspectedPoint = inspectedIndex === null ? null : data.domain[inspectedIndex];
50
+ const pointValues = inspectedIndex === null ? [] : visibleSeries.map(series => ({
51
+ ...series, value: series.values[inspectedIndex],
52
+ }));
53
+ const pointTotal = pointValues.reduce((total, series) => total + series.value, 0);
54
+ const inspectedX = inspectedIndex === null ? 0 : inspectedIndex / (data.domain.length - 1) * 100;
55
+ const accessibleIndex = inspectedIndex ?? 0;
56
+ const accessibleTotal = visibleSeries.reduce((total, series) => total + (series.values[accessibleIndex] ?? 0), 0);
57
+ const accessibleValue = hasData ? `${formatPoint(data.domain[accessibleIndex])}: ${Math.round(accessibleTotal).toLocaleString()} ${valueLabel}. ${visibleSeries.map(series => `${series.label}: ${Math.round(series.values[accessibleIndex]).toLocaleString()}`).join(", ")}` : undefined;
58
+ const inspectPointer = (svg, clientX) => {
59
+ const rect = svg.getBoundingClientRect();
60
+ if (rect.width <= 0)
61
+ return;
62
+ const index = Math.max(0, Math.min(data.domain.length - 1, Math.round((clientX - rect.left) / rect.width * (data.domain.length - 1))));
63
+ setFocusedPoint(null);
64
+ setHoveredPoint(data.domain[index].id);
65
+ };
66
+ return (_jsxs("div", { className: "ah-streamgraph", "data-expanded": expanded ? "" : undefined, style: expanded ? { "--series-glow-strength": Math.min(1, 4 / visibleSeries.length) } : undefined, children: [_jsxs("div", { className: "ah-chart-header", children: [_jsx("p", { className: "ah-chart-header__title", children: title }), note && _jsx("span", { className: "ah-chart-header__note", children: note })] }), expanded && hasData && _jsxs("p", { id: `${gradientId}-help`, className: "ah-visually-hidden", children: ["Use left and right arrow keys to inspect each recorded point. Home and End select the first and last points. Band thickness represents ", valueLabel, "; vertical position does not represent a value."] }), _jsxs("div", { className: "ah-streamgraph__plot", onPointerLeave: expanded ? () => setHoveredPoint(null) : undefined, children: [hasData ? (_jsxs("svg", { viewBox: `0 0 ${viewBoxWidth} ${viewBoxHeight}`, preserveAspectRatio: "none", role: expanded ? "slider" : "img", tabIndex: expanded ? 0 : undefined, "aria-label": ariaLabel, "aria-describedby": expanded ? `${gradientId}-help` : undefined, "aria-orientation": expanded ? "horizontal" : undefined, "aria-valuemin": expanded ? 0 : undefined, "aria-valuemax": expanded ? data.domain.length - 1 : undefined, "aria-valuenow": expanded ? accessibleIndex : undefined, "aria-valuetext": expanded ? accessibleValue : undefined, "data-inspected-point": inspectedPoint?.id, onPointerMove: expanded ? event => inspectPointer(event.currentTarget, event.clientX) : undefined, onPointerDown: expanded ? event => {
67
+ // Pointer inspection should not move keyboard focus or pin its guide.
68
+ event.preventDefault();
69
+ inspectPointer(event.currentTarget, event.clientX);
70
+ } : undefined, onFocus: expanded ? () => {
71
+ setHoveredPoint(null);
72
+ setFocusedPoint(data.domain[0].id);
73
+ } : undefined, onBlur: expanded ? () => setFocusedPoint(null) : undefined, onKeyDown: expanded ? event => {
74
+ if (!["ArrowLeft", "ArrowRight", "Home", "End"].includes(event.key))
75
+ return;
76
+ event.preventDefault();
77
+ const index = event.key === "Home" ? 0 : event.key === "End" ? data.domain.length - 1
78
+ : Math.max(0, Math.min(data.domain.length - 1, accessibleIndex + (event.key === "ArrowLeft" ? -1 : 1)));
79
+ setHoveredPoint(null);
80
+ setFocusedPoint(data.domain[index].id);
81
+ } : undefined, children: [expanded && (_jsx("defs", { children: visibleSeries.map((series, index) => (_jsxs("linearGradient", { id: `${gradientId}-${index}`, x1: "0", y1: "0", x2: "0", y2: "1", children: [_jsx("stop", { offset: "0%", stopColor: series.color, stopOpacity: "0.85" }), _jsx("stop", { offset: "100%", stopColor: series.color, stopOpacity: "0.25" })] }, series.id))) })), expanded && layerPaths.map(({ seriesId, path }) => (_jsx("path", { className: "ah-streamgraph__glow", d: path, fill: "none", stroke: seriesById.get(seriesId)?.color, strokeWidth: "4", vectorEffect: "non-scaling-stroke", "aria-hidden": "true" }, `glow-${seriesId}`))), layerPaths.map(({ seriesId, path }, index) => {
82
+ const series = seriesById.get(seriesId);
83
+ return series ? (_jsx("path", { "data-streamgraph-layer": seriesId, d: path, fill: expanded ? `url(#${gradientId}-${index})` : series.color, children: !expanded && _jsxs("title", { children: [series.label, " \u00B7 ", formatValue(Math.round(series.total))] }) }, seriesId)) : null;
84
+ }), expanded && layerPaths.map(({ seriesId, path }) => (_jsx("path", { className: "ah-streamgraph__outline", d: path, fill: "none", stroke: seriesById.get(seriesId)?.color, strokeWidth: "1.25", vectorEffect: "non-scaling-stroke", "aria-hidden": "true" }, `outline-${seriesId}`))), inspectedIndex !== null && _jsx("line", { className: "ah-streamgraph__crosshair", x1: inspectedX / 100 * viewBoxWidth, x2: inspectedX / 100 * viewBoxWidth, y1: "0", y2: viewBoxHeight, vectorEffect: "non-scaling-stroke", "aria-hidden": "true" })] })) : (_jsx("p", { className: "ah-streamgraph__empty", children: emptyMessage })), inspectedIndex !== null && inspectedPoint && _jsxs(_Fragment, { children: [layerPaths.map(({ seriesId, samples }) => {
85
+ const series = seriesById.get(seriesId);
86
+ if (series.values[inspectedIndex] <= 0)
87
+ return null;
88
+ const [x, y] = samples[inspectedIndex];
89
+ return _jsx("i", { className: "ah-streamgraph__point", "data-streamgraph-point": seriesId, "aria-hidden": "true", style: { left: `${x / viewBoxWidth * 100}%`, top: `${y / viewBoxHeight * 100}%`,
90
+ "--series-color": series.color } }, seriesId);
91
+ }), _jsxs("div", { className: "ah-streamgraph__readout", role: "tooltip", tabIndex: -1, "data-point": inspectedPoint.id, onPointerEnter: () => setHoveredPoint(inspectedPoint.id), "data-side": inspectedX > 50 ? "left" : "right", style: { "--inspect-x": `${inspectedX}%` }, children: [_jsxs("div", { className: "ah-streamgraph__readout-head", children: [_jsx("strong", { children: formatPoint(inspectedPoint) }), _jsxs("span", { children: ["Total ", _jsx("b", { "data-point-total": pointTotal, children: formatValue(Math.round(pointTotal)) }), " ", valueLabel] })] }), _jsx("div", { className: "ah-streamgraph__readout-rows", tabIndex: -1, children: pointValues.map(series => _jsxs("div", { "data-point-series": series.id, style: { "--series-color": series.color }, children: [_jsxs("span", { className: "ah-streamgraph__readout-model", children: [_jsx("i", { "aria-hidden": "true" }), series.label] }), _jsx("b", { "data-point-value": series.value, title: `${Math.round(series.value).toLocaleString()} ${valueLabel}`, children: formatValue(Math.round(series.value)) }), _jsx("small", { children: formatShare(series.value, pointTotal) })] }, series.id)) }), pointTotal === 0 && _jsx("p", { children: "No activity recorded" })] })] })] }), hasData && (_jsxs(_Fragment, { children: [_jsx("div", { className: "ah-streamgraph__axis", "aria-hidden": "true", children: data.domain.map((point, pointIndex) => point.label ? (_jsx("span", { "data-first": pointIndex === 0 ? "" : undefined, "data-last": pointIndex === data.domain.length - 1 ? "" : undefined, style: {
92
+ left: `${((pointIndex / (data.domain.length - 1)) * 100).toFixed(2)}%`,
93
+ }, children: point.label }, point.id)) : null) }), _jsx("div", { className: "ah-streamgraph__legend", tabIndex: expanded ? 0 : undefined, role: expanded ? "group" : undefined, "aria-label": expanded ? "Model totals for the 30-day period" : undefined, children: visibleSeries.map((series) => (_jsxs("span", { "data-streamgraph-series": series.id, style: { "--series-color": series.color }, children: [_jsx("i", { "aria-hidden": "true" }), _jsx("em", { title: series.id, children: series.label }), " ", _jsx("b", { title: Math.round(series.total).toLocaleString(), children: formatValue(Math.round(series.total)) })] }, series.id))) })] }))] }));
94
+ }
@@ -0,0 +1,26 @@
1
+ import type { TreemapData, TreemapItemData } from "./types.js";
2
+ export interface TreemapItemContext {
3
+ readonly share: number;
4
+ readonly isMinimumSized: boolean;
5
+ readonly isLabelVisible: boolean;
6
+ }
7
+ export interface TreemapItemNoun {
8
+ readonly singular: string;
9
+ readonly plural: string;
10
+ }
11
+ export interface TreemapProps {
12
+ readonly data: TreemapData;
13
+ readonly ariaLabel: string;
14
+ readonly title?: string;
15
+ readonly selectedItemId?: string | null;
16
+ readonly onItemSelect?: (item: TreemapItemData) => void;
17
+ readonly note?: string;
18
+ readonly showFooter?: boolean;
19
+ readonly emptyMessage?: string;
20
+ readonly itemNoun?: TreemapItemNoun;
21
+ readonly height?: number;
22
+ readonly minimumVisibleShare?: number;
23
+ readonly formatValue?: (value: number) => string;
24
+ readonly getItemAccessibleLabel?: (item: TreemapItemData, context: TreemapItemContext) => string;
25
+ }
26
+ export declare function Treemap({ data, title, ariaLabel, selectedItemId, onItemSelect, note, showFooter, emptyMessage, itemNoun, height, minimumVisibleShare, formatValue, getItemAccessibleLabel, }: TreemapProps): import("react").JSX.Element;
@@ -0,0 +1,81 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useLayoutEffect, useMemo, useRef, useState } from "react";
3
+ import { createTreemapLayout } from "./treemap-layout.js";
4
+ const DEFAULT_LAYOUT_WIDTH = 218;
5
+ const DEFAULT_HEIGHT = 110;
6
+ function defaultValueFormatter(value) {
7
+ return value.toLocaleString();
8
+ }
9
+ function countLabel(count, { singular, plural }) {
10
+ return `${count} ${count === 1 ? singular : plural}`;
11
+ }
12
+ function percentage(value, total) {
13
+ return `${((value / total) * 100).toFixed(3)}%`;
14
+ }
15
+ export function Treemap({ data, title, ariaLabel, selectedItemId = null, onItemSelect, note, showFooter = true, emptyMessage = "No data in this period", itemNoun = { singular: "item", plural: "items" }, height = DEFAULT_HEIGHT, minimumVisibleShare, formatValue = defaultValueFormatter, getItemAccessibleLabel, }) {
16
+ const chartHeight = Number.isFinite(height) && height > 0 ? height : DEFAULT_HEIGHT;
17
+ const containerRef = useRef(null);
18
+ const [containerWidth, setContainerWidth] = useState(DEFAULT_LAYOUT_WIDTH);
19
+ useLayoutEffect(() => {
20
+ const container = containerRef.current;
21
+ if (!container)
22
+ return;
23
+ const updateWidth = (nextWidth) => {
24
+ if (!Number.isFinite(nextWidth) || nextWidth <= 0)
25
+ return;
26
+ setContainerWidth((currentWidth) => Math.abs(currentWidth - nextWidth) < 0.5 ? currentWidth : nextWidth);
27
+ };
28
+ updateWidth(container.getBoundingClientRect().width);
29
+ if (typeof ResizeObserver === "undefined")
30
+ return;
31
+ const observer = new ResizeObserver(([entry]) => {
32
+ if (entry)
33
+ updateWidth(entry.contentRect.width);
34
+ });
35
+ observer.observe(container);
36
+ return () => observer.disconnect();
37
+ }, []);
38
+ const layout = useMemo(() => createTreemapLayout(data.items, {
39
+ width: containerWidth,
40
+ height: chartHeight,
41
+ minimumVisibleShare,
42
+ }), [chartHeight, containerWidth, data, minimumVisibleShare]);
43
+ const caveats = [];
44
+ if (layout.hiddenLabelCount > 0) {
45
+ caveats.push(`${layout.hiddenLabelCount} labels hidden`);
46
+ }
47
+ if (layout.minimumSizedItemCount > 0) {
48
+ caveats.push(`${layout.minimumSizedItemCount} minimum-sized`);
49
+ }
50
+ const omittedItemCount = data.items.length - layout.tiles.length;
51
+ if (omittedItemCount > 0) {
52
+ caveats.push(`${omittedItemCount} without area`);
53
+ }
54
+ const layoutNote = caveats.length > 0
55
+ ? caveats.join(" · ")
56
+ : layout.tiles.length > 0
57
+ ? "every tile true to scale"
58
+ : "no positive values";
59
+ return (_jsxs("div", { className: "ah-treemap", children: [(title || note) && (_jsxs("div", { className: "ah-chart-header", children: [title && _jsx("p", { className: "ah-chart-header__title", children: title }), note && _jsx("span", { className: "ah-chart-header__note", children: note })] })), _jsx("div", { ref: containerRef, className: "ah-treemap__plot", style: { height: chartHeight }, role: "group", "aria-label": ariaLabel, children: layout.tiles.length === 0 ? (_jsx("p", { className: "ah-treemap__empty", children: emptyMessage })) : (layout.tiles.map(({ item, rectangle, share, isMinimumSized, canShowLabel }) => {
60
+ const context = {
61
+ share,
62
+ isMinimumSized,
63
+ isLabelVisible: canShowLabel,
64
+ };
65
+ const valueText = `${formatValue(item.value)}${item.partial ? "+" : ""}`;
66
+ const accessibleLabel = getItemAccessibleLabel
67
+ ? getItemAccessibleLabel(item, context)
68
+ : `${item.label} · ${valueText} · ${(share * 100).toFixed(1)}% of total${item.partial ? " · a lower bound" : ""}${isMinimumSized ? " · shown at the minimum visible size" : ""}`;
69
+ const isSelected = item.id === selectedItemId;
70
+ const className = `ah-treemap__item${isSelected ? " is-selected" : ""}`;
71
+ const itemStyle = {
72
+ "--treemap-item-color": item.color,
73
+ left: percentage(rectangle.x, containerWidth),
74
+ top: percentage(rectangle.y, chartHeight),
75
+ width: percentage(rectangle.width, containerWidth),
76
+ height: percentage(rectangle.height, chartHeight),
77
+ };
78
+ const itemContent = (_jsx("span", { className: "ah-treemap__item-content", children: canShowLabel && (_jsxs(_Fragment, { children: [_jsx("b", { children: item.label }), _jsx("em", { children: valueText })] })) }));
79
+ return onItemSelect ? (_jsx("button", { type: "button", className: className, style: itemStyle, "aria-label": accessibleLabel, "aria-pressed": isSelected, title: accessibleLabel, onClick: () => onItemSelect(item), children: itemContent }, item.id)) : (_jsx("div", { className: className, style: itemStyle, role: "img", "aria-label": accessibleLabel, title: accessibleLabel, children: itemContent }, item.id));
80
+ })) }), showFooter && (_jsxs("div", { className: "ah-treemap__footer", children: [_jsx("span", { children: countLabel(data.items.length, itemNoun) }), _jsx("span", { children: layoutNote })] }))] }));
81
+ }
@@ -0,0 +1,9 @@
1
+ export { CalendarHeatmap } from "./CalendarHeatmap.js";
2
+ export type { CalendarHeatmapProps } from "./CalendarHeatmap.js";
3
+ export { ProjectChartDialog } from "./ProjectChartDialog.js";
4
+ export type { ProjectChartDialogProps, ProjectChartView } from "./ProjectChartDialog.js";
5
+ export { Streamgraph } from "./Streamgraph.js";
6
+ export type { StreamgraphProps } from "./Streamgraph.js";
7
+ export { Treemap } from "./Treemap.js";
8
+ export type { TreemapItemContext, TreemapItemNoun, TreemapProps } from "./Treemap.js";
9
+ export type { CalendarHeatmapCellData, CalendarHeatmapData, CalendarHeatmapWeekData, StreamgraphData, StreamgraphDomainPointData, StreamgraphSeriesData, TreemapData, TreemapItemData, } from "./types.js";
@@ -0,0 +1,4 @@
1
+ export { CalendarHeatmap } from "./CalendarHeatmap.js";
2
+ export { ProjectChartDialog } from "./ProjectChartDialog.js";
3
+ export { Streamgraph } from "./Streamgraph.js";
4
+ export { Treemap } from "./Treemap.js";
@@ -0,0 +1,23 @@
1
+ type Point = readonly [x: number, y: number];
2
+ export interface StreamgraphLayerInput {
3
+ id: string;
4
+ values: readonly number[];
5
+ }
6
+ export interface StreamgraphLayerPath {
7
+ seriesId: string;
8
+ path: string;
9
+ /** Sample centers use the same stacked boundaries as the rendered band. */
10
+ samples: readonly Point[];
11
+ }
12
+ export interface StreamgraphLayoutOptions {
13
+ width: number;
14
+ height: number;
15
+ verticalPadding?: number;
16
+ }
17
+ /**
18
+ * Builds one closed SVG path per series, preserving the order of `layers`.
19
+ * Values are normalised defensively so malformed runtime data cannot produce
20
+ * `NaN` path commands.
21
+ */
22
+ export declare function createStreamgraphLayout(layers: readonly StreamgraphLayerInput[], sampleCount: number, { width, height, verticalPadding }: StreamgraphLayoutOptions): StreamgraphLayerPath[];
23
+ export {};
@@ -0,0 +1,114 @@
1
+ function finiteMagnitude(value) {
2
+ return Number.isFinite(value) ? Math.max(0, value ?? 0) : 0;
3
+ }
4
+ /**
5
+ * Catmull-Rom interpolation expressed as cubic Bézier commands. The curve
6
+ * passes through every supplied point, so smoothing does not discard samples.
7
+ */
8
+ function createSmoothCurve(points, initialCommand) {
9
+ if (points.length === 0)
10
+ return "";
11
+ // Every point read here exists: the first is guarded above, and the loop
12
+ // runs while index + 1 < points.length, clamping the previous point at 0.
13
+ const pointAt = (index) => points[index];
14
+ let path = `${initialCommand}${pointAt(0)[0].toFixed(1)} ${pointAt(0)[1].toFixed(1)}`;
15
+ for (let index = 0; index < points.length - 1; index += 1) {
16
+ const previous = pointAt(index > 0 ? index - 1 : 0);
17
+ const current = pointAt(index);
18
+ const next = pointAt(index + 1);
19
+ const following = points[index + 2] ?? next;
20
+ path +=
21
+ `C${(current[0] + (next[0] - previous[0]) / 6).toFixed(1)} ` +
22
+ `${(current[1] + (next[1] - previous[1]) / 6).toFixed(1)},` +
23
+ `${(next[0] - (following[0] - current[0]) / 6).toFixed(1)} ` +
24
+ `${(next[1] - (following[1] - current[1]) / 6).toFixed(1)},` +
25
+ `${next[0].toFixed(1)} ${next[1].toFixed(1)}`;
26
+ }
27
+ return path;
28
+ }
29
+ /** Byron and Wattenberg's wiggle-minimising streamgraph baseline. */
30
+ function calculateWiggleBaseline(layers) {
31
+ const sampleCount = layers[0]?.length ?? 0;
32
+ if (sampleCount === 0)
33
+ return [];
34
+ // Every read here exists: each loop stays below its own bound, every layer
35
+ // holds sampleCount samples (the layout builds them at one length),
36
+ // sampleIndex starts at 1, and the baseline has sampleIndex entries by the
37
+ // time it is read.
38
+ const layerAt = (index) => layers[index];
39
+ const sampleAt = (values, index) => values[index];
40
+ const baseline = [0];
41
+ for (let sampleIndex = 1; sampleIndex < sampleCount; sampleIndex += 1) {
42
+ let totalAtSample = 0;
43
+ let weightedSlope = 0;
44
+ for (let layerIndex = 0; layerIndex < layers.length; layerIndex += 1) {
45
+ const layer = layerAt(layerIndex);
46
+ const value = sampleAt(layer, sampleIndex);
47
+ let slope = (value - sampleAt(layer, sampleIndex - 1)) / 2;
48
+ for (let earlierLayerIndex = 0; earlierLayerIndex < layerIndex; earlierLayerIndex += 1) {
49
+ const earlierLayer = layerAt(earlierLayerIndex);
50
+ slope += sampleAt(earlierLayer, sampleIndex) - sampleAt(earlierLayer, sampleIndex - 1);
51
+ }
52
+ totalAtSample += value;
53
+ weightedSlope += slope * value;
54
+ }
55
+ baseline.push(sampleAt(baseline, sampleIndex - 1) -
56
+ (totalAtSample > 0 ? weightedSlope / totalAtSample : 0));
57
+ }
58
+ return baseline;
59
+ }
60
+ /**
61
+ * Builds one closed SVG path per series, preserving the order of `layers`.
62
+ * Values are normalised defensively so malformed runtime data cannot produce
63
+ * `NaN` path commands.
64
+ */
65
+ export function createStreamgraphLayout(layers, sampleCount, { width, height, verticalPadding = 2 }) {
66
+ const safeSampleCount = Number.isFinite(sampleCount)
67
+ ? Math.max(0, Math.floor(sampleCount))
68
+ : 0;
69
+ if (layers.length === 0 ||
70
+ safeSampleCount < 2 ||
71
+ !Number.isFinite(width) ||
72
+ !Number.isFinite(height) ||
73
+ width <= 0 ||
74
+ height <= 0) {
75
+ return [];
76
+ }
77
+ const valuesByLayer = layers.map((layer) => Array.from({ length: safeSampleCount }, (_, index) => finiteMagnitude(layer.values[index])));
78
+ const baseline = calculateWiggleBaseline(valuesByLayer);
79
+ const boundaries = [baseline];
80
+ for (const values of valuesByLayer) {
81
+ const previousBoundary = boundaries[boundaries.length - 1];
82
+ boundaries.push(previousBoundary.map((value, index) => value + values[index]));
83
+ }
84
+ let minimum = Number.POSITIVE_INFINITY;
85
+ let maximum = Number.NEGATIVE_INFINITY;
86
+ for (const boundary of boundaries) {
87
+ for (const value of boundary) {
88
+ minimum = Math.min(minimum, value);
89
+ maximum = Math.max(maximum, value);
90
+ }
91
+ }
92
+ const span = maximum - minimum || 1;
93
+ const safeVerticalPadding = Number.isFinite(verticalPadding) ? verticalPadding : 0;
94
+ const padding = Math.min(Math.max(0, safeVerticalPadding), height / 2);
95
+ const drawableHeight = Math.max(0, height - padding * 2);
96
+ const xPosition = (index) => (index / (safeSampleCount - 1)) * width;
97
+ const yPosition = (value) => height - ((value - minimum) / span) * drawableHeight - padding;
98
+ return layers.map((layer, layerIndex) => {
99
+ const upperPoints = boundaries[layerIndex + 1].map((value, sampleIndex) => [
100
+ xPosition(sampleIndex),
101
+ yPosition(value),
102
+ ]);
103
+ const lowerPoints = boundaries[layerIndex]
104
+ .map((value, sampleIndex) => [xPosition(sampleIndex), yPosition(value)])
105
+ .reverse();
106
+ return {
107
+ seriesId: layer.id,
108
+ path: `${createSmoothCurve(upperPoints, "M")} ${createSmoothCurve(lowerPoints, "L")} Z`,
109
+ samples: upperPoints.map(([x, upperY], index) => [
110
+ x, (upperY + yPosition(boundaries[layerIndex][index])) / 2,
111
+ ]),
112
+ };
113
+ });
114
+ }
@@ -0,0 +1,32 @@
1
+ import type { TreemapItemData } from "./types.js";
2
+ export interface TreemapRectangle {
3
+ x: number;
4
+ y: number;
5
+ width: number;
6
+ height: number;
7
+ }
8
+ export interface TreemapTileLayout {
9
+ item: TreemapItemData;
10
+ rectangle: TreemapRectangle;
11
+ /** Exact share of the total before applying the minimum visible area. */
12
+ share: number;
13
+ isMinimumSized: boolean;
14
+ canShowLabel: boolean;
15
+ }
16
+ export interface TreemapLayout {
17
+ tiles: readonly TreemapTileLayout[];
18
+ hiddenLabelCount: number;
19
+ minimumSizedItemCount: number;
20
+ }
21
+ export interface TreemapLayoutOptions {
22
+ width: number;
23
+ height: number;
24
+ minimumVisibleShare?: number | undefined;
25
+ minimumLabelWidth?: number;
26
+ minimumLabelHeight?: number;
27
+ }
28
+ /**
29
+ * Sorts, normalises, and lays out treemap items. Zero and invalid values are
30
+ * omitted because they have no meaningful area.
31
+ */
32
+ export declare function createTreemapLayout(items: readonly TreemapItemData[], { width, height, minimumVisibleShare, minimumLabelWidth, minimumLabelHeight, }: TreemapLayoutOptions): TreemapLayout;