@heyramzi/ui 2.0.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (341) hide show
  1. package/package.json +48 -24
  2. package/scripts/sync-shadcn.mjs +3 -4
  3. package/src/agents/agent-avatar.svelte +604 -575
  4. package/src/agents/agent-cast.svelte +179 -183
  5. package/src/agents/demo.svelte +125 -113
  6. package/src/agents/helpers.ts +23 -17
  7. package/src/agents/index.ts +4 -4
  8. package/src/agents/page-state.ts +6 -6
  9. package/src/agents/types.ts +6 -6
  10. package/src/ai/ai-button/AiButton.svelte +4 -12
  11. package/src/ai/generation-progress/GenerationProgress.svelte +10 -3
  12. package/src/ai/prompt-input/PromptInputTextarea.svelte +1 -2
  13. package/src/ai/response/Response.css +2 -3
  14. package/src/animations/avatar-group-hover.css +2 -16
  15. package/src/animations/card-tilt.css +3 -6
  16. package/src/animations/digit-pop-in.css +2 -15
  17. package/src/animations/dropdown.css +4 -17
  18. package/src/animations/input-clear-dissolve.css +5 -18
  19. package/src/animations/input-shake.css +9 -41
  20. package/src/animations/learn-more-hover.css +17 -9
  21. package/src/animations/like-button.css +2 -4
  22. package/src/animations/page-side-by-side.css +2 -10
  23. package/src/animations/plus-menu-morph.css +2 -5
  24. package/src/animations/shimmer-text.css +5 -18
  25. package/src/animations/spinning-counter.css +2 -6
  26. package/src/animations/success-check.css +2 -15
  27. package/src/animations/thinking-states.css +2 -7
  28. package/src/brand/brand.test.ts +21 -5
  29. package/src/brand/index.ts +4 -10
  30. package/src/canvas/README.md +9 -12
  31. package/src/canvas/card/card-frame.svelte +43 -39
  32. package/src/canvas/card/resize-handles.svelte +89 -80
  33. package/src/canvas/chrome/chrome-zoom.svelte +101 -100
  34. package/src/canvas/chrome/lock-badge.svelte +8 -8
  35. package/src/canvas/chrome/side-handles.svelte +24 -19
  36. package/src/canvas/chrome/size-chip.svelte +30 -30
  37. package/src/canvas/chrome/snap-guides.svelte +20 -20
  38. package/src/canvas/components/icons/ToolIcon.svelte +8 -8
  39. package/src/canvas/components/shared/svg/PreviewSvg.svelte +19 -19
  40. package/src/canvas/components/shared/svg/ShapeOutline.svelte +71 -48
  41. package/src/canvas/components/shared/svg/StrokeNode.svelte +21 -27
  42. package/src/canvas/components/shared/svg/TextRule.svelte +25 -17
  43. package/src/canvas/edges/bend-handle.svelte +46 -46
  44. package/src/canvas/edges/edge-gradient.svelte +10 -10
  45. package/src/canvas/edges/gradient-edge.svelte +45 -29
  46. package/src/canvas/editor/lock.test.ts +6 -1
  47. package/src/canvas/editor/style.ts +7 -1
  48. package/src/canvas/headless/svg-figure.test.ts +5 -1
  49. package/src/canvas/lod/lod-switch.svelte +111 -106
  50. package/src/canvas/pen/PenField.svelte +87 -81
  51. package/src/canvas/pen/PenOverlay.svelte +127 -127
  52. package/src/canvas/pen/ShapeNode.svelte +31 -32
  53. package/src/canvas/pen/TextNode.svelte +54 -53
  54. package/src/canvas/pen/host-field.test.svelte +7 -7
  55. package/src/canvas/pen/shapes.ts +7 -1
  56. package/src/canvas/screen/GlyphIcon.svelte +22 -17
  57. package/src/canvas/screen/PartView.svelte +138 -72
  58. package/src/canvas/screen/ScreenBody.svelte +81 -60
  59. package/src/canvas/screen/ScreenCard.svelte +20 -20
  60. package/src/canvas/ui/CanvasToolbar.svelte +173 -85
  61. package/src/canvas/ui/FrameLayer.svelte +59 -59
  62. package/src/canvas/ui/PenLayer.svelte +39 -30
  63. package/src/canvas/ui/StylePanel.svelte +119 -102
  64. package/src/canvas/ui/canvas-grid.svelte +137 -129
  65. package/src/charts/animated-value.svelte +1 -1
  66. package/src/charts/area-chart.css +2 -5
  67. package/src/charts/area-chart.svelte +1 -1
  68. package/src/charts/bar-chart.css +2 -3
  69. package/src/charts/bar-chart.svelte +1 -1
  70. package/src/charts/charts.css +5 -17
  71. package/src/charts/crosshair.svelte +1 -1
  72. package/src/charts/dashboard/dashboard-empty.svelte +25 -25
  73. package/src/charts/dashboard/dashboard-section.svelte +24 -24
  74. package/src/charts/dashboard/data-panel.svelte +50 -50
  75. package/src/charts/dashboard/data-sort-head.svelte +56 -56
  76. package/src/charts/dashboard/data-table.svelte +138 -138
  77. package/src/charts/dashboard/panel-grid.svelte +30 -30
  78. package/src/charts/dashboard/period-filter.svelte +25 -25
  79. package/src/charts/dashboard/rating-chip.svelte +19 -19
  80. package/src/charts/dashboard/stat-band.svelte +32 -32
  81. package/src/charts/dashboard/stat-tile.svelte +106 -99
  82. package/src/charts/donut-chart.svelte +1 -1
  83. package/src/charts/edge-fade.svelte +1 -1
  84. package/src/charts/figure.svelte +1 -1
  85. package/src/charts/gauge-chart.css +4 -7
  86. package/src/charts/gauge-chart.svelte +1 -1
  87. package/src/charts/hover-focus.svelte +1 -1
  88. package/src/charts/legend.css +1 -3
  89. package/src/charts/legend.svelte +1 -1
  90. package/src/charts/line-chart.svelte +1 -1
  91. package/src/charts/number-chart.css +6 -9
  92. package/src/charts/number-chart.svelte +1 -1
  93. package/src/charts/reference-line.svelte +19 -19
  94. package/src/charts/reveal.svelte +1 -1
  95. package/src/charts/ring-chart.css +2 -4
  96. package/src/charts/ring-chart.svelte +1 -1
  97. package/src/charts/sparkline.svelte +5 -2
  98. package/src/charts/stat-card.svelte +1 -1
  99. package/src/charts/time-series-chart.css +2 -5
  100. package/src/charts/trend-pill.css +2 -4
  101. package/src/charts/trend-pill.svelte +1 -1
  102. package/src/components/copy/copy-button.svelte +93 -98
  103. package/src/components/copy/copy-button.test.ts +10 -2
  104. package/src/components/data-table/infinite-scroll.svelte +42 -42
  105. package/src/components/data-table/pagination.svelte +34 -22
  106. package/src/components/data-table/row-arrow.svelte +19 -16
  107. package/src/components/data-table/row-context-menu.svelte +52 -52
  108. package/src/components/data-table/sortable-head.svelte +25 -25
  109. package/src/components/data-table/table-skeleton.svelte +41 -36
  110. package/src/components/docs/doc-page.svelte +18 -9
  111. package/src/components/docs/on-this-page.svelte +16 -3
  112. package/src/components/docs/scroll-spy.svelte.ts +2 -3
  113. package/src/components/field-row/field-row.svelte +10 -8
  114. package/src/components/hold-to-confirm/hold-to-confirm.svelte +5 -1
  115. package/src/components/icon/icon.svelte +14 -14
  116. package/src/components/language-flag/language-flag.svelte +3 -1
  117. package/src/components/list-page/detail-empty.svelte +34 -34
  118. package/src/components/list-page/detail-header.svelte +55 -47
  119. package/src/components/list-page/detail-panel.svelte +51 -59
  120. package/src/components/list-page/detail-row.svelte +19 -19
  121. package/src/components/list-page/detail-stack.svelte +21 -21
  122. package/src/components/list-page/empty-state.svelte +30 -30
  123. package/src/components/list-page/filter-item.svelte +20 -20
  124. package/src/components/list-page/filter-popover.svelte +60 -55
  125. package/src/components/list-page/grouped-list.svelte +4 -2
  126. package/src/components/list-page/index.ts +8 -4
  127. package/src/components/list-page/list-page-shell.svelte +2 -5
  128. package/src/components/list-page/master-detail.svelte +87 -88
  129. package/src/components/list-page/notice-bar.svelte +38 -38
  130. package/src/components/list-page/prev-next.svelte +63 -63
  131. package/src/components/list-page/rail-body.svelte +18 -18
  132. package/src/components/list-page/rail-empty.svelte +23 -30
  133. package/src/components/list-page/rail-head.svelte +24 -24
  134. package/src/components/list-page/rail-row.svelte +77 -73
  135. package/src/components/list-page/section-host.ts +2 -3
  136. package/src/components/locale-switch/locale-switch.svelte +41 -41
  137. package/src/components/mark/mark.svelte +34 -38
  138. package/src/components/open-in/index.ts +1 -5
  139. package/src/components/open-in/open-in.svelte +40 -40
  140. package/src/components/picker/mark-picker.svelte +49 -49
  141. package/src/components/picker/picker.svelte +5 -3
  142. package/src/components/section-header/index.ts +1 -0
  143. package/src/components/section-header/section-header.svelte +1 -1
  144. package/src/components/section-header/section-header.test.ts +7 -1
  145. package/src/components/section-header/section-label.svelte +39 -0
  146. package/src/components/segmented/animated-tabs.svelte +31 -2
  147. package/src/components/segmented/collapse.test.ts +2 -1
  148. package/src/components/segmented/index.ts +2 -0
  149. package/src/components/segmented/segment.svelte +52 -49
  150. package/src/components/segmented/segmented-filter.svelte +79 -80
  151. package/src/components/segmented/segmented-nav.svelte +34 -40
  152. package/src/components/segmented/segmented-overflow.svelte +53 -50
  153. package/src/components/segmented/segmented-track.svelte +62 -46
  154. package/src/components/segmented/segmented.ts +2 -3
  155. package/src/components/star-rating-input/star-rating-input.svelte +103 -103
  156. package/src/components/svg/svg.svelte +4 -4
  157. package/src/components/table-view/table-view.svelte +17 -2
  158. package/src/components/table-view/table-view.test.ts +7 -1
  159. package/src/components/table-view/types.ts +11 -11
  160. package/src/components/tags-input/index.ts +13 -0
  161. package/src/components/tags-input/tags-input.svelte +305 -0
  162. package/src/components/tags-input/tags.test.ts +64 -0
  163. package/src/components/tags-input/tags.ts +142 -0
  164. package/src/components/theme/index.ts +2 -4
  165. package/src/components/theme/theme-provider.svelte +1 -3
  166. package/src/components/toolbar/index.ts +2 -0
  167. package/src/components/{list-page → toolbar}/toolbar-search.svelte +25 -25
  168. package/src/components/toolbar/toolbar.svelte +13 -9
  169. package/src/components/toolbar/toolbar.test.ts +10 -1
  170. package/src/design/index.css +21 -78
  171. package/src/design/polish.css +26 -35
  172. package/src/editor/editor-toolbar.svelte +104 -99
  173. package/src/editor/editor.svelte +296 -266
  174. package/src/editor/image-toolbar.svelte +20 -22
  175. package/src/editor/index.ts +1 -4
  176. package/src/editor/inline-editor.svelte +280 -238
  177. package/src/editor/markdown.ts +15 -15
  178. package/src/editor/renderer.svelte +71 -71
  179. package/src/editor/slash-menu.svelte +24 -27
  180. package/src/effects/animated-beam/diagrams/BeamSvg.svelte +1 -1
  181. package/src/effects/image-comparison/ImageComparison.svelte +11 -1
  182. package/src/effects/interactive-hover-button/InteractiveHoverButton.svelte +59 -46
  183. package/src/effects/magic-card/MagicCard.css +14 -7
  184. package/src/effects/marquee/Marquee.svelte +3 -5
  185. package/src/effects/reveal/observe.ts +0 -3
  186. package/src/effects/spotlight/Spotlight.svelte +1 -1
  187. package/src/effects/theme-toggle/ThemeToggle.css +25 -22
  188. package/src/effects/theme-toggle/ThemeToggle.svelte +6 -7
  189. package/src/gallery.test.ts +14 -4
  190. package/src/inbox/README.md +27 -0
  191. package/src/inbox/inbox.test.ts +214 -0
  192. package/src/inbox/index.ts +31 -0
  193. package/src/inbox/logic.ts +169 -0
  194. package/src/inbox/thread-inbox.svelte +439 -0
  195. package/src/inbox/types.ts +161 -0
  196. package/src/lib/theme-transition.ts +2 -4
  197. package/src/lib/tone.ts +2 -4
  198. package/src/lib/utils.ts +2 -2
  199. package/src/modules.test.ts +105 -0
  200. package/src/onboarding/define-tours.test.ts +55 -40
  201. package/src/onboarding/define-tours.ts +23 -23
  202. package/src/onboarding/demo.svelte +24 -5
  203. package/src/onboarding/index.ts +1 -8
  204. package/src/onboarding/onboarding-provider.svelte +19 -7
  205. package/src/onboarding/onboarding.svelte.ts +16 -16
  206. package/src/onboarding/pick.test.ts +61 -52
  207. package/src/onboarding/pick.ts +16 -13
  208. package/src/onboarding/stores.test.ts +80 -80
  209. package/src/onboarding/stores.ts +41 -39
  210. package/src/onboarding/tour-card.svelte +30 -5
  211. package/src/onboarding/tour-runner.svelte +12 -3
  212. package/src/onboarding/types.ts +20 -12
  213. package/src/onboarding/wait.ts +22 -22
  214. package/src/panel/README.md +5 -0
  215. package/src/panel/index.ts +2 -0
  216. package/src/panel/side-panel.svelte +194 -0
  217. package/src/record/README.md +3 -0
  218. package/src/record/demo-data.ts +352 -0
  219. package/src/record/field-section.svelte +57 -0
  220. package/src/record/fields.ts +194 -0
  221. package/src/record/format.ts +94 -0
  222. package/src/{tasks/record → record}/index.ts +49 -35
  223. package/src/{tasks/record → record}/info-list.svelte +145 -114
  224. package/src/record/kinds.ts +42 -0
  225. package/src/record/logic.ts +326 -0
  226. package/src/record/next-up.svelte +81 -0
  227. package/src/{tasks/record → record}/project-list.svelte +56 -50
  228. package/src/{tasks/record → record}/record-demo.svelte +130 -110
  229. package/src/{tasks/record → record}/record-header.svelte +37 -28
  230. package/src/{tasks/record → record}/record-shell.svelte +54 -54
  231. package/src/record/record-stats.svelte +48 -0
  232. package/src/record/record-timeline.svelte +251 -0
  233. package/src/record/record.test.ts +471 -0
  234. package/src/{tasks/record → record}/steps-rail.svelte +225 -216
  235. package/src/record/types.ts +299 -0
  236. package/src/record/where-we-are.svelte +125 -0
  237. package/src/shadcn/button/button.svelte +1 -1
  238. package/src/shadcn/card/card-title.svelte +1 -5
  239. package/src/shadcn/hooks/use-highlighted-code.svelte.ts +2 -13
  240. package/src/shadcn/sheet/sheet-content.svelte +2 -4
  241. package/src/shadcn/table/table-cell.svelte +2 -3
  242. package/src/shadcn/utilities.css +3 -8
  243. package/src/slides/Controls.svelte +150 -124
  244. package/src/slides/Deck.svelte +243 -238
  245. package/src/slides/Fragment.svelte +64 -57
  246. package/src/slides/Overview.svelte +106 -101
  247. package/src/slides/Presenter.svelte +300 -266
  248. package/src/slides/Slide.svelte +185 -170
  249. package/src/slides/SlideList.svelte +16 -12
  250. package/src/slides/ambient.d.ts +1 -1
  251. package/src/slides/bridges.ts +42 -23
  252. package/src/slides/deck-controller.svelte.ts +47 -35
  253. package/src/slides/demo-slides.svelte +330 -306
  254. package/src/slides/demo.svelte +164 -160
  255. package/src/slides/export/capture.ts +20 -16
  256. package/src/slides/export/ffmpeg.ts +43 -38
  257. package/src/slides/export/index.ts +5 -5
  258. package/src/slides/export/launch.ts +3 -1
  259. package/src/slides/export/pdf.ts +30 -27
  260. package/src/slides/export/types.ts +10 -8
  261. package/src/slides/index.ts +11 -11
  262. package/src/slides/labels.ts +22 -19
  263. package/src/slides/layout.ts +9 -2
  264. package/src/slides/model.ts +4 -4
  265. package/src/slides/navigation.ts +43 -31
  266. package/src/slides/theme.css +5 -7
  267. package/src/slides/types.ts +9 -10
  268. package/src/status/README.md +18 -0
  269. package/src/status/define-statuses.ts +25 -0
  270. package/src/status/index.ts +11 -0
  271. package/src/{tasks → status}/status-badge.svelte +3 -1
  272. package/src/{tasks → status}/status-dot.svelte +1 -3
  273. package/src/{tasks → status}/status-icon.svelte +1 -1
  274. package/src/status/status-types.ts +40 -0
  275. package/src/tasks/README.md +1 -1
  276. package/src/tasks/board-order.ts +2 -4
  277. package/src/tasks/demo.svelte +192 -34
  278. package/src/tasks/detail/detail-demo.svelte +75 -15
  279. package/src/tasks/detail/detail-types.ts +45 -45
  280. package/src/tasks/detail/task-detail.svelte +40 -10
  281. package/src/tasks/detail/task-property.svelte +8 -2
  282. package/src/tasks/detail/task-thread.svelte +53 -12
  283. package/src/tasks/gantt/gantt-bar.svelte +17 -6
  284. package/src/tasks/gantt/gantt-grid.svelte +40 -9
  285. package/src/tasks/gantt/gantt-group-row.svelte +8 -2
  286. package/src/tasks/gantt/gantt-header.svelte +9 -3
  287. package/src/tasks/gantt/gantt-task-row.svelte +26 -6
  288. package/src/tasks/gantt/gantt.css +2 -1
  289. package/src/tasks/gantt/gantt.test.ts +4 -2
  290. package/src/tasks/gantt/logic.test.ts +110 -20
  291. package/src/tasks/gantt/logic.ts +42 -6
  292. package/src/tasks/inbox/inbox-card.svelte +15 -7
  293. package/src/tasks/inbox/inbox-draft.svelte +1 -1
  294. package/src/tasks/inbox/inbox-logic.ts +6 -40
  295. package/src/tasks/inbox/inbox-statuses.ts +24 -24
  296. package/src/tasks/inbox/inbox-trace.svelte +9 -3
  297. package/src/tasks/inbox/inbox-types.ts +54 -58
  298. package/src/tasks/inbox/index.ts +22 -21
  299. package/src/tasks/index.ts +52 -53
  300. package/src/tasks/kanban-board.svelte +16 -12
  301. package/src/tasks/kanban-types.ts +2 -3
  302. package/src/tasks/presets/sales.ts +7 -7
  303. package/src/tasks/task-gantt.svelte +28 -8
  304. package/src/tasks/task-list.svelte +41 -17
  305. package/src/tasks/time/duration.ts +40 -40
  306. package/src/tasks/time/time-chip.svelte +9 -2
  307. package/src/tasks/time/time-dot.svelte +1 -1
  308. package/src/tasks/time/time-ring.svelte +11 -3
  309. package/src/tasks/time/time-tracker.svelte +57 -15
  310. package/src/tasks/time/time-types.ts +57 -57
  311. package/src/tasks/use-kanban-dnd.svelte.ts +2 -3
  312. package/src/thread/README.md +31 -0
  313. package/src/thread/index.ts +12 -0
  314. package/src/thread/logic.ts +61 -0
  315. package/src/thread/thread.svelte +432 -0
  316. package/src/thread/thread.test.ts +73 -0
  317. package/src/thread/types.ts +93 -0
  318. package/src/tokens/craft.css +17 -53
  319. package/src/tokens/index.css +3 -13
  320. package/src/tokens/palette.css +13 -11
  321. package/src/tokens/primitives.css +10 -23
  322. package/src/components/list-page/section-label.svelte +0 -39
  323. package/src/tasks/define-statuses.ts +0 -32
  324. package/src/tasks/inbox/inbox.svelte +0 -203
  325. package/src/tasks/record/demo-data.ts +0 -286
  326. package/src/tasks/record/field-section.svelte +0 -53
  327. package/src/tasks/record/fields.ts +0 -142
  328. package/src/tasks/record/format.ts +0 -87
  329. package/src/tasks/record/kinds.ts +0 -42
  330. package/src/tasks/record/logic.ts +0 -324
  331. package/src/tasks/record/next-up.svelte +0 -70
  332. package/src/tasks/record/record-stats.svelte +0 -48
  333. package/src/tasks/record/record-timeline.svelte +0 -231
  334. package/src/tasks/record/record.test.ts +0 -350
  335. package/src/tasks/record/types.ts +0 -280
  336. package/src/tasks/record/where-we-are.svelte +0 -121
  337. package/src/tasks/side-panel.svelte +0 -193
  338. package/src/tasks/status-types.ts +0 -40
  339. /package/src/components/{list-page → toolbar}/toolbar-trigger.ts +0 -0
  340. /package/src/{tasks → panel}/side-panel-types.ts +0 -0
  341. /package/src/{tasks/record → record}/gutter.ts +0 -0
