@heyramzi/ui 2.1.0 → 2.3.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 (317) hide show
  1. package/COMPONENTS.md +1 -1
  2. package/package.json +7 -8
  3. package/scripts/sync-shadcn.mjs +3 -4
  4. package/src/agents/agent-avatar.svelte +604 -575
  5. package/src/agents/agent-cast.svelte +179 -183
  6. package/src/agents/demo.svelte +125 -113
  7. package/src/agents/helpers.ts +23 -17
  8. package/src/agents/index.ts +4 -4
  9. package/src/agents/page-state.ts +6 -6
  10. package/src/agents/types.ts +6 -6
  11. package/src/ai/ai-button/AiButton.svelte +4 -12
  12. package/src/ai/generation-progress/GenerationProgress.svelte +10 -3
  13. package/src/ai/prompt-input/PromptInputTextarea.svelte +1 -2
  14. package/src/ai/response/Response.css +2 -3
  15. package/src/animations/avatar-group-hover.css +2 -16
  16. package/src/animations/card-tilt.css +3 -6
  17. package/src/animations/digit-pop-in.css +2 -15
  18. package/src/animations/dropdown.css +4 -17
  19. package/src/animations/input-clear-dissolve.css +5 -18
  20. package/src/animations/input-shake.css +9 -41
  21. package/src/animations/learn-more-hover.css +17 -9
  22. package/src/animations/like-button.css +2 -4
  23. package/src/animations/page-side-by-side.css +2 -10
  24. package/src/animations/plus-menu-morph.css +2 -5
  25. package/src/animations/shimmer-text.css +5 -18
  26. package/src/animations/spinning-counter.css +2 -6
  27. package/src/animations/success-check.css +2 -15
  28. package/src/animations/thinking-states.css +2 -7
  29. package/src/brand/brand.test.ts +21 -5
  30. package/src/brand/index.ts +4 -10
  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/canvas-cleanup.test.ts +1 -1
  49. package/src/canvas/headless/svg-figure.test.ts +5 -1
  50. package/src/canvas/lod/lod-switch.svelte +111 -106
  51. package/src/canvas/ops/ops.ts +1 -1
  52. package/src/canvas/pen/PenField.svelte +87 -81
  53. package/src/canvas/pen/PenOverlay.svelte +127 -127
  54. package/src/canvas/pen/ShapeNode.svelte +31 -32
  55. package/src/canvas/pen/TextNode.svelte +54 -53
  56. package/src/canvas/pen/host-field.test.svelte +7 -7
  57. package/src/canvas/pen/shapes.ts +7 -1
  58. package/src/canvas/screen/GlyphIcon.svelte +22 -17
  59. package/src/canvas/screen/PartView.svelte +138 -72
  60. package/src/canvas/screen/ScreenBody.svelte +81 -60
  61. package/src/canvas/screen/ScreenCard.svelte +20 -20
  62. package/src/canvas/ui/CanvasToolbar.svelte +173 -85
  63. package/src/canvas/ui/FrameLayer.svelte +59 -59
  64. package/src/canvas/ui/PenLayer.svelte +39 -30
  65. package/src/canvas/ui/StylePanel.svelte +119 -102
  66. package/src/canvas/ui/canvas-grid.svelte +137 -129
  67. package/src/charts/animated-value.svelte +1 -1
  68. package/src/charts/area-chart.css +2 -5
  69. package/src/charts/area-chart.svelte +1 -1
  70. package/src/charts/bar-chart.css +2 -3
  71. package/src/charts/bar-chart.svelte +1 -1
  72. package/src/charts/charts.css +5 -17
  73. package/src/charts/crosshair.svelte +1 -1
  74. package/src/charts/dashboard/dashboard-empty.svelte +25 -25
  75. package/src/charts/dashboard/dashboard-section.svelte +24 -24
  76. package/src/charts/dashboard/data-panel.svelte +50 -50
  77. package/src/charts/dashboard/data-sort-head.svelte +56 -56
  78. package/src/charts/dashboard/data-table.svelte +138 -138
  79. package/src/charts/dashboard/panel-grid.svelte +30 -30
  80. package/src/charts/dashboard/period-filter.svelte +25 -25
  81. package/src/charts/dashboard/rating-chip.svelte +19 -19
  82. package/src/charts/dashboard/stat-band.svelte +32 -32
  83. package/src/charts/dashboard/stat-tile.svelte +106 -99
  84. package/src/charts/donut-chart.svelte +1 -1
  85. package/src/charts/edge-fade.svelte +1 -1
  86. package/src/charts/figure.svelte +1 -1
  87. package/src/charts/gauge-chart.css +4 -7
  88. package/src/charts/gauge-chart.svelte +1 -1
  89. package/src/charts/hover-focus.svelte +1 -1
  90. package/src/charts/legend.css +1 -3
  91. package/src/charts/legend.svelte +1 -1
  92. package/src/charts/line-chart.svelte +1 -1
  93. package/src/charts/number-chart.css +6 -9
  94. package/src/charts/number-chart.svelte +1 -1
  95. package/src/charts/reference-line.svelte +19 -19
  96. package/src/charts/reveal.svelte +1 -1
  97. package/src/charts/ring-chart.css +2 -4
  98. package/src/charts/ring-chart.svelte +1 -1
  99. package/src/charts/sparkline.svelte +5 -2
  100. package/src/charts/stat-card.svelte +1 -1
  101. package/src/charts/time-series-chart.css +2 -5
  102. package/src/charts/trend-pill.css +2 -4
  103. package/src/charts/trend-pill.svelte +1 -1
  104. package/src/components/copy/copy-button.svelte +93 -98
  105. package/src/components/copy/copy-button.test.ts +10 -2
  106. package/src/components/data-table/infinite-scroll.svelte +42 -42
  107. package/src/components/data-table/pagination.svelte +34 -22
  108. package/src/components/data-table/row-arrow.svelte +19 -16
  109. package/src/components/data-table/row-context-menu.svelte +52 -52
  110. package/src/components/data-table/sortable-head.svelte +25 -25
  111. package/src/components/data-table/table-skeleton.svelte +41 -36
  112. package/src/components/docs/doc-page.svelte +18 -9
  113. package/src/components/docs/on-this-page.svelte +16 -3
  114. package/src/components/docs/scroll-spy.svelte.ts +2 -3
  115. package/src/components/field-row/field-row.svelte +10 -8
  116. package/src/components/hold-to-confirm/hold-to-confirm.svelte +5 -1
  117. package/src/components/icon/icon.svelte +14 -14
  118. package/src/components/language-flag/language-flag.svelte +3 -1
  119. package/src/components/list-page/detail-empty.svelte +34 -34
  120. package/src/components/list-page/detail-header.svelte +55 -47
  121. package/src/components/list-page/detail-panel.svelte +51 -59
  122. package/src/components/list-page/detail-row.svelte +19 -19
  123. package/src/components/list-page/detail-stack.svelte +21 -21
  124. package/src/components/list-page/empty-state.svelte +30 -30
  125. package/src/components/list-page/filter-item.svelte +20 -20
  126. package/src/components/list-page/filter-popover.svelte +60 -55
  127. package/src/components/list-page/grouped-list.svelte +4 -2
  128. package/src/components/list-page/index.ts +5 -1
  129. package/src/components/list-page/list-page-shell.svelte +2 -5
  130. package/src/components/list-page/master-detail.svelte +87 -88
  131. package/src/components/list-page/notice-bar.svelte +38 -38
  132. package/src/components/list-page/prev-next.svelte +63 -63
  133. package/src/components/list-page/rail-body.svelte +18 -18
  134. package/src/components/list-page/rail-empty.svelte +23 -30
  135. package/src/components/list-page/rail-head.svelte +24 -24
  136. package/src/components/list-page/rail-row.svelte +77 -73
  137. package/src/components/list-page/section-host.ts +2 -3
  138. package/src/components/locale-switch/locale-switch.svelte +41 -41
  139. package/src/components/mark/mark.svelte +34 -38
  140. package/src/components/open-in/index.ts +1 -5
  141. package/src/components/open-in/open-in.svelte +40 -40
  142. package/src/components/picker/mark-picker.svelte +49 -49
  143. package/src/components/picker/picker.svelte +5 -3
  144. package/src/components/section-header/section-header.test.ts +7 -1
  145. package/src/components/section-header/section-label.svelte +20 -20
  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/index.ts +1 -1
  158. package/src/components/table-view/table-view.svelte +352 -13
  159. package/src/components/table-view/table-view.test.ts +108 -1
  160. package/src/components/table-view/types.ts +22 -11
  161. package/src/components/tags-input/index.ts +13 -0
  162. package/src/components/tags-input/tags-input.svelte +305 -0
  163. package/src/components/tags-input/tags.test.ts +64 -0
  164. package/src/components/tags-input/tags.ts +142 -0
  165. package/src/components/theme/index.ts +2 -4
  166. package/src/components/theme/theme-provider.svelte +1 -3
  167. package/src/components/toolbar/toolbar-search.svelte +25 -25
  168. package/src/components/toolbar/toolbar.svelte +11 -7
  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/icons/trades.ts +1 -1
  191. package/src/inbox/README.md +1 -3
  192. package/src/inbox/inbox.test.ts +181 -138
  193. package/src/inbox/index.ts +20 -20
  194. package/src/inbox/logic.ts +114 -111
  195. package/src/inbox/thread-inbox.svelte +227 -151
  196. package/src/inbox/types.ts +130 -108
  197. package/src/lib/theme-transition.ts +2 -4
  198. package/src/lib/tone.ts +2 -4
  199. package/src/lib/utils.ts +2 -2
  200. package/src/modules.test.ts +79 -69
  201. package/src/onboarding/define-tours.test.ts +55 -40
  202. package/src/onboarding/define-tours.ts +23 -23
  203. package/src/onboarding/demo.svelte +24 -5
  204. package/src/onboarding/index.ts +1 -8
  205. package/src/onboarding/onboarding-provider.svelte +19 -7
  206. package/src/onboarding/onboarding.svelte.ts +16 -16
  207. package/src/onboarding/pick.test.ts +61 -52
  208. package/src/onboarding/pick.ts +16 -13
  209. package/src/onboarding/stores.test.ts +80 -80
  210. package/src/onboarding/stores.ts +41 -39
  211. package/src/onboarding/tour-card.svelte +30 -5
  212. package/src/onboarding/tour-runner.svelte +12 -3
  213. package/src/onboarding/types.ts +20 -12
  214. package/src/onboarding/wait.ts +22 -22
  215. package/src/panel/side-panel.svelte +94 -93
  216. package/src/record/demo-data.ts +291 -225
  217. package/src/record/field-section.svelte +38 -34
  218. package/src/record/fields.ts +148 -96
  219. package/src/record/format.ts +63 -56
  220. package/src/record/index.ts +49 -35
  221. package/src/record/info-list.svelte +145 -114
  222. package/src/record/kinds.ts +31 -31
  223. package/src/record/logic.ts +216 -214
  224. package/src/record/next-up.svelte +45 -34
  225. package/src/record/project-list.svelte +56 -50
  226. package/src/record/record-demo.svelte +130 -110
  227. package/src/record/record-header.svelte +37 -28
  228. package/src/record/record-shell.svelte +54 -54
  229. package/src/record/record-stats.svelte +25 -25
  230. package/src/record/record-timeline.svelte +134 -114
  231. package/src/record/record.test.ts +430 -309
  232. package/src/record/steps-rail.svelte +225 -216
  233. package/src/record/types.ts +234 -215
  234. package/src/record/where-we-are.svelte +62 -58
  235. package/src/shadcn/button/button.svelte +1 -1
  236. package/src/shadcn/card/card-title.svelte +1 -5
  237. package/src/shadcn/hooks/use-highlighted-code.svelte.ts +2 -13
  238. package/src/shadcn/sheet/sheet-content.svelte +2 -4
  239. package/src/shadcn/table/table-cell.svelte +2 -3
  240. package/src/shadcn/utilities.css +3 -8
  241. package/src/slides/Controls.svelte +150 -124
  242. package/src/slides/Deck.svelte +243 -238
  243. package/src/slides/Fragment.svelte +64 -57
  244. package/src/slides/Overview.svelte +106 -101
  245. package/src/slides/Presenter.svelte +300 -266
  246. package/src/slides/Slide.svelte +185 -170
  247. package/src/slides/SlideList.svelte +16 -12
  248. package/src/slides/ambient.d.ts +1 -1
  249. package/src/slides/bridges.ts +42 -23
  250. package/src/slides/deck-controller.svelte.ts +47 -35
  251. package/src/slides/demo-slides.svelte +330 -306
  252. package/src/slides/demo.svelte +164 -160
  253. package/src/slides/export/capture.ts +20 -16
  254. package/src/slides/export/ffmpeg.ts +43 -38
  255. package/src/slides/export/index.ts +5 -5
  256. package/src/slides/export/launch.ts +3 -1
  257. package/src/slides/export/pdf.ts +30 -27
  258. package/src/slides/export/types.ts +10 -8
  259. package/src/slides/index.ts +11 -11
  260. package/src/slides/labels.ts +22 -19
  261. package/src/slides/layout.ts +9 -2
  262. package/src/slides/model.ts +4 -4
  263. package/src/slides/navigation.ts +43 -31
  264. package/src/slides/theme.css +5 -7
  265. package/src/slides/types.ts +9 -10
  266. package/src/status/define-statuses.ts +17 -24
  267. package/src/status/index.ts +5 -5
  268. package/src/status/status-badge.svelte +3 -1
  269. package/src/status/status-dot.svelte +1 -3
  270. package/src/status/status-icon.svelte +1 -1
  271. package/src/status/status-types.ts +21 -21
  272. package/src/tasks/board-order.ts +2 -4
  273. package/src/tasks/demo.svelte +189 -31
  274. package/src/tasks/detail/detail-demo.svelte +73 -13
  275. package/src/tasks/detail/detail-types.ts +45 -45
  276. package/src/tasks/detail/task-detail.svelte +40 -10
  277. package/src/tasks/detail/task-property.svelte +8 -2
  278. package/src/tasks/detail/task-thread.svelte +53 -12
  279. package/src/tasks/gantt/gantt-bar.svelte +17 -6
  280. package/src/tasks/gantt/gantt-grid.svelte +40 -9
  281. package/src/tasks/gantt/gantt-group-row.svelte +8 -2
  282. package/src/tasks/gantt/gantt-header.svelte +9 -3
  283. package/src/tasks/gantt/gantt-task-row.svelte +26 -6
  284. package/src/tasks/gantt/gantt.css +2 -1
  285. package/src/tasks/gantt/gantt.test.ts +3 -1
  286. package/src/tasks/gantt/logic.test.ts +110 -20
  287. package/src/tasks/gantt/logic.ts +42 -6
  288. package/src/tasks/inbox/inbox-card.svelte +13 -5
  289. package/src/tasks/inbox/inbox-draft.svelte +1 -1
  290. package/src/tasks/inbox/inbox-logic.ts +6 -40
  291. package/src/tasks/inbox/inbox-statuses.ts +23 -23
  292. package/src/tasks/inbox/inbox-trace.svelte +9 -3
  293. package/src/tasks/inbox/inbox-types.ts +54 -58
  294. package/src/tasks/inbox/index.ts +22 -21
  295. package/src/tasks/index.ts +49 -52
  296. package/src/tasks/kanban-board.svelte +16 -12
  297. package/src/tasks/kanban-types.ts +2 -3
  298. package/src/tasks/presets/sales.ts +6 -6
  299. package/src/tasks/task-gantt.svelte +28 -8
  300. package/src/tasks/task-list.svelte +41 -17
  301. package/src/tasks/time/duration.ts +40 -40
  302. package/src/tasks/time/time-chip.svelte +9 -2
  303. package/src/tasks/time/time-dot.svelte +1 -1
  304. package/src/tasks/time/time-ring.svelte +11 -3
  305. package/src/tasks/time/time-tracker.svelte +57 -15
  306. package/src/tasks/time/time-types.ts +57 -57
  307. package/src/tasks/use-kanban-dnd.svelte.ts +2 -3
  308. package/src/thread/index.ts +7 -7
  309. package/src/thread/logic.ts +36 -33
  310. package/src/thread/thread.svelte +82 -16
  311. package/src/thread/thread.test.ts +58 -47
  312. package/src/thread/types.ts +68 -68
  313. package/src/tokens/craft.css +17 -53
  314. package/src/tokens/index.css +3 -13
  315. package/src/tokens/palette.css +13 -11
  316. package/src/tokens/primitives.css +10 -23
  317. package/src/tasks/inbox/inbox.svelte +0 -203
