@heyramzi/ui 1.54.2 → 2.0.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 (363) hide show
  1. package/COMPONENTS.md +3 -2
  2. package/README.md +34 -9
  3. package/package.json +205 -13
  4. package/scripts/sync-shadcn.mjs +2 -0
  5. package/src/ai/generation-progress/GenerationProgress.svelte +12 -12
  6. package/src/ambient.d.ts +8 -0
  7. package/src/brand/brand.test.ts +67 -0
  8. package/src/brand/index.ts +137 -0
  9. package/src/canvas/README.md +236 -0
  10. package/src/canvas/card/card-frame.svelte +61 -0
  11. package/src/canvas/card/card.css +39 -0
  12. package/src/canvas/card/resize-handles.svelte +119 -0
  13. package/src/canvas/card/resize.ts +33 -0
  14. package/src/canvas/chrome/chrome-zoom.svelte +120 -0
  15. package/src/canvas/chrome/chrome.css +231 -0
  16. package/src/canvas/chrome/lock-badge.svelte +18 -0
  17. package/src/canvas/chrome/side-handles.svelte +30 -0
  18. package/src/canvas/chrome/size-chip.svelte +45 -0
  19. package/src/canvas/chrome/snap-guides.svelte +69 -0
  20. package/src/canvas/components/icons/ToolIcon.svelte +30 -0
  21. package/src/canvas/components/shared/svg/PreviewSvg.svelte +67 -0
  22. package/src/canvas/components/shared/svg/ShapeOutline.svelte +76 -0
  23. package/src/canvas/components/shared/svg/StrokeNode.svelte +69 -0
  24. package/src/canvas/components/shared/svg/TextRule.svelte +37 -0
  25. package/src/canvas/edges/bend-handle.svelte +76 -0
  26. package/src/canvas/edges/edge-gradient.svelte +30 -0
  27. package/src/canvas/edges/edge-path.test.ts +58 -0
  28. package/src/canvas/edges/edge-path.ts +293 -0
  29. package/src/canvas/edges/gradient-edge.svelte +43 -0
  30. package/src/canvas/editor/context-menu.test.ts +65 -0
  31. package/src/canvas/editor/context-menu.ts +48 -0
  32. package/src/canvas/editor/dispatch.svelte.test.ts +243 -0
  33. package/src/canvas/editor/dispatch.svelte.ts +205 -0
  34. package/src/canvas/editor/editor-context.ts +62 -0
  35. package/src/canvas/editor/editor-mode.ts +67 -0
  36. package/src/canvas/editor/frame.ts +98 -0
  37. package/src/canvas/editor/hand-context.ts +15 -0
  38. package/src/canvas/editor/history-core.test.ts +239 -0
  39. package/src/canvas/editor/history-core.ts +247 -0
  40. package/src/canvas/editor/keymap.test.ts +161 -0
  41. package/src/canvas/editor/keymap.ts +151 -0
  42. package/src/canvas/editor/layout-actions.test.ts +156 -0
  43. package/src/canvas/editor/layout-actions.ts +219 -0
  44. package/src/canvas/editor/lock.test.ts +68 -0
  45. package/src/canvas/editor/lock.ts +87 -0
  46. package/src/canvas/editor/rebase.test.ts +208 -0
  47. package/src/canvas/editor/rebase.ts +272 -0
  48. package/src/canvas/editor/style.ts +184 -0
  49. package/src/canvas/editor/text-field-context.ts +41 -0
  50. package/src/canvas/editor/z-order.test.ts +75 -0
  51. package/src/canvas/editor/z-order.ts +87 -0
  52. package/src/canvas/headless/alignment.test.ts +59 -0
  53. package/src/canvas/headless/alignment.ts +23 -0
  54. package/src/canvas/headless/break-apart-find.ts +418 -0
  55. package/src/canvas/headless/break-apart-pieces.ts +104 -0
  56. package/src/canvas/headless/break-apart-plan.ts +439 -0
  57. package/src/canvas/headless/break-apart-style.ts +106 -0
  58. package/src/canvas/headless/break-apart.ts +244 -0
  59. package/src/canvas/headless/canvas-cleanup.test.ts +181 -0
  60. package/src/canvas/headless/canvas-cleanup.ts +102 -0
  61. package/src/canvas/headless/edge-anchors.test.ts +70 -0
  62. package/src/canvas/headless/edge-anchors.ts +143 -0
  63. package/src/canvas/headless/edge-memo.test.ts +58 -0
  64. package/src/canvas/headless/edge-memo.ts +32 -0
  65. package/src/canvas/headless/edge-serialization.test.ts +55 -0
  66. package/src/canvas/headless/edge-serialization.ts +32 -0
  67. package/src/canvas/headless/figure-geometry.ts +293 -0
  68. package/src/canvas/headless/fit-viewport.test.ts +85 -0
  69. package/src/canvas/headless/fit-viewport.ts +137 -0
  70. package/src/canvas/headless/grid.test.ts +34 -0
  71. package/src/canvas/headless/grid.ts +94 -0
  72. package/src/canvas/headless/icon-node.test.ts +23 -0
  73. package/src/canvas/headless/icon-node.ts +66 -0
  74. package/src/canvas/headless/index.ts +9 -0
  75. package/src/canvas/headless/linkify.test.ts +23 -0
  76. package/src/canvas/headless/linkify.ts +25 -0
  77. package/src/canvas/headless/pen-marks.ts +133 -0
  78. package/src/canvas/headless/sketch.test.ts +99 -0
  79. package/src/canvas/headless/sketch.ts +302 -0
  80. package/src/canvas/headless/snap-guides.test.ts +61 -0
  81. package/src/canvas/headless/snap-guides.ts +146 -0
  82. package/src/canvas/headless/svg-figure.test.ts +49 -0
  83. package/src/canvas/headless/svg-figure.ts +176 -0
  84. package/src/canvas/headless/table-layout.test.ts +158 -0
  85. package/src/canvas/headless/table-layout.ts +309 -0
  86. package/src/canvas/index.ts +35 -0
  87. package/src/canvas/lod/lod-mode.svelte.ts +30 -0
  88. package/src/canvas/lod/lod-switch.svelte +140 -0
  89. package/src/canvas/lod/render-all.svelte.ts +52 -0
  90. package/src/canvas/ops/arrange.ts +108 -0
  91. package/src/canvas/ops/ops.test.ts +98 -0
  92. package/src/canvas/ops/ops.ts +347 -0
  93. package/src/canvas/pen/PenField.svelte +128 -0
  94. package/src/canvas/pen/PenField.test.ts +87 -0
  95. package/src/canvas/pen/PenOverlay.svelte +172 -0
  96. package/src/canvas/pen/ShapeNode.svelte +64 -0
  97. package/src/canvas/pen/TextNode.svelte +97 -0
  98. package/src/canvas/pen/colors.ts +28 -0
  99. package/src/canvas/pen/eraser.ts +46 -0
  100. package/src/canvas/pen/geometry.ts +148 -0
  101. package/src/canvas/pen/host-field.test.svelte +13 -0
  102. package/src/canvas/pen/index.ts +40 -0
  103. package/src/canvas/pen/ops.ts +16 -0
  104. package/src/canvas/pen/pen-session.ts +177 -0
  105. package/src/canvas/pen/pen.css +149 -0
  106. package/src/canvas/pen/plugin.ts +139 -0
  107. package/src/canvas/pen/shapes.ts +104 -0
  108. package/src/canvas/pen/stroke-path.ts +54 -0
  109. package/src/canvas/pen/types.ts +96 -0
  110. package/src/canvas/pen/ui.ts +26 -0
  111. package/src/canvas/plugin.test.ts +45 -0
  112. package/src/canvas/plugin.ts +159 -0
  113. package/src/canvas/screen/GlyphIcon.svelte +25 -0
  114. package/src/canvas/screen/PartView.svelte +176 -0
  115. package/src/canvas/screen/ScreenBody.svelte +142 -0
  116. package/src/canvas/screen/ScreenCard.svelte +32 -0
  117. package/src/canvas/screen/glyphs/button.svg +1 -0
  118. package/src/canvas/screen/glyphs/close.svg +1 -0
  119. package/src/canvas/screen/glyphs/down.svg +1 -0
  120. package/src/canvas/screen/glyphs/header.svg +1 -0
  121. package/src/canvas/screen/glyphs/hills.svg +1 -0
  122. package/src/canvas/screen/glyphs/image.svg +1 -0
  123. package/src/canvas/screen/glyphs/input.svg +1 -0
  124. package/src/canvas/screen/glyphs/list.svg +1 -0
  125. package/src/canvas/screen/glyphs/modal.svg +1 -0
  126. package/src/canvas/screen/glyphs/plus.svg +1 -0
  127. package/src/canvas/screen/glyphs/up.svg +1 -0
  128. package/src/canvas/screen/glyphs.ts +40 -0
  129. package/src/canvas/screen/labels.ts +41 -0
  130. package/src/canvas/screen/parts.ts +164 -0
  131. package/src/canvas/screen/plugin.ts +67 -0
  132. package/src/canvas/screen/screen.css +593 -0
  133. package/src/canvas/screen/types.ts +47 -0
  134. package/src/canvas/screen/ui.ts +13 -0
  135. package/src/canvas/test-utils.ts +18 -0
  136. package/src/canvas/types.ts +53 -0
  137. package/src/canvas/ui/CanvasToolbar.svelte +169 -0
  138. package/src/canvas/ui/FrameLayer.svelte +116 -0
  139. package/src/canvas/ui/PenLayer.svelte +39 -0
  140. package/src/canvas/ui/StylePanel.svelte +268 -0
  141. package/src/canvas/ui/canvas-grid.svelte +156 -0
  142. package/src/canvas/ui/canvas-ui.css +350 -0
  143. package/src/canvas/ui/forward-wheel.ts +29 -0
  144. package/src/canvas/ui/icons.ts +124 -0
  145. package/src/canvas/ui/place-panel.ts +80 -0
  146. package/src/canvas/ui/shortcut-tip.test.ts +16 -0
  147. package/src/canvas/ui/shortcut-tip.ts +17 -0
  148. package/src/canvas/ui/tools.ts +146 -0
  149. package/src/charts/README.md +104 -0
  150. package/src/charts/ambient.d.ts +4 -0
  151. package/src/charts/animated-value.css +7 -0
  152. package/src/charts/animated-value.svelte +110 -0
  153. package/src/charts/area-chart.css +29 -0
  154. package/src/charts/area-chart.svelte +204 -0
  155. package/src/charts/bar-chart.css +119 -0
  156. package/src/charts/bar-chart.svelte +275 -0
  157. package/src/charts/charts.css +201 -0
  158. package/src/charts/crosshair.svelte +35 -0
  159. package/src/charts/dashboard/chart.ts +5 -0
  160. package/src/charts/dashboard/dashboard-empty.svelte +55 -0
  161. package/src/charts/dashboard/dashboard-section.svelte +58 -0
  162. package/src/charts/dashboard/data-panel.svelte +81 -0
  163. package/src/charts/dashboard/data-sort-head.svelte +102 -0
  164. package/src/charts/dashboard/data-table.svelte +278 -0
  165. package/src/charts/dashboard/data-table.ts +26 -0
  166. package/src/charts/dashboard/index.ts +25 -0
  167. package/src/charts/dashboard/panel-grid.svelte +43 -0
  168. package/src/charts/dashboard/period-filter.svelte +53 -0
  169. package/src/charts/dashboard/rating-chip.svelte +35 -0
  170. package/src/charts/dashboard/stat-band.svelte +47 -0
  171. package/src/charts/dashboard/stat-tile.svelte +176 -0
  172. package/src/charts/dashboard/windows.ts +8 -0
  173. package/src/charts/donut-chart.css +56 -0
  174. package/src/charts/donut-chart.svelte +158 -0
  175. package/src/charts/edge-fade.svelte +43 -0
  176. package/src/charts/edge-fade.ts +16 -0
  177. package/src/charts/figure.css +100 -0
  178. package/src/charts/figure.svelte +122 -0
  179. package/src/charts/gauge-chart.css +94 -0
  180. package/src/charts/gauge-chart.svelte +302 -0
  181. package/src/charts/hover-band.test.ts +30 -0
  182. package/src/charts/hover-band.ts +21 -0
  183. package/src/charts/hover-focus.svelte +85 -0
  184. package/src/charts/index.ts +31 -0
  185. package/src/charts/legend.css +136 -0
  186. package/src/charts/legend.svelte +112 -0
  187. package/src/charts/line-chart.css +26 -0
  188. package/src/charts/line-chart.svelte +256 -0
  189. package/src/charts/motion.ts +19 -0
  190. package/src/charts/number-chart.css +179 -0
  191. package/src/charts/number-chart.svelte +187 -0
  192. package/src/charts/primitives/chart-container.svelte +57 -0
  193. package/src/charts/primitives/chart-style.svelte +36 -0
  194. package/src/charts/primitives/chart-tooltip.svelte +161 -0
  195. package/src/charts/primitives/chart-utils.ts +54 -0
  196. package/src/charts/primitives/index.ts +11 -0
  197. package/src/charts/reference-line.css +18 -0
  198. package/src/charts/reference-line.svelte +31 -0
  199. package/src/charts/reveal.svelte +61 -0
  200. package/src/charts/ring-chart.css +104 -0
  201. package/src/charts/ring-chart.svelte +166 -0
  202. package/src/charts/sparkline.css +45 -0
  203. package/src/charts/sparkline.svelte +141 -0
  204. package/src/charts/stat-card.css +105 -0
  205. package/src/charts/stat-card.svelte +267 -0
  206. package/src/charts/time-series-chart.css +28 -0
  207. package/src/charts/time-series-chart.svelte +179 -0
  208. package/src/charts/trend-pill.css +38 -0
  209. package/src/charts/trend-pill.svelte +48 -0
  210. package/src/components/copy/copy-button.svelte +1 -1
  211. package/src/components/docs/doc-page.svelte +1 -1
  212. package/src/components/docs/on-this-page.svelte +2 -2
  213. package/src/components/list-page/empty-state.svelte +1 -1
  214. package/src/components/list-page/filter-popover.svelte +1 -1
  215. package/src/components/list-page/section-label.svelte +1 -1
  216. package/src/components/segmented/segment.svelte +4 -4
  217. package/src/components/theme/theme-provider.svelte +6 -4
  218. package/src/components/toolbar/index.ts +5 -0
  219. package/src/components/toolbar/toolbar.svelte +168 -0
  220. package/src/components/toolbar/toolbar.test.ts +66 -0
  221. package/src/editor/README.md +58 -0
  222. package/src/editor/ambient.d.ts +1 -0
  223. package/src/editor/block-id.ts +77 -0
  224. package/src/editor/block-ops.test.ts +237 -0
  225. package/src/editor/block-ops.ts +157 -0
  226. package/src/editor/content.css +418 -0
  227. package/src/editor/editor-toolbar.svelte +303 -0
  228. package/src/editor/editor.svelte +469 -0
  229. package/src/editor/extensions.ts +145 -0
  230. package/src/editor/image-toolbar.svelte +123 -0
  231. package/src/editor/index.ts +17 -0
  232. package/src/editor/inline-editor.svelte +261 -0
  233. package/src/editor/markdown-escaping.test.ts +158 -0
  234. package/src/editor/markdown-marks.test.ts +89 -0
  235. package/src/editor/markdown-test-helpers.ts +13 -0
  236. package/src/editor/markdown-youtube.test.ts +93 -0
  237. package/src/editor/markdown.test.ts +250 -0
  238. package/src/editor/markdown.ts +511 -0
  239. package/src/editor/renderer.svelte +92 -0
  240. package/src/editor/serializer.test.ts +133 -0
  241. package/src/editor/serializer.ts +370 -0
  242. package/src/editor/slash-items.ts +115 -0
  243. package/src/editor/slash-menu.svelte +159 -0
  244. package/src/editor/youtube-url.ts +35 -0
  245. package/src/effects/interactive-hover-button/InteractiveHoverButton.svelte +13 -13
  246. package/src/gallery.test.ts +20 -1
  247. package/src/lib/result.ts +19 -0
  248. package/src/onboarding/README.md +120 -0
  249. package/src/onboarding/define-tours.test.ts +50 -0
  250. package/src/onboarding/define-tours.ts +31 -0
  251. package/src/onboarding/demo.svelte +86 -0
  252. package/src/onboarding/index.ts +12 -0
  253. package/src/onboarding/onboarding-provider.svelte +120 -0
  254. package/src/onboarding/onboarding.svelte.ts +24 -0
  255. package/src/onboarding/pick.test.ts +79 -0
  256. package/src/onboarding/pick.ts +36 -0
  257. package/src/onboarding/stores.test.ts +103 -0
  258. package/src/onboarding/stores.ts +58 -0
  259. package/src/onboarding/tour-card.svelte +70 -0
  260. package/src/onboarding/tour-runner.svelte +197 -0
  261. package/src/onboarding/types.ts +34 -0
  262. package/src/onboarding/wait.ts +29 -0
  263. package/src/shadcn/command/command-input.svelte +1 -1
  264. package/src/slides/Controls.svelte +138 -0
  265. package/src/slides/Deck.svelte +271 -0
  266. package/src/slides/Fragment.svelte +71 -0
  267. package/src/slides/Overview.svelte +113 -0
  268. package/src/slides/Presenter.svelte +292 -0
  269. package/src/slides/README.md +89 -0
  270. package/src/slides/Slide.svelte +179 -0
  271. package/src/slides/SlideList.svelte +19 -0
  272. package/src/slides/ambient.d.ts +1 -0
  273. package/src/slides/bridges.ts +69 -0
  274. package/src/slides/deck-controller.svelte.ts +292 -0
  275. package/src/slides/demo-slides.svelte +358 -0
  276. package/src/slides/demo.svelte +176 -0
  277. package/src/slides/export/capture.ts +113 -0
  278. package/src/slides/export/ffmpeg.ts +120 -0
  279. package/src/slides/export/index.ts +9 -0
  280. package/src/slides/export/launch.ts +10 -0
  281. package/src/slides/export/pdf.ts +152 -0
  282. package/src/slides/export/types.ts +48 -0
  283. package/src/slides/index.ts +19 -0
  284. package/src/slides/labels.ts +48 -0
  285. package/src/slides/layout.ts +51 -0
  286. package/src/slides/model.ts +47 -0
  287. package/src/slides/navigation.ts +100 -0
  288. package/src/slides/theme.css +205 -0
  289. package/src/slides/types.ts +86 -0
  290. package/src/tasks/README.md +312 -0
  291. package/src/tasks/ambient.d.ts +3 -0
  292. package/src/tasks/board-order.ts +137 -0
  293. package/src/tasks/dates.ts +31 -0
  294. package/src/tasks/define-statuses.ts +32 -0
  295. package/src/tasks/demo.svelte +389 -0
  296. package/src/tasks/detail/detail-demo.svelte +186 -0
  297. package/src/tasks/detail/detail-types.ts +54 -0
  298. package/src/tasks/detail/task-chip.svelte +28 -0
  299. package/src/tasks/detail/task-detail.svelte +315 -0
  300. package/src/tasks/detail/task-property.svelte +17 -0
  301. package/src/tasks/detail/task-thread.svelte +258 -0
  302. package/src/tasks/gantt/gantt-bar.svelte +243 -0
  303. package/src/tasks/gantt/gantt-grid.svelte +94 -0
  304. package/src/tasks/gantt/gantt-group-row.svelte +55 -0
  305. package/src/tasks/gantt/gantt-header.svelte +71 -0
  306. package/src/tasks/gantt/gantt-task-row.svelte +186 -0
  307. package/src/tasks/gantt/gantt.css +22 -0
  308. package/src/tasks/gantt/gantt.test.ts +77 -0
  309. package/src/tasks/gantt/logic.test.ts +163 -0
  310. package/src/tasks/gantt/logic.ts +248 -0
  311. package/src/tasks/inbox/inbox-card.svelte +283 -0
  312. package/src/tasks/inbox/inbox-draft.svelte +84 -0
  313. package/src/tasks/inbox/inbox-logic.ts +55 -0
  314. package/src/tasks/inbox/inbox-statuses.ts +48 -0
  315. package/src/tasks/inbox/inbox-trace.svelte +45 -0
  316. package/src/tasks/inbox/inbox-types.ts +108 -0
  317. package/src/tasks/inbox/inbox.svelte +203 -0
  318. package/src/tasks/inbox/index.ts +23 -0
  319. package/src/tasks/index.ts +85 -0
  320. package/src/tasks/kanban-board.svelte +561 -0
  321. package/src/tasks/kanban-column.svelte +117 -0
  322. package/src/tasks/kanban-drag.ts +38 -0
  323. package/src/tasks/kanban-types.ts +65 -0
  324. package/src/tasks/presets/sales.ts +16 -0
  325. package/src/tasks/record/demo-data.ts +286 -0
  326. package/src/tasks/record/field-section.svelte +53 -0
  327. package/src/tasks/record/fields.ts +142 -0
  328. package/src/tasks/record/format.ts +87 -0
  329. package/src/tasks/record/gutter.ts +2 -0
  330. package/src/tasks/record/index.ts +54 -0
  331. package/src/tasks/record/info-list.svelte +316 -0
  332. package/src/tasks/record/kinds.ts +42 -0
  333. package/src/tasks/record/logic.ts +324 -0
  334. package/src/tasks/record/next-up.svelte +70 -0
  335. package/src/tasks/record/project-list.svelte +133 -0
  336. package/src/tasks/record/record-demo.svelte +324 -0
  337. package/src/tasks/record/record-header.svelte +70 -0
  338. package/src/tasks/record/record-shell.svelte +138 -0
  339. package/src/tasks/record/record-stats.svelte +48 -0
  340. package/src/tasks/record/record-timeline.svelte +231 -0
  341. package/src/tasks/record/record.test.ts +350 -0
  342. package/src/tasks/record/steps-rail.svelte +474 -0
  343. package/src/tasks/record/types.ts +280 -0
  344. package/src/tasks/record/where-we-are.svelte +121 -0
  345. package/src/tasks/reorder.ts +19 -0
  346. package/src/tasks/side-panel-types.ts +16 -0
  347. package/src/tasks/side-panel.svelte +193 -0
  348. package/src/tasks/status-badge.svelte +63 -0
  349. package/src/tasks/status-dot.svelte +228 -0
  350. package/src/tasks/status-icon.svelte +30 -0
  351. package/src/tasks/status-types.ts +40 -0
  352. package/src/tasks/task-gantt.svelte +230 -0
  353. package/src/tasks/task-list.svelte +470 -0
  354. package/src/tasks/task-types.ts +33 -0
  355. package/src/tasks/time/duration.ts +69 -0
  356. package/src/tasks/time/time-chip.svelte +62 -0
  357. package/src/tasks/time/time-dot.svelte +58 -0
  358. package/src/tasks/time/time-ring.svelte +74 -0
  359. package/src/tasks/time/time-tracker.svelte +565 -0
  360. package/src/tasks/time/time-types.ts +74 -0
  361. package/src/tasks/use-kanban-dnd.svelte.ts +173 -0
  362. package/src/tokens/base.css +5 -5
  363. package/src/tokens/palette.css +115 -120