@@ -1,8 +1,6 @@
1
1
  /* ── Radius, by role not by size ─────────────────────────────
2
- A chip inside a control inside a card has to read as nested, which needs
3
- three radii that step, not one radius reused. The Tailwind names are the
4
- home so `rounded-lg` on a button already resolves to the control radius and
5
- no component has to learn a new class. */
2
+ These override Tailwind's own names, so `rounded-lg` on a button already
3
+ resolves to the control radius. */
6
4
  @theme {
7
5
  --radius-xs: 4px; /* tag, dot, inline code */
8
6
  --radius-sm: 6px; /* chip, badge, small pill */
@@ -14,30 +12,9 @@
14
12
  }
15
13
 
16
14
  /* ── Elevation ───────────────────────────────────────────────
17
- Four steps of lift, plus the two edges that are not lift.
18
-
19
- WHY the steps are shadow-only and the hairline is its own token: most boxes
20
- in a kit already draw a border, and an elevation that bakes a `0 0 0 1px`
21
- ring into every step gives those boxes two edges a pixel apart. Compose them
22
- where a component drops its border:
23
-
24
- box-shadow: var(--elev-hairline), var(--elev-card);
25
-
26
- WHY each step is stacked rather than one blurred drop: a single shadow reads
27
- as a sticker. Depth is several offsets falling off together, which is what
28
- the eye reads as distance. They cost nothing extra because every stop is the
29
- same colour at a different alpha.
30
-
31
- WHY the blur radii are large and the alphas small: a 4px blur at 20% draws a
32
- visible grey edge, which the eye reads as a second border. A 28px blur at 7%
33
- is barely a colour anywhere and reads as height. Reach for more blur before
34
- more alpha, every time.
35
-
36
- `--elev-tint` is the one knob an app turns. Pure black greys a warm palette
37
- out; the hue of the app's own darkest surface keeps the shadow in the
38
- family. */
15
+ The steps carry no ring, since a bordered box would get two edges a pixel
16
+ apart; a borderless one adds `var(--elev-hairline)` in front. */
39
17
  :root {
40
-
41
18
  /* Resting on the page. A button, a chip, a row. */
42
19
  --elev-btn:
43
20
  0 1px 1px -0.5px color-mix(in oklab, var(--elev-tint) 7%, transparent),
@@ -61,9 +38,8 @@
61
38
  0 12px 26px -8px color-mix(in oklab, var(--elev-tint) 12%, transparent),
62
39
  0 40px 72px -20px color-mix(in oklab, var(--elev-tint) 20%, transparent);
63
40
 
64
- /* The two edges. A hairline separates a surface from what is behind it; a rim
65
- is the light catching its top edge, which is what makes a filled control
66
- read as a physical key rather than a coloured rectangle. */
41
+ /* A hairline separates a surface from what's behind it; a rim is light on the
42
+ top edge, which makes a filled control read as a key. */
67
43
  --elev-hairline: 0 0 0 1px var(--color-border, var(--border));
68
44
  --elev-rim: inset 0 1px 0 color-mix(in oklab, white 16%, transparent);
69
45
 
@@ -71,10 +47,8 @@
71
47
  --elev-field: inset 0 1px 2px color-mix(in oklab, var(--elev-tint) 7%, transparent);
72
48
  }