@@ -40,12 +40,28 @@
40
40
  </script>
41
41
 
42
42
  {#if image}
43
- <img src={image} alt="" class="aspect-video w-full rounded-t-[inherit] object-cover outline -outline-offset-1 outline-black/10 dark:outline-white/10" />
43
+ <img
44
+ src={image}
45
+ alt=""
46
+ class="aspect-video w-full rounded-t-[inherit] object-cover outline -outline-offset-1 outline-black/10 dark:outline-white/10"
47
+ />
44
48
  {/if}
45
49
  <div class="flex flex-col gap-1.5 p-4 pb-3">
46
50
  <div class="flex items-start justify-between gap-3">
47
- <h2 {...titleAttrs} class={["font-semibold leading-snug text-balance", intro ? "text-base" : "text-sm"]}>{title}</h2>
48
- <Button onclick={onclose} variant="ghost" size="sm" icon class="-mt-1 -mr-1.5 text-muted-foreground" aria-label={strings.skip}>
51
+ <h2
52
+ {...titleAttrs}
53
+ class={["font-semibold leading-snug text-balance", intro ? "text-base" : "text-sm"]}
54
+ >
55
+ {title}
56
+ </h2>
57
+ <Button
58
+ onclick={onclose}
59
+ variant="ghost"
60
+ size="sm"
61
+ icon
62
+ class="-mt-1 -mr-1.5 text-muted-foreground"
63
+ aria-label={strings.skip}
64
+ >
49
65
  <X weight="bold" />
50
66
  </Button>
51
67
  </div>
@@ -53,9 +69,18 @@
53
69
  </div>
54
70
  <div class="flex items-center justify-between gap-3 px-4 pb-4">
55
71
  {#if total > 1}
56
- <div role="img" aria-label={strings.progress(index + 1, total)} class="flex items-center gap-1.5">
72
+ <div
73
+ role="img"
74
+ aria-label={strings.progress(index + 1, total)}
75
+ class="flex items-center gap-1.5"
76
+ >
57
77
  {#each { length: total } as _, i (i)}
58
- <span class={["h-1.5 rounded-full transition-[width,background-color] duration-(--duration-fast) ease-(--ease-smooth-out) motion-reduce:transition-none", i === index ? "bg-foreground w-4" : "bg-border w-1.5"]}></span>
78
+ <span
79
+ class={[
80
+ "h-1.5 rounded-full transition-[width,background-color] duration-(--duration-fast) ease-(--ease-smooth-out) motion-reduce:transition-none",
81
+ i === index ? "bg-foreground w-4" : "bg-border w-1.5",
82
+ ]}
83
+ ></span>
59
84
  {/each}
60
85
  </div>
61
86
  {:else}
@@ -47,7 +47,8 @@
47
47
  // dismissal waits a microtask and the completion wins.
48
48
  function onStatusChange({ status }: zag.StatusChangeDetails) {
49
49
  if (status === "completed") finish("completed");
50
- else if (status === "dismissed" || status === "skipped") queueMicrotask(() => finish("dismissed"));
50
+ else if (status === "dismissed" || status === "skipped")
51
+ queueMicrotask(() => finish("dismissed"));
51
52
  else if (status === "not-found") finish(null);
52
53
  }
53
54
 
@@ -107,13 +108,21 @@
107
108
  </script>
108
109
 
109
110
  {#if step}
110
- <div use:portal {...api.getBackdropProps()} class="tour-backdrop" data-instant={instant || undefined}></div>
111
+ <div
112
+ use:portal
113
+ {...api.getBackdropProps()}
114
+ class="tour-backdrop"
115
+ data-instant={instant || undefined}
116
+ ></div>
111
117
  <div use:portal {...api.getSpotlightProps()} class="tour-spotlight"></div>
112
118
  <div use:portal {...api.getPositionerProps()} class="tour-positioner">
113
119
  <div
114
120
  {...api.getContentProps()}
115
121
  bind:this={card}
116
- class={["tour-card bg-popover text-popover-foreground ring-foreground/10 shadow-overlay pointer-events-auto max-w-full text-sm ring-1 outline-none", intro ? "w-96 rounded-2xl" : "w-80 rounded-xl"]}
122
+ class={[
123
+ "tour-card bg-popover text-popover-foreground ring-foreground/10 shadow-overlay pointer-events-auto max-w-full text-sm ring-1 outline-none",
124
+ intro ? "w-96 rounded-2xl" : "w-80 rounded-xl",
125
+ ]}
117
126
  >
118
127
  <TourCard
119
128
  title={String(step.title)}
@@ -9,26 +9,34 @@ export type Step = { target: string; title: string; body: string; placement?: Pl
9
9
  * `version` starts at 1. Raise it and everyone who saw the old one sees the tour again.
10
10
  */
11
11
  export type Tour =
12
- | { id: string; version?: number; route: string; kind: "steps"; steps: Step[] }
13
- | { id: string; version?: number; route: string; kind: "intro"; title: string; body: string; image?: string };
12
+ | { id: string; version?: number; route: string; kind: "steps"; steps: Step[] }
13
+ | {
14
+ id: string;
15
+ version?: number;
16
+ route: string;
17
+ kind: "intro";
18
+ title: string;
19
+ body: string;
20
+ image?: string;
21
+ };
14
22
 
15
23
  export type Outcome = "completed" | "dismissed";
16
24
 
17
25
  /** Where seen-state lives. `load` runs once per session. */
18
26
  export interface SeenStore {
19
- /** Tour id to the version seen. */
20
- load(): Promise<Record<string, number>>;
21
- markSeen(tourId: string, version: number, outcome: Outcome): Promise<void>;
22
- /** One tour, or all of them when no id is given. */
23
- reset(tourId?: string): Promise<void>;
27
+ /** Tour id to the version seen. */
28
+ load(): Promise<Record<string, number>>;
29
+ markSeen(tourId: string, version: number, outcome: Outcome): Promise<void>;
30
+ /** One tour, or all of them when no id is given. */
31
+ reset(tourId?: string): Promise<void>;
24
32
  }
25
33
 
26
34
  export type Strings = {
27
- next: string;
28
- back: string;
29
- done: string;
30
- skip: string;
31
- progress: (n: number, total: number) => string;
35
+ next: string;
36
+ back: string;
37
+ done: string;
38
+ skip: string;
39
+ progress: (n: number, total: number) => string;
32
40
  };
33
41
 
34
42
  export const MAX_STEPS = 6;
@@ -2,28 +2,28 @@ const find = (selector: string) => document.querySelector(selector);
2
2
 
3
3
  /** Resolves with the element once the selector matches, or null after `timeoutMs` or an abort. */
4
4
  export function waitForElement(
5
- selector: string,
6
- timeoutMs: number,
7
- signal?: AbortSignal,
5
+ selector: string,
6
+ timeoutMs: number,
7
+ signal?: AbortSignal,
8
8
  ): Promise<Element | null> {
9
- return new Promise((resolve) => {
10
- const now = find(selector);
11
- if (now) return resolve(now);
12
- if (signal?.aborted) return resolve(null);
9
+ return new Promise((resolve) => {
10
+ const now = find(selector);
11
+ if (now) return resolve(now);
12
+ if (signal?.aborted) return resolve(null);
13
13
 
14
- const finish = (el: Element | null) => {
15
- observer.disconnect();
16
- clearTimeout(timer);
17
- signal?.removeEventListener("abort", onAbort);
18
- resolve(el);
19
- };
20
- const onAbort = () => finish(null);
21
- const observer = new MutationObserver(() => {
22
- const el = find(selector);
23
- if (el) finish(el);
24
- });
25
- const timer = setTimeout(() => finish(find(selector)), timeoutMs);
26
- observer.observe(document.documentElement, { childList: true, subtree: true });
27
- signal?.addEventListener("abort", onAbort);
28
- });
14
+ const finish = (el: Element | null) => {
15
+ observer.disconnect();
16
+ clearTimeout(timer);
17
+ signal?.removeEventListener("abort", onAbort);
18
+ resolve(el);
19
+ };
20
+ const onAbort = () => finish(null);
21
+ const observer = new MutationObserver(() => {
22
+ const el = find(selector);
23
+ if (el) finish(el);
24
+ });
25
+ const timer = setTimeout(() => finish(find(selector)), timeoutMs);
26
+ observer.observe(document.documentElement, { childList: true, subtree: true });
27
+ signal?.addEventListener("abort", onAbort);
28
+ });
29
29
  }
@@ -1,104 +1,103 @@
1
1
  <script lang="ts" module>
2
- import type { PanelStat, SidePanelWidth } from "./side-panel-types.js";
3
- export type { PanelStat, SidePanelWidth };
2
+ import type { PanelStat, SidePanelWidth } from "./side-panel-types.js";
3
+ export type { PanelStat, SidePanelWidth };
4
4
  </script>
5
5
 
6
6
  <script lang="ts">
7
- // =========================
8
- // IMPORTS
9
- // =========================
10
- import type { Snippet } from "svelte";
11
- import * as Sheet from "../shadcn/sheet/index.js";
12
- import { OpenIn, type OpenInTool } from "../components/open-in/index.js";
13
- import { CopyButton } from "../components/copy/index.js";
14
- import type { Tone } from "../lib/tone.js";
15
- import { cn } from "../lib/utils.js";
16
- import StatusBadge from "../status/status-badge.svelte";
7
+ // =========================
8
+ // IMPORTS
9
+ // =========================
10
+ import type { Snippet } from "svelte";
11
+ import * as Sheet from "../shadcn/sheet/index.js";
12
+ import { OpenIn, type OpenInTool } from "../components/open-in/index.js";
13
+ import { CopyButton } from "../components/copy/index.js";
14
+ import type { Tone } from "../lib/tone.js";
15
+ import { cn } from "../lib/utils.js";
16
+ import StatusBadge from "../status/status-badge.svelte";
17
17
 
18
- // =========================
19
- // PROPS
20
- // =========================
21
- interface Props {
22
- open: boolean;
23
- /** Names the dialog for screen readers and heads the panel. */
24
- title: string;
25
- /** Merged onto the title. A short path is set `font-mono`, a subject is not. */
26
- titleClass?: string;
27
- /** The facts the record is filed under, read out under the title. */
28
- description?: string;
29
- /** Status chips, left of the action row. */
30
- badges?: { label: string; tone: Tone }[];
31
- /** Doors out to another tool. `text` replaces the tool's name on the button. */
32
- openIn?: { tool: OpenInTool; href: string; label: string; text?: string }[];
33
- /** A prompt for an AI about this record, copied on click. */
34
- handoff?: { value: () => string | Promise<string>; label: string; copiedLabel: string };
35
- /** The record's numbers, as a strip under the badges. Same place in every panel. */
36
- stats?: PanelStat[];
37
- /** Anything else the record can do, drawn last in the action row. */
38
- actions?: Snippet;
39
- /** Pinned actions. The body scrolls above it; the caller aligns the row. */
40
- footer?: Snippet;
41
- children: Snippet;
42
- width?: SidePanelWidth;
43
- /** The body brings its own header: the title is read out but not drawn, and
18
+ // =========================
19
+ // PROPS
20
+ // =========================
21
+ interface Props {
22
+ open: boolean;
23
+ /** Names the dialog for screen readers and heads the panel. */
24
+ title: string;
25
+ /** Merged onto the title. A short path is set `font-mono`, a subject is not. */
26
+ titleClass?: string;
27
+ /** The facts the record is filed under, read out under the title. */
28
+ description?: string;
29
+ /** Status chips, left of the action row. */
30
+ badges?: { label: string; tone: Tone }[];
31
+ /** Doors out to another tool. `text` replaces the tool's name on the button. */
32
+ openIn?: { tool: OpenInTool; href: string; label: string; text?: string }[];
33
+ /** A prompt for an AI about this record, copied on click. */
34
+ handoff?: { value: () => string | Promise<string>; label: string; copiedLabel: string };
35
+ /** The record's numbers, as a strip under the badges. Same place in every panel. */
36
+ stats?: PanelStat[];
37
+ /** Anything else the record can do, drawn last in the action row. */
38
+ actions?: Snippet;
39
+ /** Pinned actions. The body scrolls above it; the caller aligns the row. */
40
+ footer?: Snippet;
41
+ children: Snippet;
42
+ width?: SidePanelWidth;
43
+ /** The body brings its own header: the title is read out but not drawn, and
44
44
  the body is neither padded nor scrolled by the panel. */
45
- bare?: boolean;
46
- /** Fires once when the panel closes, from the close button, Escape or the overlay. */
47
- onClose?: () => void;
48
- };
49
- let {
50
- open = $bindable(),
51
- title,
52
- titleClass,
53
- description,
54
- badges = [],
55
- openIn = [],
56
- handoff,
57
- stats = [],
58
- actions,
59
- footer,
60
- children,
61
- width = "sm",
62
- bare = false,
63
- onClose,
64
- }: Props = $props();
45
+ bare?: boolean;
46
+ /** Fires once when the panel closes, from the close button, Escape or the overlay. */
47
+ onClose?: () => void;
48
+ }
49
+ let {
50
+ open = $bindable(),
51
+ title,
52
+ titleClass,
53
+ description,
54
+ badges = [],
55
+ openIn = [],
56
+ handoff,
57
+ stats = [],
58
+ actions,
59
+ footer,
60
+ children,
61
+ width = "sm",
62
+ bare = false,
63
+ onClose,
64
+ }: Props = $props();
65
65
 
66
- // =========================
67
- // STATE
68
- // =========================
69
- let panel = $state<HTMLElement | null>(null);
66
+ // =========================
67
+ // STATE
68
+ // =========================
69
+ let panel = $state<HTMLElement | null>(null);
70
70
 
71
- // =========================
72
- // DERIVED
73
- // =========================
74
- let hasRow = $derived(badges.length > 0 || openIn.length > 0 || Boolean(handoff || actions));
75
- const STAT_TONE = {
76
- default: "",
77
- muted: "text-muted-foreground",
78
- good: "text-success",
79
- bad: "text-destructive",
80
- } as const;
81
- const WIDTH: Record<SidePanelWidth, string> = {
82
- sm: "sm:max-w-sm",
83
- md: "sm:max-w-md",
84
- lg: "sm:max-w-xl",
85
- xl: "sm:max-w-2xl",
86
- "2xl": "sm:max-w-3xl",
87
- };
71
+ // =========================
72
+ // DERIVED
73
+ // =========================
74
+ let hasRow = $derived(badges.length > 0 || openIn.length > 0 || Boolean(handoff || actions));
75
+ const STAT_TONE = {
76
+ default: "",
77
+ muted: "text-muted-foreground",
78
+ good: "text-success",
79
+ bad: "text-destructive",
80
+ } as const;
81
+ const WIDTH: Record<SidePanelWidth, string> = {
82
+ sm: "sm:max-w-sm",
83
+ md: "sm:max-w-md",
84
+ lg: "sm:max-w-xl",
85
+ xl: "sm:max-w-2xl",
86
+ "2xl": "sm:max-w-3xl",
87
+ };
88
88
 
89
- // =========================
90
- // HANDLERS
91
- // =========================
92
- // WHY focus goes to the panel unless a text field comes first: the dialog otherwise
93
- // opens on its first control, which draws a ring on a record nobody has touched.
94
- // A form that opens on its field keeps the default, since that is where you type.
95
- const FIRST_CONTROL =
96
- 'a[href], button:not([disabled]), input:not([type="hidden"]):not([disabled]), textarea:not([disabled]), select:not([disabled])';
97
- function onOpenAutoFocus(event: Event) {
98
- if (!panel || panel.querySelector(FIRST_CONTROL)?.matches("input, textarea, select")) return;
99
- event.preventDefault();
100
- panel.focus();
101
- }
89
+ // =========================
90
+ // HANDLERS
91
+ // =========================
92
+ // Focus goes to the panel unless a text field comes first, or the first control draws
93
+ // a ring on a record nobody touched.
94
+ const FIRST_CONTROL =
95
+ 'a[href], button:not([disabled]), input:not([type="hidden"]):not([disabled]), textarea:not([disabled]), select:not([disabled])';
96
+ function onOpenAutoFocus(event: Event) {
97
+ if (!panel || panel.querySelector(FIRST_CONTROL)?.matches("input, textarea, select")) return;
98
+ event.preventDefault();
99
+ panel.focus();
100
+ }
102
101
  </script>
103
102
 
104
103
  <!-- =========================== -->
@@ -165,7 +164,9 @@ function onOpenAutoFocus(event: Event) {
165
164
  {#each stats as stat (stat.label)}
166
165
  <div class="min-w-0">
167
166
  <dt class="text-xs text-muted-foreground">{stat.label}</dt>
168
- <dd class={cn("text-sm font-medium tabular-nums", STAT_TONE[stat.tone ?? "default"])}>
167
+ <dd
168
+ class={cn("text-sm font-medium tabular-nums", STAT_TONE[stat.tone ?? "default"])}
169
+ >
169
170
  {stat.value}
170
171
  {#if stat.sub}
171
172
  <span class="text-xs font-normal text-muted-foreground">{stat.sub}</span>