@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,10 +1,5 @@
1
- /* Thinking states — transitions.dev 28.
2
- A status line that swaps copy while a model works. This file owns only the
3
- swap: JS exits the old line upward and enters the new one from below, held
4
- back by --think-gap. The sweep is `.t-shimmer` from shimmer-text.css — put
5
- both classes on the line and keep data-text in sync with the text, or the
6
- shimmer paints the wrong words. The hidden sizer carries your longest state
7
- and is what stops the box resizing mid-swap. */
1
+ /* Thinking states, transitions.dev 28: put `.t-shimmer` on the line too and
2
+ keep data-text in sync with the text, or the shimmer paints the wrong words. */
8
3
  :root {
9
4
  --think-hold: 2000ms;
10
5
  --think-swap: var(--duration-quick, 150ms);
@@ -1,7 +1,14 @@
1
1
  import { readFileSync, readdirSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { describe, expect, it } from "vite-plus/test";
4
- import { BRAND_COLOR_VARS, applyBrand, brandCss, brandStyle, brandVars, defineBrand } from "./index.js";
4
+ import {
5
+ BRAND_COLOR_VARS,
6
+ applyBrand,
7
+ brandCss,
8
+ brandStyle,
9
+ brandVars,
10
+ defineBrand,
11
+ } from "./index.js";
5
12
 
6
13
  const TOKENS = join(import.meta.dirname, "..", "tokens");
7
14
  const palette = readFileSync(join(TOKENS, "palette.css"), "utf8");
@@ -9,7 +16,10 @@ const palette = readFileSync(join(TOKENS, "palette.css"), "utf8");
9
16
  describe("brandVars", () => {
10
17
  it("sets only the roles a brand names", () => {
11
18
  const brand = defineBrand({ colors: { brand: "oklch(0.5 0.2 272)", signal: "#f0503d" } });
12
- expect(brandVars(brand)).toEqual({ "--brand": "oklch(0.5 0.2 272)", "--brand-signal": "#f0503d" });
19
+ expect(brandVars(brand)).toEqual({
20
+ "--brand": "oklch(0.5 0.2 272)",
21
+ "--brand-signal": "#f0503d",
22
+ });
13
23
  });
14
24
 
15
25
  it("maps fonts and extra properties", () => {
@@ -27,7 +37,9 @@ describe("brandVars", () => {
27
37
  });
28
38
 
29
39
  it("refuses a value that would break out of its declaration", () => {
30
- expect(() => brandVars({ colors: { brand: "red; } body { display: none" } })).toThrow(/--brand/);
40
+ expect(() => brandVars({ colors: { brand: "red; } body { display: none" } })).toThrow(
41
+ /--brand/,
42
+ );
31
43
  });
32
44
  });
33
45
 
@@ -36,7 +48,9 @@ describe("the three ways to apply one", () => {
36
48
 
37
49
  it("writes a style attribute and a rule", () => {
38
50
  expect(brandStyle(brand)).toBe("--brand:blue;--brand-ink:black");
39
- expect(brandCss(brand, ".client")).toBe(".client {\n --brand: blue;\n --brand-ink: black;\n}\n");
51
+ expect(brandCss(brand, ".client")).toBe(
52
+ ".client {\n --brand: blue;\n --brand-ink: black;\n}\n",
53
+ );
40
54
  });
41
55
 
42
56
  it("applies to an element and puts back what it found", () => {
@@ -61,7 +75,9 @@ describe("the tokens", () => {
61
75
  it("carry no client palette", () => {
62
76
  for (const file of readdirSync(TOKENS).filter((f) => f.endsWith(".css"))) {
63
77
  const css = readFileSync(join(TOKENS, file), "utf8");
64
- expect(css, file).not.toMatch(/brand-(indigo|sand|terra|night|veil|dusk|cream|linen|mist)|--(indigo|sand|terra)-\d/);
78
+ expect(css, file).not.toMatch(
79
+ /brand-(indigo|sand|terra|night|veil|dusk|cream|linen|mist)|--(indigo|sand|terra)-\d/,
80
+ );
65
81
  }
66
82
  });
67
83
  });
@@ -1,13 +1,6 @@
1
1
  /**
2
- * A client's brand is a typed object in its own `brand.ts`, with no build step:
3
- *
4
- * export const brand = defineBrand({ colors: { brand: "oklch(0.5 0.2 272)" } });
5
- *
6
- * `@heyramzi/ui/tokens.css` ships a neutral look. A brand replaces only the roles it names:
7
- * - on the server, put `brandStyle(brand)` on the `<html style>` so the first paint is right;
8
- * - or call `applyBrand(brand)` once in the browser;
9
- * - or write `brandCss(brand)` into a stylesheet.
10
- * Light and dark come from the tokens, which read these roles, so a brand never types both.
2
+ * A brand replaces only the token roles it names. Put `brandStyle(brand)` on the server's
3
+ * `<html style>`, or the first paint shows the neutral look.
11
4
  */
12
5
 
13
6
  /** Any CSS colour: oklch(), hex, a named colour or a var(). */
@@ -86,7 +79,8 @@ export function defineBrand<const B extends Brand>(brand: B): B {
86
79
  }
87
80
 
88
81
  function checked(name: string, value: string): string {
89
- if (/[;{}<>]/.test(value)) throw new Error(`brand: ${name} holds a character CSS would read as more than a value`);
82
+ if (/[;{}<>]/.test(value))
83
+ throw new Error(`brand: ${name} holds a character CSS would read as more than a value`);
90
84
  return value;
91
85
  }
92
86
 
@@ -9,7 +9,7 @@ MIT. It runs on any domain and costs nothing per install.
9
9
  ## What's in it
10
10
 
11
11
  - **Pen layer.** Freehand strokes (perfect-freehand), a highlighter, an eraser, plain text, and rectangle, ellipse and diamond shapes. Colours are token names, so a board drawn in light mode reads fine in dark.
12
- - **Tool bar.** A floating bar with the tools, undo and redo. Pick from 6 colours and 3 brush sizes. Keys: V, H, P, M, E, T, R, O, D.
12
+ - **Tool bar.** A floating bar with the tools, a colour and a brush size. Pick from 8 colours and 3 brush sizes. Keys: V, H, L, P, M, E, T, R, O, D, F. Undo and redo are yours to wire to `createCanvasDispatcher`.
13
13
  - **Plugin API.** Register your own card types, ops and layout passes. One registry builds xyflow's `nodeTypes` for you.
14
14
  - **Ops.** 8 core ops (`addNode`, `removeNode`, `patchNode`, `moveNode`, `setParent`, `addEdge`, `removeEdge`, `patchEdge`) as plain functions over nodes and edges. Zod checks every one.
15
15
  - **Undo history.** `createCanvasDispatcher` wraps each edit in a transaction. A whole pen stroke is one undo step.
@@ -68,14 +68,15 @@ Drop this in `src/routes/+page.svelte`.
68
68
  toolbar: "Canvas tools",
69
69
  style: "Style",
70
70
  tools: {
71
- select: "Select", hand: "Hand", pen: "Pen", highlighter: "Highlighter",
72
- eraser: "Eraser", text: "Text", rectangle: "Rectangle", ellipse: "Ellipse",
73
- diamond: "Diamond", shapes: "Shapes",
71
+ select: "Select", hand: "Hand", connect: "Connect", frame: "Frame", pen: "Pen",
72
+ highlighter: "Highlighter", eraser: "Eraser", text: "Text", rectangle: "Rectangle",
73
+ ellipse: "Ellipse", diamond: "Diamond", shapes: "Shapes", note: "Note",
74
+ },
75
+ colors: {
76
+ ink: "Ink", red: "Red", blue: "Blue", green: "Green", amber: "Amber",
77
+ violet: "Violet", indigo: "Indigo", terra: "Terracotta",
74
78
  },
75
- colors: { ink: "Ink", red: "Red", blue: "Blue", green: "Green", amber: "Amber", violet: "Violet" },
76
79
  sizes: ["Small", "Medium", "Large"],
77
- undo: "Undo",
78
- redo: "Redo",
79
80
  } as const;
80
81
  </script>
81
82
 
@@ -100,10 +101,6 @@ Drop this in `src/routes/+page.svelte`.
100
101
  bind:color
101
102
  bind:level
102
103
  {labels}
103
- canUndo={history.canUndo}
104
- canRedo={history.canRedo}
105
- onUndo={history.undo}
106
- onRedo={history.redo}
107
104
  />
108
105
  </SvelteFlow>
109
106
  </div>
@@ -192,7 +189,7 @@ The pen is `PenOverlay` (or `PenLayer` inside xyflow), and it needs 2 callbacks
192
189
 
193
190
  The eraser removes whole strokes, never part of one. A swipe across a big shape doesn't wipe it.
194
191
 
195
- The pen has 6 colours: `ink`, `red`, `blue`, `green`, `amber` and `violet`. `pen.css` maps them to CSS variables with a light and a dark value. Dark mode applies under a `.dark` class on an ancestor or on `.svelte-flow`.
192
+ The pen has 8 colours: `ink`, `red`, `blue`, `green`, `amber`, `violet`, `indigo` and `terra`. `pen.css` maps them to CSS variables with a light and a dark value. Dark mode applies under a `.dark` class on an ancestor or on `.svelte-flow`.
196
193
 
197
194
  ## Headless ops
198
195
 
@@ -1,47 +1,47 @@
1
1
  <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import type { Snippet } from "svelte";
6
- import { getDiagramEditorContext } from "../editor/editor-context.js";
7
- import { isLocked } from "../editor/lock.js";
8
- import { userHeightOf, type CardResize } from "./resize.js";
9
- import ResizeHandles from "./resize-handles.svelte";
10
- import "./card.css";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import type { Snippet } from "svelte";
6
+ import { getDiagramEditorContext } from "../editor/editor-context.js";
7
+ import { isLocked } from "../editor/lock.js";
8
+ import { userHeightOf, type CardResize } from "./resize.js";
9
+ import ResizeHandles from "./resize-handles.svelte";
10
+ import "./card.css";
11
11
 
12
- // The frame every card sits in: it keeps the saved height as a floor and draws the grips
13
- // on a selected card. A card whose policy is `false` has neither.
12
+ // The frame every card sits in: it keeps the saved height as a floor and draws the grips
13
+ // on a selected card. A card whose policy is `false` has neither.
14
14
 
15
- // =========================
16
- // TYPES
17
- // =========================
18
- interface Props {
19
- id: string;
20
- data?: unknown;
21
- selected?: boolean;
22
- /** How this card type resizes, or false when its size isn't the person's to set. */
23
- resize: CardResize | false;
24
- children: Snippet;
25
- }
15
+ // =========================
16
+ // TYPES
17
+ // =========================
18
+ interface Props {
19
+ id: string;
20
+ data?: unknown;
21
+ selected?: boolean;
22
+ /** How this card type resizes, or false when its size isn't the person's to set. */
23
+ resize: CardResize | false;
24
+ children: Snippet;
25
+ }
26
26
 
27
- // =========================
28
- // PROPS
29
- // =========================
30
- let { id, data, selected = false, resize, children }: Props = $props();
27
+ // =========================
28
+ // PROPS
29
+ // =========================
30
+ let { id, data, selected = false, resize, children }: Props = $props();
31
31
 
32
- // =========================
33
- // STATE
34
- // =========================
35
- const editor = getDiagramEditorContext();
36
- let frame = $state<HTMLElement>();
32
+ // =========================
33
+ // STATE
34
+ // =========================
35
+ const editor = getDiagramEditorContext();
36
+ let frame = $state<HTMLElement>();
37
37
 
38
- // =========================
39
- // DERIVED
40
- // =========================
41
- let floor = $derived(userHeightOf(data));
42
- // A box card is sized by the node itself, so it needs no frame.
43
- let framed = $derived(!!resize && resize.store !== "box");
44
- let grips = $derived(!!resize && !!editor && selected && !isLocked({ data }));
38
+ // =========================
39
+ // DERIVED
40
+ // =========================
41
+ let floor = $derived(userHeightOf(data));
42
+ // A box card is sized by the node itself, so it needs no frame.
43
+ let framed = $derived(!!resize && resize.store !== "box");
44
+ let grips = $derived(!!resize && !!editor && selected && !isLocked({ data }));
45
45
  </script>
46
46
 
47
47
  <!-- =========================== -->
@@ -53,7 +53,11 @@ let grips = $derived(!!resize && !!editor && selected && !isLocked({ data }));
53
53
  {/if}
54
54
 
55
55
  {#if framed}
56
- <div class="cv-card-frame" bind:this={frame} style:--cv-card-h={floor > 0 ? `${floor}px` : undefined}>
56
+ <div
57
+ class="cv-card-frame"
58
+ bind:this={frame}
59
+ style:--cv-card-h={floor > 0 ? `${floor}px` : undefined}
60
+ >
57
61
  {@render children()}
58
62
  </div>
59
63
  {:else}
@@ -1,98 +1,103 @@
1
1
  <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import { NodeResizeControl, ResizeControlVariant, useSvelteFlow } from "@xyflow/svelte";
6
- import { getDiagramEditorContext } from "../editor/editor-context.js";
7
- import {
8
- DEFAULT_MAX_WIDTH,
9
- DEFAULT_MIN_HEIGHT,
10
- DEFAULT_MIN_WIDTH,
11
- keptHeight,
12
- type CardResize,
13
- } from "./resize.js";
14
- import "./card.css";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import { NodeResizeControl, ResizeControlVariant, useSvelteFlow } from "@xyflow/svelte";
6
+ import { getDiagramEditorContext } from "../editor/editor-context.js";
7
+ import {
8
+ DEFAULT_MAX_WIDTH,
9
+ DEFAULT_MIN_HEIGHT,
10
+ DEFAULT_MIN_WIDTH,
11
+ keptHeight,
12
+ type CardResize,
13
+ } from "./resize.js";
14
+ import "./card.css";
15
15
 
16
- // The grips of a selected card. One drag is one undo step: width, kept height and extra data close together.
16
+ // The grips of a selected card. One drag is one undo step: width, kept height and extra data close together.
17
17
 
18
- // =========================
19
- // TYPES
20
- // =========================
21
- interface Props {
22
- id: string;
23
- resize: CardResize;
24
- /** The framed element, read for the height its content needs. */
25
- frame?: HTMLElement;
26
- }
18
+ // =========================
19
+ // TYPES
20
+ // =========================
21
+ interface Props {
22
+ id: string;
23
+ resize: CardResize;
24
+ /** The framed element, read for the height its content needs. */
25
+ frame?: HTMLElement;
26
+ }
27
27
 
28
- // =========================
29
- // CONSTANTS
30
- // =========================
31
- const CORNERS = ["top-left", "top-right", "bottom-left", "bottom-right"] as const;
32
- const EDGES = ["left", "right", "top", "bottom"] as const;
33
- const SIDES = ["left", "right"] as const;
28
+ // =========================
29
+ // CONSTANTS
30
+ // =========================
31
+ const CORNERS = ["top-left", "top-right", "bottom-left", "bottom-right"] as const;
32
+ const EDGES = ["left", "right", "top", "bottom"] as const;
33
+ const SIDES = ["left", "right"] as const;
34
34
 
35
- // =========================
36
- // PROPS
37
- // =========================
38
- let { id, resize, frame }: Props = $props();
35
+ // =========================
36
+ // PROPS
37
+ // =========================
38
+ let { id, resize, frame }: Props = $props();
39
39
 
40
- // =========================
41
- // STATE
42
- // =========================
43
- const editor = getDiagramEditorContext();
44
- const { updateNode } = useSvelteFlow();
45
- let endGesture: (() => void) | null = null;
46
- // The height the content needs, read when a drag starts.
47
- let contentHeight = $state(DEFAULT_MIN_HEIGHT);
40
+ // =========================
41
+ // STATE
42
+ // =========================
43
+ const editor = getDiagramEditorContext();
44
+ const { updateNode } = useSvelteFlow();
45
+ let endGesture: (() => void) | null = null;
46
+ // The height the content needs, read when a drag starts.
47
+ let contentHeight = $state(DEFAULT_MIN_HEIGHT);
48
48
 
49
- // =========================
50
- // DERIVED
51
- // =========================
52
- let both = $derived(resize.axes === "both");
53
- let ratio = $derived(resize.axes === "ratio");
54
- let direction = $derived(both || ratio ? undefined : ("horizontal" as const));
55
- let box = $derived(resize.store === "box");
56
- let minWidth = $derived(resize.minWidth ?? DEFAULT_MIN_WIDTH);
57
- let maxWidth = $derived(resize.maxWidth ?? DEFAULT_MAX_WIDTH);
58
- let minHeight = $derived(both && !box ? contentHeight : (resize.minHeight ?? DEFAULT_MIN_HEIGHT));
49
+ // =========================
50
+ // DERIVED
51
+ // =========================
52
+ let both = $derived(resize.axes === "both");
53
+ let ratio = $derived(resize.axes === "ratio");
54
+ let direction = $derived(both || ratio ? undefined : ("horizontal" as const));
55
+ let box = $derived(resize.store === "box");
56
+ let minWidth = $derived(resize.minWidth ?? DEFAULT_MIN_WIDTH);
57
+ let maxWidth = $derived(resize.maxWidth ?? DEFAULT_MAX_WIDTH);
58
+ let minHeight = $derived(both && !box ? contentHeight : (resize.minHeight ?? DEFAULT_MIN_HEIGHT));
59
59
 
60
- // =========================
61
- // FUNCTIONS
62
- // =========================
63
- function measureContent() {
64
- if (!frame) return;
65
- // With the saved floor dropped, the frame is as tall as the content needs.
66
- frame.setAttribute("data-resizing", "");
67
- contentHeight = Math.max(DEFAULT_MIN_HEIGHT, Math.ceil(frame.offsetHeight));
68
- }
60
+ // =========================
61
+ // FUNCTIONS
62
+ // =========================
63
+ function measureContent() {
64
+ if (!frame) return;
65
+ // With the saved floor dropped, the frame is as tall as the content needs.
66
+ frame.setAttribute("data-resizing", "");
67
+ contentHeight = Math.max(DEFAULT_MIN_HEIGHT, Math.ceil(frame.offsetHeight));
68
+ }
69
69
 
70
- function start() {
71
- endGesture = editor?.startGesture() ?? null;
72
- if (both && !box) measureContent();
73
- }
70
+ function start() {
71
+ endGesture = editor?.startGesture() ?? null;
72
+ if (both && !box) measureContent();
73
+ }
74
74
 
75
- function finish(size: { width: number; height: number }) {
76
- frame?.removeAttribute("data-resizing");
77
- const width = Math.round(size.width);
78
- if (!box) {
79
- editor?.setNodeWidth(id, width);
80
- const patch: Record<string, unknown> = { ...resize.extraData?.({ width, height: size.height }) };
81
- // A text card keeps growing with its words, and an image's height follows its width.
82
- if (both) patch.userHeight = keptHeight(size.height, contentHeight);
83
- if (Object.keys(patch).length > 0) editor?.patchNodeData(id, patch);
84
- // The drag gave the node a height; the content and the saved floor size it from here.
85
- updateNode(id, { height: undefined });
75
+ function finish(size: { width: number; height: number }) {
76
+ frame?.removeAttribute("data-resizing");
77
+ const width = Math.round(size.width);
78
+ if (!box) {
79
+ editor?.setNodeWidth(id, width);
80
+ const patch: Record<string, unknown> = {
81
+ ...resize.extraData?.({ width, height: size.height }),
82
+ };
83
+ // A text card keeps growing with its words, and an image's height follows its width.
84
+ if (both) patch.userHeight = keptHeight(size.height, contentHeight);
85
+ if (Object.keys(patch).length > 0) editor?.patchNodeData(id, patch);
86
+ // The drag gave the node a height; the content and the saved floor size it from here.
87
+ updateNode(id, { height: undefined });
88
+ }
89
+ endGesture?.();
90
+ endGesture = null;
86
91
  }
87
- endGesture?.();
88
- endGesture = null;
89
- }
90
92
  </script>
91
93
 
92
94
  <!-- =========================== -->
93
95
  <!-- MARKUP -->
94
96
  <!-- =========================== -->
95
- {#snippet control(position: (typeof CORNERS)[number] | (typeof EDGES)[number], variant: ResizeControlVariant)}
97
+ {#snippet control(
98
+ position: (typeof CORNERS)[number] | (typeof EDGES)[number],
99
+ variant: ResizeControlVariant,
100
+ )}
96
101
  <NodeResizeControl
97
102
  nodeId={id}
98
103
  {position}
@@ -102,7 +107,11 @@ function finish(size: { width: number; height: number }) {
102
107
  {minHeight}
103
108
  keepAspectRatio={ratio}
104
109
  resizeDirection={direction}
105
- class={variant === ResizeControlVariant.Line ? "cv-edge" : direction ? "cv-grip cv-grip-x" : "cv-grip"}
110
+ class={variant === ResizeControlVariant.Line
111
+ ? "cv-edge"
112
+ : direction
113
+ ? "cv-grip cv-grip-x"
114
+ : "cv-grip"}
106
115
  onResizeStart={start}
107
116
  onResizeEnd={(_, size) => finish(size)}
108
117
  />