73
49
 
74
- /* A dark surface cannot cast a darker shadow than the page it sits on, so the
75
- hairline does the separating and the shadow only sinks what is behind it. On
76
- dark the hairline is white at a low alpha, which is what reads as a lit edge
77
- rather than as a drawn border. */
50
+ /* A shadow can't get darker than a dark page, so on dark the low-alpha white
51
+ hairline does the separating. */
78
52
  .dark {
79
53
  --elev-btn: 0 1px 2px color-mix(in oklab, black 30%, transparent);
80
54
  --elev-card:
@@ -93,25 +67,19 @@
93
67
  }
94
68
 
95
69
  /* ── Focus ───────────────────────────────────────────────────
96
- One ring, two parts: a solid edge that redraws the control's own outline in
97
- the accent, and a soft halo that carries it off the surface. Written as a
98
- variable so a component spends one line on it and every control focuses the
99
- same way. */
70
+ One variable, so every control focuses the same way: a solid accent edge,
71
+ then a soft halo. */
100
72
  :root {
101
73
  --ring-color: var(--color-ring, var(--ring));
102
74
  --ring-halo: color-mix(in oklab, var(--ring-color) 16%, transparent);
103
- /* A solid pixel redrawing the control's own edge, then a wide soft band. At a
104
- higher alpha the band reads as a second border rather than as a glow, which
105
- is why it is 16% over 4px and not 30% over 2px. */
75
+ /* The halo is 16% over 4px, not 30% over 2px, because a stronger band reads
76
+ as a second border instead of a glow. */
106
77
  --ring-focus: 0 0 0 1px var(--ring-color), 0 0 0 5px var(--ring-halo);
107
78
  }