@@ -0,0 +1,201 @@
1
+ /* Global theming for layerchart-rendered chrome shared by every chart in this
2
+ package (tooltip surface, header, rows). Ships with `@heyramzi/ui/charts`, so
3
+ any consumer that defines the shadcn token set gets a legible tooltip in both
4
+ themes for free.
5
+
6
+ WHY: layerchart's built-in `default` tooltip paints itself with
7
+ `light-dark(var(--color-surface-100, white), var(--color-surface-300, black))`
8
+ and a `backdrop-filter` blur. Apps that don't define `--color-surface-*` (and
9
+ don't set `color-scheme`) fall back to a translucent white card, which is
10
+ unreadable on a dark page. These rules repaint it with `--popover` /
11
+ `--border` / `--muted-foreground`, which already flip per theme. The selectors
12
+ stay unlayered so they beat layerchart's `@layer component` defaults
13
+ regardless of specificity. */
14
+
15
+ .lc-tooltip-container[data-variant="default"] {
16
+ color: var(--popover-foreground, var(--foreground));
17
+ background-color: var(--popover, #fff);
18
+ border: 1px solid var(--border, transparent);
19
+ border-radius: 0.5rem;
20
+ padding: 0.625rem 0.75rem;
21
+ min-width: 8.75rem;
22
+ backdrop-filter: none;
23
+ box-shadow:
24
+ 0 1px 2px -1px oklch(0 0 0 / 0.2),
25
+ 0 10px 28px -10px oklch(0 0 0 / 0.32);
26
+ /* WHY: opacity only; the container's transform is inline positioning, so
27
+ animating transform here would fight it and make the tooltip jump. */
28
+ transform-origin: top left;
29
+ animation: hr-tooltip-in var(--duration-fast, 250ms) var(--ease-smooth-out) both;
30
+ }
31
+
32
+ /* WHY `scale` and `translate`, not `transform`: the container's inline transform is
33
+ its position, and the individual properties compose with it instead of fighting. */
34
+ @keyframes hr-tooltip-in {
35
+ from {
36
+ opacity: 0;
37
+ scale: var(--scale-large, 0.96);
38
+ translate: calc(var(--distance-small, 6px) * -1) 0;
39
+ }
40
+ }
41
+
42
+ @media (prefers-reduced-motion: reduce) {
43
+ .lc-tooltip-container[data-variant="default"] {
44
+ animation: none;
45
+ }
46
+ }
47
+
48
+ /* bklit-ui's panel: a small title with no rule under it, and a dot on every row. */
49
+ .lc-tooltip-header {
50
+ color: var(--popover-foreground, var(--foreground));
51
+ font-size: 0.75rem;
52
+ font-weight: 500;
53
+ border-bottom: none;
54
+ margin-bottom: 0.5rem;
55
+ padding-bottom: 0;
56
+ }
57
+
58
+ .lc-tooltip-list {
59
+ gap: 0.375rem 1rem;
60
+ font-size: 0.8125rem;
61
+ }
62
+
63
+ .lc-tooltip-item-color {
64
+ width: 0.625rem;
65
+ height: 0.625rem;
66
+ }
67
+
68
+ .lc-tooltip-item-label,
69
+ .lc-tooltip-container .label {
70
+ color: var(--muted-foreground);
71
+ }
72
+
73
+ .lc-tooltip-item-value,
74
+ .lc-tooltip-container .value {
75
+ color: var(--popover-foreground, var(--foreground));
76
+ font-weight: 500;
77
+ font-variant-numeric: tabular-nums;
78
+ }
79
+
80
+ /* Shared chart tokens, held at zero specificity by `:where()`. The reveal pair
81
+ is the entrance every chart shares; motion.ts mirrors its duration. */
82
+ :where(
83
+ .hr-area-chart,
84
+ .hr-bar-chart,
85
+ .hr-line-chart,
86
+ .hr-sparkline,
87
+ .hr-number-chart,
88
+ .hr-stat-card,
89
+ .hr-legend,
90
+ .hr-ts-chart,
91
+ .hr-donut-chart,
92
+ .hr-ring,
93
+ .hr-gauge
94
+ ) {
95
+ --chart-label: var(--muted-foreground, oklch(0.5 0.01 286));
96
+ --chart-grid: color-mix(in srgb, var(--border, oklch(0.5 0.01 286)) 65%, transparent);
97
+ --chart-ease-out: cubic-bezier(0.22, 1, 0.36, 1);
98
+ --chart-motion-fast: 150ms;
99
+ --chart-motion: 250ms;
100
+ --chart-motion-slow: 800ms;
101
+ --chart-motion-reveal: 1100ms;
102
+ --chart-ease-reveal: cubic-bezier(0.85, 0, 0.15, 1);
103
+ --chart-stagger: 30ms;
104
+ --chart-crosshair: color-mix(
105
+ in srgb,
106
+ var(--muted-foreground, oklch(0.5 0.01 286)) 60%,
107
+ transparent
108
+ );
109
+ --chart-card-shadow: 0 1px 2px 0 oklch(0 0 0 / 0.04), 0 1px 3px 0 oklch(0 0 0 / 0.06);
110
+ }
111
+
112
+ /* Shared layerchart chrome: axis labels, grid rules, and hover highlights were
113
+ duplicated identically across every chart's scoped <style>. Centralize them
114
+ here, scoped to the chart roots, so the look is defined once. `:where()` keeps
115
+ specificity at 0 so a component can still override locally if it must. */
116
+ :where(
117
+ .hr-area-chart,
118
+ .hr-bar-chart,
119
+ .hr-line-chart,
120
+ .hr-scatter-chart,
121
+ .hr-live-line-chart,
122
+ .hr-sparkline,
123
+ .hr-stat-card,
124
+ .hr-donut-chart
125
+ )
126
+ .lc-axis-tick-label {
127
+ fill: var(--chart-label);
128
+ font-size: 11px;
129
+ }
130
+ :where(
131
+ .hr-area-chart,
132
+ .hr-bar-chart,
133
+ .hr-line-chart,
134
+ .hr-scatter-chart,
135
+ .hr-live-line-chart,
136
+ .hr-sparkline,
137
+ .hr-stat-card,
138
+ .hr-donut-chart
139
+ )
140
+ .lc-axis-tick {
141
+ stroke: transparent;
142
+ }
143
+ :where(
144
+ .hr-area-chart,
145
+ .hr-bar-chart,
146
+ .hr-line-chart,
147
+ .hr-scatter-chart,
148
+ .hr-live-line-chart,
149
+ .hr-sparkline,
150
+ .hr-stat-card,
151
+ .hr-donut-chart
152
+ )
153
+ :is(.lc-grid-x-rule, .lc-grid-y-rule) {
154
+ stroke: var(--chart-grid);
155
+ stroke-dasharray: 4 4;
156
+ }
157
+ :where(
158
+ .hr-area-chart,
159
+ .hr-bar-chart,
160
+ .hr-line-chart,
161
+ .hr-scatter-chart,
162
+ .hr-live-line-chart,
163
+ .hr-sparkline,
164
+ .hr-stat-card,
165
+ .hr-donut-chart
166
+ )
167
+ .lc-highlight-point {
168
+ stroke: var(--background, white);
169
+ stroke-width: 2;
170
+ }
171
+ :where(
172
+ .hr-area-chart,
173
+ .hr-bar-chart,
174
+ .hr-line-chart,
175
+ .hr-scatter-chart,
176
+ .hr-live-line-chart,
177
+ .hr-sparkline,
178
+ .hr-stat-card,
179
+ .hr-donut-chart
180
+ )
181
+ .lc-highlight-line {
182
+ stroke: var(--chart-grid);
183
+ stroke-width: 1;
184
+ stroke-dasharray: none;
185
+ }
186
+ /* bklit-ui's hover: the series drops to 0.3 and hover-focus.svelte redraws the
187
+ stretch around the point at full strength. */
188
+ .hr-hover-dim {
189
+ transition: opacity 400ms ease-in-out;
190
+ }
191
+ .hr-hover-dim.hr-hover-dimmed {
192
+ opacity: 0.3;
193
+ }
194
+ @media (prefers-reduced-motion: reduce) {
195
+ .hr-hover-dim {
196
+ transition: none;
197
+ }
198
+ }
199
+ .hr-crosshair-stop {
200
+ stop-color: var(--chart-crosshair);
201
+ }
@@ -0,0 +1,35 @@
1
+ <script lang="ts">
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import { getChartContext } from "layerchart";
6
+
7
+ // =========================
8
+ // TYPES
9
+ // =========================
10
+ interface Props {
11
+ /** Gradient id. Paint the highlight line with `stroke: url(#id)`. */
12
+ id: string;
13
+ };
14
+
15
+ // =========================
16
+ // PROPS
17
+ // =========================
18
+ let { id }: Props = $props();
19
+
20
+ const ctx = getChartContext();
21
+ </script>
22
+
23
+ <!-- =========================== -->
24
+ <!-- CROSSHAIR -->
25
+ <!-- =========================== -->
26
+ <!-- WHY userSpaceOnUse: a vertical line has a zero-width bounding box, so an
27
+ objectBoundingBox gradient on it paints nothing. -->
28
+ <defs>
29
+ <linearGradient {id} gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="0" y2={ctx.height}>
30
+ <stop offset="0" class="hr-crosshair-stop" stop-opacity="0" />
31
+ <stop offset="0.12" class="hr-crosshair-stop" />
32
+ <stop offset="0.88" class="hr-crosshair-stop" />
33
+ <stop offset="1" class="hr-crosshair-stop" stop-opacity="0" />
34
+ </linearGradient>
35
+ </defs>
@@ -0,0 +1,5 @@
1
+ /**
2
+ * The height of every dashboard chart, half width or full, so a trend is one
3
+ * size on every page.
4
+ */
5
+ export const CHART_HEIGHT = 240;
@@ -0,0 +1,55 @@
1
+ <script lang="ts">
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import type { Snippet } from "svelte";
6
+ import { cn } from "../../lib/utils.js";
7
+ import type { PhosphorIcon } from "../../lib/icon-types.js";
8
+
9
+ // =========================
10
+ // TYPES
11
+ // =========================
12
+ interface Props {
13
+ // Not list-page's `EmptyState`: that one takes exactly one action, and a
14
+ // dashboard can be empty for reasons (nothing connected, nothing synced)
15
+ // that need two buttons.
16
+
17
+ // The dashed edge marks truly empty, not loading.
18
+ icon?: PhosphorIcon;
19
+ title: string;
20
+ description?: string;
21
+ /** One or two buttons. Omit where there is nothing the reader can do. */
22
+ actions?: Snippet;
23
+ class?: string;
24
+ };
25
+
26
+ // =========================
27
+ // PROPS
28
+ // =========================
29
+ let { icon: Icon, title, description, actions, class: className }: Props = $props();
30
+ </script>
31
+
32
+ <!-- =========================== -->
33
+ <!-- MARKUP -->
34
+ <!-- =========================== -->
35
+ <div
36
+ class={cn(
37
+ "rounded-xl border-2 border-dashed border-border bg-transparent p-10 text-center",
38
+ className,
39
+ )}
40
+ >
41
+ <div class="mx-auto flex max-w-md flex-col items-center gap-4">
42
+ {#if Icon}
43
+ <Icon class="size-10 text-muted-foreground" aria-hidden="true" />
44
+ {/if}
45
+ <div class="space-y-1.5">
46
+ <h2 class="font-heading text-lg font-semibold">{title}</h2>
47
+ {#if description}
48
+ <p class="text-sm text-muted-foreground">{description}</p>
49
+ {/if}
50
+ </div>
51
+ {#if actions}
52
+ <div class="flex flex-wrap justify-center gap-2">{@render actions()}</div>
53
+ {/if}
54
+ </div>
55
+ </div>
@@ -0,0 +1,58 @@
1
+ <script lang="ts">
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import { ArrowRight } from "phosphor-svelte";
6
+ import type { Snippet } from "svelte";
7
+ import { cn } from "../../lib/utils.js";
8
+
9
+ // =========================
10
+ // TYPES
11
+ // =========================
12
+ interface Props {
13
+ title: string;
14
+ /** One line on the right: a window, a provenance, a count. Never a sentence. */
15
+ note?: string;
16
+ /** The page that owns this area, when one page does. */
17
+ href?: string;
18
+ /** The link's wording. Translated at the call site. */
19
+ openLabel?: string;
20
+ children: Snippet;
21
+ class?: string;
22
+ };
23
+
24
+ // =========================
25
+ // PROPS
26
+ // =========================
27
+ let { title, note, href, openLabel = "Open", children, class: className }: Props = $props();
28
+ </script>
29
+
30
+ <!-- =========================== -->
31
+ <!-- MARKUP -->
32
+ <!-- =========================== -->
33
+ <!-- An area of a dashboard, named, then its numbers. No box of its own: its
34
+ cards are the containers, and a card in a card is never right. -->
35
+ <section class={cn("flex flex-col gap-4", className)}>
36
+ <div class="flex flex-wrap items-baseline justify-between gap-x-4 gap-y-1">
37
+ <h2 class="text-base font-semibold tracking-tight">{title}</h2>
38
+ <div class="flex items-baseline gap-3">
39
+ {#if note}
40
+ <p class="text-xs text-muted-foreground">{note}</p>
41
+ {/if}
42
+ {#if href}
43
+ <a
44
+ {href}
45
+ class="group inline-flex items-center gap-1 rounded-md text-xs font-medium text-muted-foreground transition-colors duration-[var(--duration-quick)] hover:text-foreground focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-none"
46
+ >
47
+ {openLabel}
48
+ <ArrowRight
49
+ size={12}
50
+ class="shrink-0 transition-transform duration-[var(--duration-quick)] ease-out group-hover:translate-x-0.5"
51
+ />
52
+ </a>
53
+ {/if}
54
+ </div>
55
+ </div>
56
+
57
+ {@render children()}
58
+ </section>
@@ -0,0 +1,81 @@
1
+ <script lang="ts">
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import type { Snippet } from "svelte";
6
+ import { cn } from "../../lib/utils.js";
7
+ import { panelHeaderClass, panelShellClass } from "../../lib/panel.js";
8
+ import { SectionLabel } from "../../components/list-page/index.js";
9
+ import type { PhosphorIcon } from "../../lib/icon-types.js";
10
+
11
+ // =========================
12
+ // TYPES
13
+ // =========================
14
+ interface Props {
15
+ /** What this section of the dashboard is. Drawn as the app's one panel label. */
16
+ label: string;
17
+ labelIcon?: PhosphorIcon;
18
+ /**
19
+ * A row count, total or freshness stamp. It's a prop, not an action,
20
+ * because hand-placed hints ended up at three type sizes on one screen.
21
+ */
22
+ hint?: string;
23
+ /** Controls that act on this panel. Pinned right, after the hint. */
24
+ actions?: Snippet;
25
+ /** A line under the label, for the scope a number was measured over. */
26
+ sublabel?: string;
27
+ /** Off for a body that supplies its own padding: a table, a full-bleed list. */
28
+ pad?: boolean;
29
+ /**
30
+ * The body scrolls sideways rather than squeezing. For a wide table, which is
31
+ * every table on a dashboard once the window is narrower than its columns.
32
+ */
33
+ scroll?: boolean;
34
+ children: Snippet;
35
+ class?: string;
36
+ bodyClass?: string;
37
+ };
38
+
39
+ // =========================
40
+ // PROPS
41
+ // =========================
42
+ let {
43
+ label,
44
+ labelIcon,
45
+ hint,
46
+ actions,
47
+ sublabel,
48
+ pad = true,
49
+ scroll = false,
50
+ children,
51
+ class: className,
52
+ bodyClass,
53
+ }: Props = $props();
54
+ </script>
55
+
56
+ <!-- =========================== -->
57
+ <!-- MARKUP -->
58
+ <!-- =========================== -->
59
+ <section class={cn(panelShellClass, className)}>
60
+ <div class={panelHeaderClass}>
61
+ <div class="flex min-w-0 flex-col gap-0.5">
62
+ <SectionLabel icon={labelIcon}>{label}</SectionLabel>
63
+ {#if sublabel}
64
+ <span class="truncate text-xs text-muted-foreground">{sublabel}</span>
65
+ {/if}
66
+ </div>
67
+ {#if hint || actions}
68
+ <div class="flex min-w-0 shrink-0 items-center gap-3">
69
+ {#if hint}
70
+ <span class="text-xs tabular-nums text-muted-foreground">{hint}</span>
71
+ {/if}
72
+ {#if actions}
73
+ {@render actions()}
74
+ {/if}
75
+ </div>
76
+ {/if}
77
+ </div>
78
+ <div class={cn(pad && "p-4", scroll && "overflow-x-auto", bodyClass)}>
79
+ {@render children()}
80
+ </div>
81
+ </section>
@@ -0,0 +1,102 @@
1
+ <script lang="ts">
2
+ /*
3
+ * A column head that sorts the table under it. Sort buttons above a table
4
+ * never say which column they rank, and read as filter tabs.
5
+ */
6
+
7
+ // Every sortable head shows its caret, at 40%, solid on the active column.
8
+ // Hover-only makes people hunt, and it shifts the label under the pointer.
9
+
10
+ // No padding of its own: it comes from the skin classes, so heads and cells
11
+ // stay in line when a dense table tightens both.
12
+
13
+ // =========================
14
+ // IMPORTS
15
+ // =========================
16
+ import type { Snippet } from "svelte";
17
+ import { CaretDown, CaretUp } from "phosphor-svelte";
18
+ import { cn } from "../../lib/utils.js";
19
+ import { dataThClass, dataThNumClass } from "./data-table.js";
20
+
21
+ // =========================
22
+ // TYPES
23
+ // =========================
24
+ interface Props {
25
+ /** True when the table is currently ranked by this column. */
26
+ active: boolean;
27
+ /** Which way the active column runs. Ignored while inactive. */
28
+ descending: boolean;
29
+ /** Called on click and on Enter/Space. Toggling direction is the caller's. */
30
+ onSort: () => void;
31
+ /** Numbers right-align to their digits, so their caret leads the label. */
32
+ numeric?: boolean;
33
+ /** The long form of an abbreviated head, as the browser tooltip. */
34
+ title?: string;
35
+ class?: string;
36
+ children: Snippet;
37
+ };
38
+
39
+ // =========================
40
+ // PROPS
41
+ // =========================
42
+ let {
43
+ active,
44
+ descending,
45
+ onSort,
46
+ numeric = false,
47
+ title,
48
+ class: className,
49
+ children,
50
+ }: Props = $props();
51
+
52
+ // =========================
53
+ // DERIVED
54
+ // =========================
55
+ const ariaSort = $derived(active ? (descending ? "descending" : "ascending") : "none");
56
+
57
+ // =========================
58
+ // FUNCTIONS
59
+ // =========================
60
+ function handleKeydown(event: KeyboardEvent) {
61
+ if (event.key === "Enter" || event.key === " ") {
62
+ event.preventDefault();
63
+ onSort();
64
+ }
65
+ }
66
+ </script>
67
+
68
+ <!-- =========================== -->
69
+ <!-- MARKUP -->
70
+ <!-- =========================== -->
71
+ <th
72
+ scope="col"
73
+ {title}
74
+ tabindex="0"
75
+ aria-sort={ariaSort}
76
+ onclick={onSort}
77
+ onkeydown={handleKeydown}
78
+ class={cn(
79
+ numeric ? dataThNumClass : dataThClass,
80
+ "cursor-pointer select-none transition-colors hover:text-foreground",
81
+ active && "text-foreground",
82
+ className,
83
+ )}
84
+ >
85
+ <span class="inline-flex items-center gap-0.5 whitespace-nowrap">
86
+ {#if numeric}
87
+ {@render caret()}{@render children()}
88
+ {:else}
89
+ {@render children()}{@render caret()}
90
+ {/if}
91
+ </span>
92
+ </th>
93
+
94
+ {#snippet caret()}
95
+ {#if active && !descending}
96
+ <CaretUp class="size-3 shrink-0" weight="bold" />
97
+ {:else if active}
98
+ <CaretDown class="size-3 shrink-0" weight="bold" />
99
+ {:else}
100
+ <CaretDown class="size-3 shrink-0 text-muted-foreground/40" />
101
+ {/if}
102
+ {/snippet}