108
79
 
109
80
  /* ── Motion ──────────────────────────────────────────────────
110
- The transitions-dev scale, verbatim, so a snippet pasted from that skill and
111
- a component written here read from the same names.
112
-
113
- The doctrine in one line: an open is an invitation and a close should get out
114
- of the way, so every close is shorter than its open and no close bounces. */
81
+ The transitions-dev scale, verbatim, so a pasted snippet reads the same
82
+ names. Every close is shorter than its open, and no close bounces. */
115
83
  :root {
116
84
  --duration-stagger: 40ms;
117
85
  --duration-micro: 80ms;
@@ -163,13 +131,9 @@
163
131
 
164
132
  --ease-smooth-out: var(--ease-smooth-out);
165
133
 
166
- /* WHY the durations are re-declared under a second name: Tailwind's
167
- `duration-*` utility reads `--transition-duration-*`, not `--duration-*`, so
168
- the six names above generated no class at all. `duration-fast` therefore
169
- compiled to nothing and the element ended up at `transition-duration: 0s`:
170
- no error, no warning, and a component that simply does not animate. The
171
- comment at the top of this block had claimed `duration-fast` was a class
172
- since the block was written. Found 31 Aug 2026 by probing a live page. */
134
+ /* Tailwind's `duration-*` reads `--transition-duration-*`, so without these
135
+ `duration-fast` silently compiles to nothing and the element doesn't animate
136
+ (31 Aug 2026). */
173
137
  --transition-duration-micro: var(--duration-micro);
174
138
  --transition-duration-quick: var(--duration-quick);
175
139
  --transition-duration-fast: var(--duration-fast);
@@ -1,16 +1,6 @@
1
- /* tokens.css
2
- *
3
- * The shared style layer for @heyramzi/ui, imported once after Tailwind:
4
- *
5
- * @import "tailwindcss";
6
- * @import "@heyramzi/ui/tokens.css";
7
- * @import "@heyramzi/ui/animations";
8
- * @import "@heyramzi/ui/shadcn/utilities.css"; // only if you use Attachment or Message
9
- *
10
- * It carries three things: the color palette and semantic tokens, the craft
11
- * layer (radius, elevation, focus and motion), and the small set of motion
12
- * primitives the components share. Override any variable on :root to retheme.
13
- * The parts load in this order because later blocks read earlier variables.
1
+ /* tokens.css: import once, right after `@import "tailwindcss"`. Override any
2
+ * variable on :root to retheme; the parts load in this order because later ones
3
+ * read earlier variables.
14
4
  */
15
5
 
16
6
  @import "./palette.css";
@@ -22,9 +22,8 @@
22
22
  --gray-900: oklch(0.15 0 0); /* Near-black — headings, primary text */
23
23
 
24
24
  /* === Brand roles ===
25
- The look of a product, named by what each colour does. These defaults are neutral, and a
26
- brand replaces them through `@heyramzi/ui/brand` (typed colours, no build step). Every
27
- other token below reads from these, so nothing else in the package names a hue. */
25
+ Neutral defaults a brand replaces through `@heyramzi/ui/brand`; every token below reads
26
+ from these, so nothing else in the package names a hue. */
28
27
  --brand: oklch(0.28 0 0); /* The signature colour: focus ring, links, active states, the mark */
29
28
  --brand-on-dark: oklch(0.92 0 0); /* The same colour lifted for a dark page */
30
29
  --brand-highlight: oklch(0.78 0 0); /* Warmth: badges, highlights, soft accents */
@@ -39,9 +38,9 @@
39
38
  --brand-surface-light: oklch(0.99 0 0); /* Cards on a light page */
40
39
  --brand-line-light: oklch(0.91 0 0); /* Borders on a light page */
41
40
 
42
- /* === Tint scales, derived from the three hues so a brand types one colour each ===
43
- WHY mixes and not typed steps: a brand that sets `--brand` gets a matching 100 to 800
44
- with nothing else to maintain. 400 is the colour lifted for dark, 600 the colour itself. */
41
+ /* === Tint scales ===
42
+ Mixed from the three hues so `--brand` alone yields 100 to 800; 400 is the colour lifted
43
+ for dark, 600 the colour itself. */
45
44
  --brand-100: color-mix(in oklab, var(--brand) 12%, white);
46
45
  --brand-200: color-mix(in oklab, var(--brand) 28%, white);
47
46
  --brand-400: var(--brand-on-dark);
@@ -117,8 +116,7 @@
117
116
  --sidebar-ring: var(--brand);
118
117
 
119
118
  /* === Shadow tint ===
120
- The one knob the design layer exposes. Pure black greys out warm surfaces,
121
- so the shadow takes the darkest surface's hue. Radius, focus and motion live there. */
119
+ Pure black greys out warm surfaces, so the shadow takes the darkest surface's hue. */
122
120
  --elev-tint: var(--brand-ink);
123
121
  }
124
122
 
@@ -198,14 +196,18 @@
198
196
  --color-cat-indigo: var(--cat-indigo);
199
197
  --color-cat-purple: var(--cat-purple);
200
198
 
201
- /* A hue as text or icon on its own tint: 40% toward the foreground clears AA in both themes.
202
- A tone's 10% tint under plain destructive text is 3.6:1; the ink mix is not. */
199
+ /* A hue as text on its own tint mixes 40% toward the foreground to clear AA in both themes,
200
+ where plain destructive text on that tint is 3.6:1. */
203
201
  --color-cat-cyan-ink: color-mix(in oklab, var(--color-cat-cyan) 60%, var(--color-foreground));
204
202
  --color-cat-teal-ink: color-mix(in oklab, var(--color-cat-teal) 60%, var(--color-foreground));
205
203
  --color-cat-purple-ink: color-mix(in oklab, var(--color-cat-purple) 60%, var(--color-foreground));
206
204
  --color-cat-amber-ink: color-mix(in oklab, var(--color-cat-amber) 60%, var(--color-foreground));
207
205
  --color-success-ink: color-mix(in oklab, var(--color-success) 60%, var(--color-foreground));
208
- --color-destructive-ink: color-mix(in oklab, var(--color-destructive) 60%, var(--color-foreground));
206
+ --color-destructive-ink: color-mix(
207
+ in oklab,
208
+ var(--color-destructive) 60%,
209
+ var(--color-foreground)
210
+ );
209
211
 
210
212
  /* Light theme color system: the light surfaces and lines of the brand roles */
211
213
  --color-background: var(--gray-50);
@@ -1,11 +1,5 @@
1
- /* The bits-ui state variants every primitive in `./shadcn` is written against.
2
- *
3
- * bits-ui v2 writes `data-checked` / `data-open` and v1 wrote
4
- * `data-state="checked"`, so each variant matches both and a component does not
5
- * have to know which it is talking to. They live here rather than in a
6
- * consumer's stylesheet because the classes that need them ship in this
7
- * package: an app that defines only the palette would render a switch that
8
- * never moves. */
1
+ /* Each variant matches both bits-ui v2's `data-checked` and v1's `data-state="checked"`, and
2
+ * ships here because an app that defines only the palette gets a switch that never moves. */
9
3
  @custom-variant data-open {
10
4
  &:where([data-state="open"]),
11
5
  &:where([data-open]:not([data-open="false"])) {
@@ -80,9 +74,8 @@
80
74
  background: color-mix(in oklab, var(--ring-color) 24%, transparent);
81
75
  }
82
76
 
83
- /* A default macOS overlay scrollbar paints a black bar on a dark panel. Track
84
- transparent, thumb on the line token, so it belongs to whatever surface it
85
- is over. The border is what insets the thumb without a second element. */
77
+ /* A default macOS overlay scrollbar paints a black bar on a dark panel, so the track stays
78
+ transparent and the thumb takes the line token. */
86
79
  :where(html) {
87
80
  scrollbar-color: var(--color-border, var(--border)) transparent;
88
81
  }
@@ -111,16 +104,11 @@
111
104
  }
112
105
 
113
106
  @layer components {
114
- /* Three moves the kit repeats. They are classes rather than tokens because a
115
- component wants one name, not four declarations, and because the reduced
116
- motion guard then lives in one place instead of in every component.
107
+ /* Classes, not tokens, so a component takes one name and the reduced-motion guard lives in
108
+ one place; the `t-` prefix matches the transitions-dev snippets. */
117
109
 
118
- Namespaced `t-` to match the transitions-dev snippets, which these sit
119
- beside rather than duplicate. */
120
-
121
- /* A control that reads as a key: it travels down a pixel under the finger.
122
- The travel is what sells it, because a colour change alone reads as a hover
123
- state, not as a press. */
110
+ /* A key-like control travels down a pixel under the finger, because a colour change alone
111
+ reads as a hover, not a press. */
124
112
  .t-press {
125
113
  transition:
126
114
  background-color var(--duration-quick) var(--ease-smooth-out),
@@ -134,9 +122,8 @@
134
122
  transform: translateY(1px);
135
123
  }
136
124
 
137
- /* A card that answers the pointer. It rises by a pixel and gains the next
138
- step of elevation, so the hover reads as the card coming forward rather
139
- than as its border changing colour. */
125
+ /* A hover card rises a pixel and gains the next elevation step, so it reads as coming
126
+ forward, not as its border changing colour. */
140
127
  .t-lift {
141
128
  transition:
142
129
  box-shadow var(--duration-fast) var(--ease-smooth-out),
@@ -1,39 +0,0 @@
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
- // The one label style for panel, rail group and sub-section headers. Retyped
14
- // copies drifted to a different tracking value.
15
- icon?: PhosphorIcon;
16
- children: Snippet;
17
- class?: string;
18
- };
19
-
20
- // =========================
21
- // PROPS
22
- // =========================
23
- let { icon: Icon, children, class: className }: Props = $props();
24
- </script>
25
-
26
- <!-- =========================== -->
27
- <!-- MARKUP -->
28
- <!-- =========================== -->
29
- <span
30
- class={cn(
31
- "flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.14em] text-muted-foreground",
32
- className,
33
- )}
34
- >
35
- {#if Icon}
36
- <Icon class="size-4 text-brand" aria-hidden="true" />
37
- {/if}
38
- {@render children()}
39
- </span>
@@ -1,32 +0,0 @@
1
- import type { StatusDef, StatusId, StatusMap, Statuses } from "./status-types.js";
2
-
3
- /**
4
- * Build a status registry from a plain map. The registry keeps the map's keys as
5
- * a union, so `statuses.ids` and lookups stay typed.
6
- *
7
- * ```ts
8
- * const statuses = defineStatuses({
9
- * draft: { label: "Draft", tone: "neutral" },
10
- * sent: { label: "Sent", tone: "info" },
11
- * });
12
- * ```
13
- */
14
- export function defineStatuses<const M extends StatusMap>(
15
- map: M,
16
- ): Statuses & { ids: StatusId<M>[] } {
17
- const lookup = map as Record<string, StatusDef | undefined>;
18
- const find = (id: string): StatusDef | undefined => lookup[id];
19
-
20
- return {
21
- map,
22
- ids: Object.keys(map) as StatusId<M>[],
23
- has: (id) => find(id) !== undefined,
24
- get: find,
25
- label: (id) => find(id)?.label ?? id,
26
- tone: (id) => find(id)?.tone ?? "neutral",
27
- icon: (id) => find(id)?.icon,
28
- variant: (id) => find(id)?.variant ?? "open",
29
- progress: (id) => find(id)?.progress,
30
- color: (id) => find(id)?.color,
31
- };
32
- }
@@ -1,203 +0,0 @@
1
- <script lang="ts">
2
- import { tick, type Snippet } from "svelte";
3
- import { Badge } from "../../shadcn/badge/index.js";
4
- import { Button } from "../../shadcn/button/index.js";
5
- import * as Empty from "../../shadcn/empty/index.js";
6
- import { Kbd, KbdGroup } from "../../shadcn/kbd/index.js";
7
- import { Tabs, TabsList, TabsTrigger } from "../../shadcn/tabs/index.js";
8
- import { ToggleGroup, ToggleGroupItem } from "../../shadcn/toggle-group/index.js";
9
- import { cn } from "../../lib/utils.js";
10
- import StatusDot from "../status-dot.svelte";
11
- import InboxCard from "./inbox-card.svelte";
12
- import { flatten, groupByProject, inLane, isTyping, laneOf } from "./inbox-logic.js";
13
- import { inboxKinds, inboxStates } from "./inbox-statuses.js";
14
- import type {
15
- InboxItem,
16
- InboxKindFilter,
17
- InboxLane,
18
- InboxProject,
19
- InboxVerdict,
20
- } from "./inbox-types.js";
21
-
22
- interface Props {
23
- items: InboxItem[];
24
- /** The host applies the verdict and passes the updated items back. */
25
- onverdict: (verdict: InboxVerdict) => void;
26
- /** Replaces an item's summary lines. */
27
- renderProposal?: Snippet<[InboxItem]>;
28
- /** Trailing content in a project's header, such as a link to its timeline. */
29
- renderGroupAction?: Snippet<[InboxProject]>;
30
- class?: string;
31
- };
32
-
33
- let { items, onverdict, renderProposal, renderGroupAction, class: className }: Props = $props();
34
-
35
- const LANES: { id: InboxLane; label: string }[] = [
36
- { id: "needs", label: "Needs you" },
37
- { id: "auto", label: "Done by the agent" },
38
- { id: "decided", label: "Decided" },
39
- ];
40
-
41
- let lane = $state<InboxLane>("needs");
42
- let kind = $state<InboxKindFilter>("all");
43
- let cursor = $state(0);
44
- let cards = $state<Record<string, ReturnType<typeof InboxCard> | null>>({});
45
-
46
- let counts = $derived(
47
- Object.fromEntries(LANES.map((l) => [l.id, items.filter((i) => laneOf(i) === l.id).length])) as Record<
48
- InboxLane,
49
- number
50
- >,
51
- );
52
- let groups = $derived(groupByProject(inLane(items, lane, kind)));
53
- let visible = $derived(flatten(groups));
54
- // WHY an index and not an id: when a verdict removes the item under the cursor,
55
- // the same index lands on the next one, so triage keeps moving down the queue.
56
- let selected = $derived(visible[Math.min(cursor, visible.length - 1)]);
57
-
58
- async function move(step: number) {
59
- if (!visible.length) return;
60
- cursor = Math.max(0, Math.min(visible.length - 1, cursor + step));
61
- await tick();
62
- document.querySelector<HTMLElement>(`[data-inbox-item="${CSS.escape(visible[cursor].id)}"]`)?.focus();
63
- }
64
-
65
- function onkeydown(event: KeyboardEvent) {
66
- if (event.defaultPrevented || event.metaKey || event.ctrlKey || event.altKey) return;
67
- if (isTyping(event.target)) return;
68
- const card = selected ? cards[selected.id] : null;
69
- const actions: Record<string, (() => void) | undefined> = {
70
- j: () => move(1),
71
- k: () => move(-1),
72
- a: () => card?.approve(),
73
- d: () => card?.decline(),
74
- s: () => card?.steer(),
75
- e: () => card?.edit(),
76
- u: () => card?.undo(),
77
- };
78
- const run = actions[event.key];
79
- if (!run) return;
80
- event.preventDefault();
81
- run();
82
- }
83
-
84
- function setLane(next: string) {
85
- lane = next as InboxLane;
86
- cursor = 0;
87
- }
88
-
89
- function setKind(next: string | undefined) {
90
- kind = (next || "all") as InboxKindFilter;
91
- cursor = 0;
92
- }
93
- </script>
94
-
95
- <!-- WHY the key handler sits on the region: the shortcuts work wherever focus is inside the inbox and nowhere else on the page. -->
96
- <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
97
- <div
98
- role="region"
99
- aria-label="Inbox"
100
- class={cn("flex h-full min-h-0 flex-col gap-3", className)}
101
- {onkeydown}
102
- >
103
- <div class="flex flex-wrap items-center justify-between gap-2">
104
- <Tabs value={lane} onValueChange={setLane}>
105
- <TabsList>
106
- {#each LANES as l (l.id)}
107
- <TabsTrigger value={l.id} class="gap-2 px-3">
108
- {l.label}
109
- <span class="text-muted-foreground text-xs tabular-nums">{counts[l.id]}</span>
110
- </TabsTrigger>
111
- {/each}
112
- </TabsList>
113
- </Tabs>
114
- <ToggleGroup
115
- type="single"
116
- variant="outline"
117
- size="sm"
118
- value={kind}
119
- onValueChange={setKind}
120
- aria-label="Filter by kind"
121
- >
122
- <ToggleGroupItem value="all">All</ToggleGroupItem>
123
- {#each inboxKinds.ids as id (id)}
124
- <ToggleGroupItem value={id}>{inboxKinds.label(id)}</ToggleGroupItem>
125
- {/each}
126
- </ToggleGroup>
127
- </div>
128
-
129
- <div class="-mx-1 min-h-0 flex-1 overflow-y-auto px-1 pb-1">
130
- {#if !visible.length}
131
- <Empty.Root class="h-full">
132
- <Empty.Header>
133
- <Empty.Media>
134
- <StatusDot statuses={inboxStates} status="approved" size={28} />
135
- </Empty.Media>
136
- {#if kind !== "all"}
137
- <Empty.Title>Nothing of this kind here</Empty.Title>
138
- <Empty.Description>Show every kind to see the rest of this lane.</Empty.Description>
139
- {:else if lane === "needs"}
140
- <Empty.Title>Queue clear</Empty.Title>
141
- <Empty.Description>The agent handled the rest, and nothing is waiting on you.</Empty.Description>
142
- {:else if lane === "auto"}
143
- <Empty.Title>The agent hasn't done anything on its own yet</Empty.Title>
144
- <Empty.Description>Safe work it finishes by itself lands here, with an undo.</Empty.Description>
145
- {:else}
146
- <Empty.Title>No verdicts yet</Empty.Title>
147
- <Empty.Description>What you approve or decline shows here and on the project timeline.</Empty.Description>
148
- {/if}
149
- </Empty.Header>
150
- <Empty.Content>
151
- {#if kind !== "all"}
152
- <Button size="sm" variant="outline" onclick={() => setKind("all")}>Show every kind</Button>
153
- {:else if lane === "needs" && counts.auto}
154
- <Button size="sm" variant="outline" onclick={() => setLane("auto")}>See what the agent did</Button>
155
- {:else if lane !== "needs" && counts.needs}
156
- <Button size="sm" variant="outline" onclick={() => setLane("needs")}>Go to the queue</Button>
157
- {/if}
158
- </Empty.Content>
159
- </Empty.Root>
160
- {:else}
161
- <div class="flex flex-col gap-5">
162
- {#each groups as group (group.project.id)}
163
- <section aria-label={group.project.name} class="flex flex-col gap-2">
164
- <header class="flex items-center gap-2 px-1">
165
- <h2 class="text-sm font-medium">{group.project.name}</h2>
166
- {#if group.project.client}
167
- <span class="text-muted-foreground text-sm">{group.project.client}</span>
168
- {/if}
169
- <Badge variant="outline" class="tabular-nums">{group.items.length}</Badge>
170
- <div class="ml-auto flex items-center gap-1">
171
- {#if group.project.href}
172
- <Button size="sm" variant="ghost" href={group.project.href}>Timeline</Button>
173
- {/if}
174
- {@render renderGroupAction?.(group.project)}
175
- </div>
176
- </header>
177
- <div class="flex flex-col gap-2">
178
- {#each group.items as item (item.id)}
179
- <InboxCard
180
- bind:this={cards[item.id]}
181
- {item}
182
- selected={selected?.id === item.id}
183
- onselect={() => (cursor = visible.findIndex((v) => v.id === item.id))}
184
- {onverdict}
185
- {renderProposal}
186
- />
187
- {/each}
188
- </div>
189
- </section>
190
- {/each}
191
- </div>
192
- {/if}
193
- </div>
194
-
195
- <footer class="text-muted-foreground flex flex-wrap items-center gap-x-4 gap-y-1 text-xs max-sm:hidden">
196
- <span class="inline-flex items-center gap-1.5"><KbdGroup><Kbd>j</Kbd><Kbd>k</Kbd></KbdGroup> move</span>
197
- <span class="inline-flex items-center gap-1.5"><Kbd>a</Kbd> approve</span>
198
- <span class="inline-flex items-center gap-1.5"><Kbd>d</Kbd> decline</span>
199
- <span class="inline-flex items-center gap-1.5"><Kbd>s</Kbd> steer</span>
200
- <span class="inline-flex items-center gap-1.5"><Kbd>e</Kbd> edit draft</span>
201
- <span class="inline-flex items-center gap-1.5"><Kbd>u</Kbd> undo</span>
202
- </footer>
203
- </div>