@heyramzi/ui 2.1.0 → 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 (312) hide show
  1. package/package.json +7 -8
  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/card/card-frame.svelte +43 -39
  31. package/src/canvas/card/resize-handles.svelte +89 -80
  32. package/src/canvas/chrome/chrome-zoom.svelte +101 -100
  33. package/src/canvas/chrome/lock-badge.svelte +8 -8
  34. package/src/canvas/chrome/side-handles.svelte +24 -19
  35. package/src/canvas/chrome/size-chip.svelte +30 -30
  36. package/src/canvas/chrome/snap-guides.svelte +20 -20
  37. package/src/canvas/components/icons/ToolIcon.svelte +8 -8
  38. package/src/canvas/components/shared/svg/PreviewSvg.svelte +19 -19
  39. package/src/canvas/components/shared/svg/ShapeOutline.svelte +71 -48
  40. package/src/canvas/components/shared/svg/StrokeNode.svelte +21 -27
  41. package/src/canvas/components/shared/svg/TextRule.svelte +25 -17
  42. package/src/canvas/edges/bend-handle.svelte +46 -46
  43. package/src/canvas/edges/edge-gradient.svelte +10 -10
  44. package/src/canvas/edges/gradient-edge.svelte +45 -29
  45. package/src/canvas/editor/lock.test.ts +6 -1
  46. package/src/canvas/editor/style.ts +7 -1
  47. package/src/canvas/headless/svg-figure.test.ts +5 -1
  48. package/src/canvas/lod/lod-switch.svelte +111 -106
  49. package/src/canvas/pen/PenField.svelte +87 -81
  50. package/src/canvas/pen/PenOverlay.svelte +127 -127
  51. package/src/canvas/pen/ShapeNode.svelte +31 -32
  52. package/src/canvas/pen/TextNode.svelte +54 -53
  53. package/src/canvas/pen/host-field.test.svelte +7 -7
  54. package/src/canvas/pen/shapes.ts +7 -1
  55. package/src/canvas/screen/GlyphIcon.svelte +22 -17
  56. package/src/canvas/screen/PartView.svelte +138 -72
  57. package/src/canvas/screen/ScreenBody.svelte +81 -60
  58. package/src/canvas/screen/ScreenCard.svelte +20 -20
  59. package/src/canvas/ui/CanvasToolbar.svelte +173 -85
  60. package/src/canvas/ui/FrameLayer.svelte +59 -59
  61. package/src/canvas/ui/PenLayer.svelte +39 -30
  62. package/src/canvas/ui/StylePanel.svelte +119 -102
  63. package/src/canvas/ui/canvas-grid.svelte +137 -129
  64. package/src/charts/animated-value.svelte +1 -1
  65. package/src/charts/area-chart.css +2 -5
  66. package/src/charts/area-chart.svelte +1 -1
  67. package/src/charts/bar-chart.css +2 -3
  68. package/src/charts/bar-chart.svelte +1 -1
  69. package/src/charts/charts.css +5 -17
  70. package/src/charts/crosshair.svelte +1 -1
  71. package/src/charts/dashboard/dashboard-empty.svelte +25 -25
  72. package/src/charts/dashboard/dashboard-section.svelte +24 -24
  73. package/src/charts/dashboard/data-panel.svelte +50 -50
  74. package/src/charts/dashboard/data-sort-head.svelte +56 -56
  75. package/src/charts/dashboard/data-table.svelte +138 -138
  76. package/src/charts/dashboard/panel-grid.svelte +30 -30
  77. package/src/charts/dashboard/period-filter.svelte +25 -25
  78. package/src/charts/dashboard/rating-chip.svelte +19 -19
  79. package/src/charts/dashboard/stat-band.svelte +32 -32
  80. package/src/charts/dashboard/stat-tile.svelte +106 -99
  81. package/src/charts/donut-chart.svelte +1 -1
  82. package/src/charts/edge-fade.svelte +1 -1
  83. package/src/charts/figure.svelte +1 -1
  84. package/src/charts/gauge-chart.css +4 -7
  85. package/src/charts/gauge-chart.svelte +1 -1
  86. package/src/charts/hover-focus.svelte +1 -1
  87. package/src/charts/legend.css +1 -3
  88. package/src/charts/legend.svelte +1 -1
  89. package/src/charts/line-chart.svelte +1 -1
  90. package/src/charts/number-chart.css +6 -9
  91. package/src/charts/number-chart.svelte +1 -1
  92. package/src/charts/reference-line.svelte +19 -19
  93. package/src/charts/reveal.svelte +1 -1
  94. package/src/charts/ring-chart.css +2 -4
  95. package/src/charts/ring-chart.svelte +1 -1
  96. package/src/charts/sparkline.svelte +5 -2
  97. package/src/charts/stat-card.svelte +1 -1
  98. package/src/charts/time-series-chart.css +2 -5
  99. package/src/charts/trend-pill.css +2 -4
  100. package/src/charts/trend-pill.svelte +1 -1
  101. package/src/components/copy/copy-button.svelte +93 -98
  102. package/src/components/copy/copy-button.test.ts +10 -2
  103. package/src/components/data-table/infinite-scroll.svelte +42 -42
  104. package/src/components/data-table/pagination.svelte +34 -22
  105. package/src/components/data-table/row-arrow.svelte +19 -16
  106. package/src/components/data-table/row-context-menu.svelte +52 -52
  107. package/src/components/data-table/sortable-head.svelte +25 -25
  108. package/src/components/data-table/table-skeleton.svelte +41 -36
  109. package/src/components/docs/doc-page.svelte +18 -9
  110. package/src/components/docs/on-this-page.svelte +16 -3
  111. package/src/components/docs/scroll-spy.svelte.ts +2 -3
  112. package/src/components/field-row/field-row.svelte +10 -8
  113. package/src/components/hold-to-confirm/hold-to-confirm.svelte +5 -1
  114. package/src/components/icon/icon.svelte +14 -14
  115. package/src/components/language-flag/language-flag.svelte +3 -1
  116. package/src/components/list-page/detail-empty.svelte +34 -34
  117. package/src/components/list-page/detail-header.svelte +55 -47
  118. package/src/components/list-page/detail-panel.svelte +51 -59
  119. package/src/components/list-page/detail-row.svelte +19 -19
  120. package/src/components/list-page/detail-stack.svelte +21 -21
  121. package/src/components/list-page/empty-state.svelte +30 -30
  122. package/src/components/list-page/filter-item.svelte +20 -20
  123. package/src/components/list-page/filter-popover.svelte +60 -55
  124. package/src/components/list-page/grouped-list.svelte +4 -2
  125. package/src/components/list-page/index.ts +5 -1
  126. package/src/components/list-page/list-page-shell.svelte +2 -5
  127. package/src/components/list-page/master-detail.svelte +87 -88
  128. package/src/components/list-page/notice-bar.svelte +38 -38
  129. package/src/components/list-page/prev-next.svelte +63 -63
  130. package/src/components/list-page/rail-body.svelte +18 -18
  131. package/src/components/list-page/rail-empty.svelte +23 -30
  132. package/src/components/list-page/rail-head.svelte +24 -24
  133. package/src/components/list-page/rail-row.svelte +77 -73
  134. package/src/components/list-page/section-host.ts +2 -3
  135. package/src/components/locale-switch/locale-switch.svelte +41 -41
  136. package/src/components/mark/mark.svelte +34 -38
  137. package/src/components/open-in/index.ts +1 -5
  138. package/src/components/open-in/open-in.svelte +40 -40
  139. package/src/components/picker/mark-picker.svelte +49 -49
  140. package/src/components/picker/picker.svelte +5 -3
  141. package/src/components/section-header/section-header.test.ts +7 -1
  142. package/src/components/section-header/section-label.svelte +20 -20
  143. package/src/components/segmented/animated-tabs.svelte +31 -2
  144. package/src/components/segmented/collapse.test.ts +2 -1
  145. package/src/components/segmented/index.ts +2 -0
  146. package/src/components/segmented/segment.svelte +52 -49
  147. package/src/components/segmented/segmented-filter.svelte +79 -80
  148. package/src/components/segmented/segmented-nav.svelte +34 -40
  149. package/src/components/segmented/segmented-overflow.svelte +53 -50
  150. package/src/components/segmented/segmented-track.svelte +62 -46
  151. package/src/components/segmented/segmented.ts +2 -3
  152. package/src/components/star-rating-input/star-rating-input.svelte +103 -103
  153. package/src/components/svg/svg.svelte +4 -4
  154. package/src/components/table-view/table-view.svelte +17 -2
  155. package/src/components/table-view/table-view.test.ts +7 -1
  156. package/src/components/table-view/types.ts +11 -11
  157. package/src/components/tags-input/index.ts +13 -0
  158. package/src/components/tags-input/tags-input.svelte +305 -0
  159. package/src/components/tags-input/tags.test.ts +64 -0
  160. package/src/components/tags-input/tags.ts +142 -0
  161. package/src/components/theme/index.ts +2 -4
  162. package/src/components/theme/theme-provider.svelte +1 -3
  163. package/src/components/toolbar/toolbar-search.svelte +25 -25
  164. package/src/components/toolbar/toolbar.svelte +11 -7
  165. package/src/components/toolbar/toolbar.test.ts +10 -1
  166. package/src/design/index.css +21 -78
  167. package/src/design/polish.css +26 -35
  168. package/src/editor/editor-toolbar.svelte +104 -99
  169. package/src/editor/editor.svelte +296 -266
  170. package/src/editor/image-toolbar.svelte +20 -22
  171. package/src/editor/index.ts +1 -4
  172. package/src/editor/inline-editor.svelte +280 -238
  173. package/src/editor/markdown.ts +15 -15
  174. package/src/editor/renderer.svelte +71 -71
  175. package/src/editor/slash-menu.svelte +24 -27
  176. package/src/effects/animated-beam/diagrams/BeamSvg.svelte +1 -1
  177. package/src/effects/image-comparison/ImageComparison.svelte +11 -1
  178. package/src/effects/interactive-hover-button/InteractiveHoverButton.svelte +59 -46
  179. package/src/effects/magic-card/MagicCard.css +14 -7
  180. package/src/effects/marquee/Marquee.svelte +3 -5
  181. package/src/effects/reveal/observe.ts +0 -3
  182. package/src/effects/spotlight/Spotlight.svelte +1 -1
  183. package/src/effects/theme-toggle/ThemeToggle.css +25 -22
  184. package/src/effects/theme-toggle/ThemeToggle.svelte +6 -7
  185. package/src/gallery.test.ts +14 -4
  186. package/src/inbox/README.md +1 -3
  187. package/src/inbox/inbox.test.ts +181 -138
  188. package/src/inbox/index.ts +20 -20
  189. package/src/inbox/logic.ts +114 -111
  190. package/src/inbox/thread-inbox.svelte +227 -151
  191. package/src/inbox/types.ts +130 -108
  192. package/src/lib/theme-transition.ts +2 -4
  193. package/src/lib/tone.ts +2 -4
  194. package/src/lib/utils.ts +2 -2
  195. package/src/modules.test.ts +79 -69
  196. package/src/onboarding/define-tours.test.ts +55 -40
  197. package/src/onboarding/define-tours.ts +23 -23
  198. package/src/onboarding/demo.svelte +24 -5
  199. package/src/onboarding/index.ts +1 -8
  200. package/src/onboarding/onboarding-provider.svelte +19 -7
  201. package/src/onboarding/onboarding.svelte.ts +16 -16
  202. package/src/onboarding/pick.test.ts +61 -52
  203. package/src/onboarding/pick.ts +16 -13
  204. package/src/onboarding/stores.test.ts +80 -80
  205. package/src/onboarding/stores.ts +41 -39
  206. package/src/onboarding/tour-card.svelte +30 -5
  207. package/src/onboarding/tour-runner.svelte +12 -3
  208. package/src/onboarding/types.ts +20 -12
  209. package/src/onboarding/wait.ts +22 -22
  210. package/src/panel/side-panel.svelte +94 -93
  211. package/src/record/demo-data.ts +291 -225
  212. package/src/record/field-section.svelte +38 -34
  213. package/src/record/fields.ts +148 -96
  214. package/src/record/format.ts +63 -56
  215. package/src/record/index.ts +49 -35
  216. package/src/record/info-list.svelte +145 -114
  217. package/src/record/kinds.ts +31 -31
  218. package/src/record/logic.ts +216 -214
  219. package/src/record/next-up.svelte +45 -34
  220. package/src/record/project-list.svelte +56 -50
  221. package/src/record/record-demo.svelte +130 -110
  222. package/src/record/record-header.svelte +37 -28
  223. package/src/record/record-shell.svelte +54 -54
  224. package/src/record/record-stats.svelte +25 -25
  225. package/src/record/record-timeline.svelte +134 -114
  226. package/src/record/record.test.ts +430 -309
  227. package/src/record/steps-rail.svelte +225 -216
  228. package/src/record/types.ts +234 -215
  229. package/src/record/where-we-are.svelte +62 -58
  230. package/src/shadcn/button/button.svelte +1 -1
  231. package/src/shadcn/card/card-title.svelte +1 -5
  232. package/src/shadcn/hooks/use-highlighted-code.svelte.ts +2 -13
  233. package/src/shadcn/sheet/sheet-content.svelte +2 -4
  234. package/src/shadcn/table/table-cell.svelte +2 -3
  235. package/src/shadcn/utilities.css +3 -8
  236. package/src/slides/Controls.svelte +150 -124
  237. package/src/slides/Deck.svelte +243 -238
  238. package/src/slides/Fragment.svelte +64 -57
  239. package/src/slides/Overview.svelte +106 -101
  240. package/src/slides/Presenter.svelte +300 -266
  241. package/src/slides/Slide.svelte +185 -170
  242. package/src/slides/SlideList.svelte +16 -12
  243. package/src/slides/ambient.d.ts +1 -1
  244. package/src/slides/bridges.ts +42 -23
  245. package/src/slides/deck-controller.svelte.ts +47 -35
  246. package/src/slides/demo-slides.svelte +330 -306
  247. package/src/slides/demo.svelte +164 -160
  248. package/src/slides/export/capture.ts +20 -16
  249. package/src/slides/export/ffmpeg.ts +43 -38
  250. package/src/slides/export/index.ts +5 -5
  251. package/src/slides/export/launch.ts +3 -1
  252. package/src/slides/export/pdf.ts +30 -27
  253. package/src/slides/export/types.ts +10 -8
  254. package/src/slides/index.ts +11 -11
  255. package/src/slides/labels.ts +22 -19
  256. package/src/slides/layout.ts +9 -2
  257. package/src/slides/model.ts +4 -4
  258. package/src/slides/navigation.ts +43 -31
  259. package/src/slides/theme.css +5 -7
  260. package/src/slides/types.ts +9 -10
  261. package/src/status/define-statuses.ts +17 -24
  262. package/src/status/index.ts +5 -5
  263. package/src/status/status-badge.svelte +3 -1
  264. package/src/status/status-dot.svelte +1 -3
  265. package/src/status/status-icon.svelte +1 -1
  266. package/src/status/status-types.ts +21 -21
  267. package/src/tasks/board-order.ts +2 -4
  268. package/src/tasks/demo.svelte +189 -31
  269. package/src/tasks/detail/detail-demo.svelte +73 -13
  270. package/src/tasks/detail/detail-types.ts +45 -45
  271. package/src/tasks/detail/task-detail.svelte +40 -10
  272. package/src/tasks/detail/task-property.svelte +8 -2
  273. package/src/tasks/detail/task-thread.svelte +53 -12
  274. package/src/tasks/gantt/gantt-bar.svelte +17 -6
  275. package/src/tasks/gantt/gantt-grid.svelte +40 -9
  276. package/src/tasks/gantt/gantt-group-row.svelte +8 -2
  277. package/src/tasks/gantt/gantt-header.svelte +9 -3
  278. package/src/tasks/gantt/gantt-task-row.svelte +26 -6
  279. package/src/tasks/gantt/gantt.css +2 -1
  280. package/src/tasks/gantt/gantt.test.ts +3 -1
  281. package/src/tasks/gantt/logic.test.ts +110 -20
  282. package/src/tasks/gantt/logic.ts +42 -6
  283. package/src/tasks/inbox/inbox-card.svelte +13 -5
  284. package/src/tasks/inbox/inbox-draft.svelte +1 -1
  285. package/src/tasks/inbox/inbox-logic.ts +6 -40
  286. package/src/tasks/inbox/inbox-statuses.ts +23 -23
  287. package/src/tasks/inbox/inbox-trace.svelte +9 -3
  288. package/src/tasks/inbox/inbox-types.ts +54 -58
  289. package/src/tasks/inbox/index.ts +22 -21
  290. package/src/tasks/index.ts +49 -52
  291. package/src/tasks/kanban-board.svelte +16 -12
  292. package/src/tasks/kanban-types.ts +2 -3
  293. package/src/tasks/presets/sales.ts +6 -6
  294. package/src/tasks/task-gantt.svelte +28 -8
  295. package/src/tasks/task-list.svelte +41 -17
  296. package/src/tasks/time/duration.ts +40 -40
  297. package/src/tasks/time/time-chip.svelte +9 -2
  298. package/src/tasks/time/time-dot.svelte +1 -1
  299. package/src/tasks/time/time-ring.svelte +11 -3
  300. package/src/tasks/time/time-tracker.svelte +57 -15
  301. package/src/tasks/time/time-types.ts +57 -57
  302. package/src/tasks/use-kanban-dnd.svelte.ts +2 -3
  303. package/src/thread/index.ts +7 -7
  304. package/src/thread/logic.ts +36 -33
  305. package/src/thread/thread.svelte +82 -16
  306. package/src/thread/thread.test.ts +58 -47
  307. package/src/thread/types.ts +68 -68
  308. package/src/tokens/craft.css +17 -53
  309. package/src/tokens/index.css +3 -13
  310. package/src/tokens/palette.css +13 -11
  311. package/src/tokens/primitives.css +10 -23
  312. package/src/tasks/inbox/inbox.svelte +0 -203
@@ -1,53 +1,53 @@
1
1
  <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import { cn } from "../lib/utils.js";
6
- import { Progress } from "../shadcn/progress/index.js";
7
- import { TONE_TEXT } from "../lib/tone.js";
8
- import StatusBadge from "../status/status-badge.svelte";
9
- import { todayIso } from "../tasks/dates.js";
10
- import { formatMoney, formatRange } from "./format.js";
11
- import {
12
- resolveLabels,
13
- type RecordLabels,
14
- type RecordSummary,
15
- type StageDef,
16
- } from "./types.js";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import { cn } from "../lib/utils.js";
6
+ import { Progress } from "../shadcn/progress/index.js";
7
+ import { TONE_TEXT } from "../lib/tone.js";
8
+ import StatusBadge from "../status/status-badge.svelte";
9
+ import { todayIso } from "../tasks/dates.js";
10
+ import { formatMoney, formatRange } from "./format.js";
11
+ import {
12
+ resolveLabels,
13
+ type RecordLabels,
14
+ type RecordSummary,
15
+ type StageDef,
16
+ } from "./types.js";
17
17
 
18
- // =========================
19
- // PROPS
20
- // =========================
21
- interface Props {
22
- projects: RecordSummary[];
23
- /** The agency's stages: each project draws its stage's label and tone. */
24
- stages: StageDef[];
25
- /** A project being looked at, marked in the list. */
26
- activeId?: string | null;
27
- /** Money shown when a project has none of its own. */
28
- currency?: string;
29
- labels?: Partial<RecordLabels>;
30
- locale?: string;
31
- /** ISO date. Decides "late". Defaults to the viewer's today. */
32
- today?: string;
33
- class?: string;
34
- }
35
- let {
36
- projects,
37
- stages,
38
- activeId = null,
39
- currency = "EUR",
40
- labels,
41
- locale,
42
- today = todayIso(),
43
- class: className,
44
- }: Props = $props();
18
+ // =========================
19
+ // PROPS
20
+ // =========================
21
+ interface Props {
22
+ projects: RecordSummary[];
23
+ /** The agency's stages: each project draws its stage's label and tone. */
24
+ stages: StageDef[];
25
+ /** A project being looked at, marked in the list. */
26
+ activeId?: string | null;
27
+ /** Money shown when a project has none of its own. */
28
+ currency?: string;
29
+ labels?: Partial<RecordLabels>;
30
+ locale?: string;
31
+ /** ISO date. Decides "late". Defaults to the viewer's today. */
32
+ today?: string;
33
+ class?: string;
34
+ }
35
+ let {
36
+ projects,
37
+ stages,
38
+ activeId = null,
39
+ currency = "EUR",
40
+ labels,
41
+ locale,
42
+ today = todayIso(),
43
+ class: className,
44
+ }: Props = $props();
45
45
 
46
- // =========================
47
- // DERIVED
48
- // =========================
49
- let t = $derived(resolveLabels(labels));
50
- let stageByKey = $derived(new Map(stages.map((s) => [s.key, s])));
46
+ // =========================
47
+ // DERIVED
48
+ // =========================
49
+ let t = $derived(resolveLabels(labels));
50
+ let stageByKey = $derived(new Map(stages.map((s) => [s.key, s])));
51
51
  </script>
52
52
 
53
53
  <!-- =========================== -->
@@ -67,7 +67,9 @@ let stageByKey = $derived(new Map(stages.map((s) => [s.key, s])));
67
67
  </span>
68
68
  <span class="flex flex-wrap items-center gap-x-2 gap-y-1">
69
69
  <StatusBadge tone={stage?.tone ?? "neutral"} dot capitalize={false}>
70
- {stage ? stage.label : project.stageKey}{project.stageDetail ? ` · ${project.stageDetail}` : ""}
70
+ {stage ? stage.label : project.stageKey}{project.stageDetail
71
+ ? ` · ${project.stageDetail}`
72
+ : ""}
71
73
  </StatusBadge>
72
74
  {#if project.whereWeAre?.health}
73
75
  <StatusBadge tone={project.whereWeAre.health.tone} capitalize={false}>
@@ -76,7 +78,9 @@ let stageByKey = $derived(new Map(stages.map((s) => [s.key, s])));
76
78
  {/if}
77
79
  </span>
78
80
  {#if project.whereWeAre?.text}
79
- <span class="line-clamp-2 text-xs leading-snug text-muted-foreground">{project.whereWeAre.text}</span>
81
+ <span class="line-clamp-2 text-xs leading-snug text-muted-foreground"
82
+ >{project.whereWeAre.text}</span
83
+ >
80
84
  {/if}
81
85
  {#if project.progress && project.progress.total > 0}
82
86
  <span class="flex items-center gap-2">
@@ -96,7 +100,9 @@ let stageByKey = $derived(new Map(stages.map((s) => [s.key, s])));
96
100
  <span class="flex flex-wrap items-baseline gap-x-2 text-xs text-muted-foreground">
97
101
  <span>{t.next}</span>
98
102
  {#if next.date}
99
- <span class={cn("tabular-nums", late && TONE_TEXT.warning)}>{formatRange(next.date, next.endDate, locale)}</span>
103
+ <span class={cn("tabular-nums", late && TONE_TEXT.warning)}
104
+ >{formatRange(next.date, next.endDate, locale)}</span
105
+ >
100
106
  {/if}
101
107
  <span class="min-w-0 truncate text-foreground">{next.label}</span>
102
108
  </span>
@@ -1,117 +1,130 @@
1
1
  <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import { DotsThree, NotePencil } from "phosphor-svelte";
6
- import { SegmentedFilter } from "../components/segmented/index.js";
7
- import { Button } from "../shadcn/button/index.js";
8
- import { Input } from "../shadcn/input/index.js";
9
- import * as Tabs from "../shadcn/tabs/index.js";
10
- import RecordShell from "./record-shell.svelte";
11
- import RecordHeader from "./record-header.svelte";
12
- import WhereWeAre from "./where-we-are.svelte";
13
- import NextUp from "./next-up.svelte";
14
- import RecordStats from "./record-stats.svelte";
15
- import StepsRail from "./steps-rail.svelte";
16
- import RecordTimeline from "./record-timeline.svelte";
17
- import ProjectList from "./project-list.svelte";
18
- import FieldSection from "./field-section.svelte";
19
- import InfoList from "./info-list.svelte";
20
- import { formatAge, formatMoney, formatRange } from "./format.js";
21
- import {
22
- latestWhereWeAre,
23
- mergeClientEvents,
24
- nextAcrossProjects,
25
- rollupProjects,
26
- stepProgress,
27
- } from "./logic.js";
28
- import type { FieldValue, RecordEvent, StepDef } from "./types.js";
29
- import {
30
- DEMO_NOW,
31
- DEMO_TODAY,
32
- clientFields,
33
- clientOnlyEvents,
34
- clientProjects,
35
- clientSections,
36
- clientValues,
37
- projectFields,
38
- projectSections,
39
- stages,
40
- stepGroups,
41
- website,
42
- websiteEvents,
43
- websiteSteps,
44
- websiteValues,
45
- } from "./demo-data.js";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import { DotsThree, NotePencil } from "phosphor-svelte";
6
+ import { SegmentedFilter } from "../components/segmented/index.js";
7
+ import { Button } from "../shadcn/button/index.js";
8
+ import { Input } from "../shadcn/input/index.js";
9
+ import * as Tabs from "../shadcn/tabs/index.js";
10
+ import RecordShell from "./record-shell.svelte";
11
+ import RecordHeader from "./record-header.svelte";
12
+ import WhereWeAre from "./where-we-are.svelte";
13
+ import NextUp from "./next-up.svelte";
14
+ import RecordStats from "./record-stats.svelte";
15
+ import StepsRail from "./steps-rail.svelte";
16
+ import RecordTimeline from "./record-timeline.svelte";
17
+ import ProjectList from "./project-list.svelte";
18
+ import FieldSection from "./field-section.svelte";
19
+ import InfoList from "./info-list.svelte";
20
+ import { formatAge, formatMoney, formatRange } from "./format.js";
21
+ import {
22
+ latestWhereWeAre,
23
+ mergeClientEvents,
24
+ nextAcrossProjects,
25
+ rollupProjects,
26
+ stepProgress,
27
+ } from "./logic.js";
28
+ import type { FieldValue, RecordEvent, StepDef } from "./types.js";
29
+ import {
30
+ DEMO_NOW,
31
+ DEMO_TODAY,
32
+ clientFields,
33
+ clientOnlyEvents,
34
+ clientProjects,
35
+ clientSections,
36
+ clientValues,
37
+ projectFields,
38
+ projectSections,
39
+ stages,
40
+ stepGroups,
41
+ website,
42
+ websiteEvents,
43
+ websiteSteps,
44
+ websiteValues,
45
+ } from "./demo-data.js";
46
46
 
47
- // =========================
48
- // STATE
49
- // =========================
50
- type Fixture = "project" | "client";
51
- let fixture = $state<Fixture>("project");
52
- let narrow = $state(false);
53
- let tab = $state<"activity" | "notes">("activity");
54
- let filter = $state("all");
55
- let steps = $state<StepDef[]>(structuredClone(websiteSteps));
56
- let activeStep = $state<string | null>("s2");
57
- let projectValues = $state<Record<string, FieldValue | undefined>>({ ...websiteValues });
58
- let personValues = $state<Record<string, FieldValue | undefined>>({ ...clientValues });
59
- let extra = $state<RecordEvent[]>([]);
60
- let draftNote = $state("");
61
- let railOpen = $state(false);
47
+ // =========================
48
+ // STATE
49
+ // =========================
50
+ type Fixture = "project" | "client";
51
+ let fixture = $state<Fixture>("project");
52
+ let narrow = $state(false);
53
+ let tab = $state<"activity" | "notes">("activity");
54
+ let filter = $state("all");
55
+ let steps = $state<StepDef[]>(structuredClone(websiteSteps));
56
+ let activeStep = $state<string | null>("s2");
57
+ let projectValues = $state<Record<string, FieldValue | undefined>>({ ...websiteValues });
58
+ let personValues = $state<Record<string, FieldValue | undefined>>({ ...clientValues });
59
+ let extra = $state<RecordEvent[]>([]);
60
+ let draftNote = $state("");
61
+ let railOpen = $state(false);
62
62
 
63
- // =========================
64
- // DERIVED
65
- // =========================
66
- const locale = "en-GB";
67
- let projectEvents = $derived([...extra, ...websiteEvents]);
68
- let clientEvents = $derived(
69
- mergeClientEvents(
70
- clientOnlyEvents,
71
- clientProjects.map((p) => ({
72
- id: p.summary.id,
73
- name: p.summary.name,
74
- href: p.summary.href,
75
- events: p.events,
76
- })),
77
- ),
78
- );
79
- let summaries = $derived(clientProjects.map((p) => p.summary));
80
- let rollup = $derived(rollupProjects(summaries, stages));
81
- let latest = $derived(latestWhereWeAre(summaries));
82
- let next = $derived(nextAcrossProjects(summaries, stages));
83
- let progress = $derived(stepProgress(steps));
84
- let notes = $derived(projectEvents.filter((e) => e.kind === "note"));
85
- let stage = $derived(stages.find((s) => s.key === website.stageKey));
63
+ // =========================
64
+ // DERIVED
65
+ // =========================
66
+ const locale = "en-GB";
67
+ let projectEvents = $derived([...extra, ...websiteEvents]);
68
+ let clientEvents = $derived(
69
+ mergeClientEvents(
70
+ clientOnlyEvents,
71
+ clientProjects.map((p) => ({
72
+ id: p.summary.id,
73
+ name: p.summary.name,
74
+ href: p.summary.href,
75
+ events: p.events,
76
+ })),
77
+ ),
78
+ );
79
+ let summaries = $derived(clientProjects.map((p) => p.summary));
80
+ let rollup = $derived(rollupProjects(summaries, stages));
81
+ let latest = $derived(latestWhereWeAre(summaries));
82
+ let next = $derived(nextAcrossProjects(summaries, stages));
83
+ let progress = $derived(stepProgress(steps));
84
+ let notes = $derived(projectEvents.filter((e) => e.kind === "note"));
85
+ let stage = $derived(stages.find((s) => s.key === website.stageKey));
86
86
 
87
- let clientStats = $derived.by(() => {
88
- const eur = rollup.totals.EUR;
89
- return [
90
- { label: "Projects", value: String(rollup.count), sub: `${rollup.activeCount} active` },
91
- { label: "Pipeline", value: formatMoney(eur?.pipeline ?? 0, "EUR", locale, true) },
92
- { label: "Won", value: formatMoney(eur?.won ?? 0, "EUR", locale, true) },
93
- { label: "Paid", value: formatMoney(eur?.paid ?? 0, "EUR", locale, true), tone: "good" as const },
94
- { label: "Last call", value: rollup.lastCallAt ? formatAge(rollup.lastCallAt, DEMO_NOW, locale) : "None yet" },
95
- ];
96
- });
87
+ let clientStats = $derived.by(() => {
88
+ const eur = rollup.totals.EUR;
89
+ return [
90
+ { label: "Projects", value: String(rollup.count), sub: `${rollup.activeCount} active` },
91
+ { label: "Pipeline", value: formatMoney(eur?.pipeline ?? 0, "EUR", locale, true) },
92
+ { label: "Won", value: formatMoney(eur?.won ?? 0, "EUR", locale, true) },
93
+ {
94
+ label: "Paid",
95
+ value: formatMoney(eur?.paid ?? 0, "EUR", locale, true),
96
+ tone: "good" as const,
97
+ },
98
+ {
99
+ label: "Last call",
100
+ value: rollup.lastCallAt ? formatAge(rollup.lastCallAt, DEMO_NOW, locale) : "None yet",
101
+ },
102
+ ];
103
+ });
97
104
 
98
- // =========================
99
- // FUNCTIONS
100
- // =========================
101
- function addNote() {
102
- const title = draftNote.trim();
103
- if (!title) return;
104
- extra = [
105
- { id: `n${extra.length}`, kind: "note", occurredAt: DEMO_NOW.toISOString(), title, actor: "Ramzi" },
106
- ...extra,
107
- ];
108
- draftNote = "";
109
- }
105
+ // =========================
106
+ // FUNCTIONS
107
+ // =========================
108
+ function addNote() {
109
+ const title = draftNote.trim();
110
+ if (!title) return;
111
+ extra = [
112
+ {
113
+ id: `n${extra.length}`,
114
+ kind: "note",
115
+ occurredAt: DEMO_NOW.toISOString(),
116
+ title,
117
+ actor: "Ramzi",
118
+ },
119
+ ...extra,
120
+ ];
121
+ draftNote = "";
122
+ }
110
123
 
111
- // A real host would round-trip here. The pause lets the rail show its optimistic order.
112
- async function pause() {
113
- await new Promise((resolve) => setTimeout(resolve, 250));
114
- }
124
+ // A real host would round-trip here. The pause lets the rail show its optimistic order.
125
+ async function pause() {
126
+ await new Promise((resolve) => setTimeout(resolve, 250));
127
+ }
115
128
  </script>
116
129
 
117
130
  <!-- =========================== -->
@@ -125,7 +138,12 @@ async function pause() {
125
138
  addNote();
126
139
  }}
127
140
  >
128
- <Input bind:value={draftNote} placeholder="Write a note" aria-label="Write a note" class="flex-1" />
141
+ <Input
142
+ bind:value={draftNote}
143
+ placeholder="Write a note"
144
+ aria-label="Write a note"
145
+ class="flex-1"
146
+ />
129
147
  <Button type="submit" variant="outline" disabled={!draftNote.trim()}>
130
148
  <NotePencil data-icon="inline-start" />
131
149
  Add
@@ -188,7 +206,9 @@ async function pause() {
188
206
  ]}
189
207
  >
190
208
  {#snippet actions()}
191
- <Button variant="ghost" size="sm" icon aria-label="More actions"><DotsThree weight="bold" /></Button>
209
+ <Button variant="ghost" size="sm" icon aria-label="More actions"
210
+ ><DotsThree weight="bold" /></Button
211
+ >
192
212
  {/snippet}
193
213
  </RecordHeader>
194
214
  {/snippet}
@@ -1,33 +1,42 @@
1
1
  <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import type { Snippet } from "svelte";
6
- import { CaretLeft } from "phosphor-svelte";
7
- import { cn } from "../lib/utils.js";
8
- import type { Tone } from "../lib/tone.js";
9
- import StatusBadge from "../status/status-badge.svelte";
10
- import { gutterClass } from "./gutter.js";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import type { Snippet } from "svelte";
6
+ import { CaretLeft } from "phosphor-svelte";
7
+ import { cn } from "../lib/utils.js";
8
+ import type { Tone } from "../lib/tone.js";
9
+ import StatusBadge from "../status/status-badge.svelte";
10
+ import { gutterClass } from "./gutter.js";
11
11
 
12
- // =========================
13
- // PROPS
14
- // =========================
15
- interface Props {
16
- title: string;
17
- /** A mark drawn before the title: a logo, an avatar. */
18
- logo?: Snippet;
19
- /** The stage, in the agency's own word and the tone of its category. */
20
- stage?: { label: string; tone: Tone; detail?: string | null } | null;
21
- /** What this record sits under, such as the client of a project. */
22
- parent?: { label: string; href: string } | null;
23
- /** Quiet facts after the stage: a value, dates. */
24
- facts?: string[];
25
- actions?: Snippet;
26
- /** Under the title row, for a stat strip. */
27
- children?: Snippet;
28
- class?: string;
29
- }
30
- let { title, logo, stage, parent, facts = [], actions, children, class: className }: Props = $props();
12
+ // =========================
13
+ // PROPS
14
+ // =========================
15
+ interface Props {
16
+ title: string;
17
+ /** A mark drawn before the title: a logo, an avatar. */
18
+ logo?: Snippet;
19
+ /** The stage, in the agency's own word and the tone of its category. */
20
+ stage?: { label: string; tone: Tone; detail?: string | null } | null;
21
+ /** What this record sits under, such as the client of a project. */
22
+ parent?: { label: string; href: string } | null;
23
+ /** Quiet facts after the stage: a value, dates. */
24
+ facts?: string[];
25
+ actions?: Snippet;
26
+ /** Under the title row, for a stat strip. */
27
+ children?: Snippet;
28
+ class?: string;
29
+ }
30
+ let {
31
+ title,
32
+ logo,
33
+ stage,
34
+ parent,
35
+ facts = [],
36
+ actions,
37
+ children,
38
+ class: className,
39
+ }: Props = $props();
31
40
  </script>
32
41
 
33
42
  <!-- =========================== -->
@@ -1,60 +1,60 @@
1
1
  <script lang="ts" generics="T extends string = string">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import type { Snippet } from "svelte";
6
- import { CaretRight } from "phosphor-svelte";
7
- import { cn } from "../lib/utils.js";
8
- import { AnimatedTabs, type TabDef } from "../components/segmented/index.js";
9
- import * as Sheet from "../shadcn/sheet/index.js";
10
- import { Progress } from "../shadcn/progress/index.js";
11
- import { gutterClass } from "./gutter.js";
12
- import { resolveLabels, type RecordLabels } from "./types.js";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import type { Snippet } from "svelte";
6
+ import { CaretRight } from "phosphor-svelte";
7
+ import { cn } from "../lib/utils.js";
8
+ import { AnimatedTabs, type TabDef } from "../components/segmented/index.js";
9
+ import * as Sheet from "../shadcn/sheet/index.js";
10
+ import { Progress } from "../shadcn/progress/index.js";
11
+ import { gutterClass } from "./gutter.js";
12
+ import { resolveLabels, type RecordLabels } from "./types.js";
13
13
 
14
- // =========================
15
- // PROPS
16
- // =========================
17
- interface Props {
18
- header: Snippet;
19
- /** Where we are, next up, stats: whatever sits between the header and the body. */
20
- banner?: Snippet;
21
- /** The tab row. Put each panel in a `Tabs.Content` from `@heyramzi/ui/shadcn/tabs`. */
22
- tabs?: readonly TabDef<T>[];
23
- tab?: T;
24
- tabsLabel?: string;
25
- /** The side rail. Docked at 48rem and wider, a sheet behind one row under that. */
26
- rail?: Snippet;
27
- /** The sheet's title, read out by screen readers. Defaults to the steps label. */
28
- railTitle?: string;
29
- /** Fills the narrow row's bar. Leave it out for a row with no progress. */
30
- railProgress?: { done: number; total: number } | null;
31
- railOpen?: boolean;
32
- children: Snippet;
33
- labels?: Partial<RecordLabels>;
34
- class?: string;
35
- }
36
- let {
37
- header,
38
- banner,
39
- tabs,
40
- tab = $bindable(),
41
- tabsLabel,
42
- rail,
43
- railTitle,
44
- railProgress = null,
45
- railOpen = $bindable(false),
46
- children,
47
- labels,
48
- class: className,
49
- }: Props = $props();
14
+ // =========================
15
+ // PROPS
16
+ // =========================
17
+ interface Props {
18
+ header: Snippet;
19
+ /** Where we are, next up, stats: whatever sits between the header and the body. */
20
+ banner?: Snippet;
21
+ /** The tab row. Put each panel in a `Tabs.Content` from `@heyramzi/ui/shadcn/tabs`. */
22
+ tabs?: readonly TabDef<T>[];
23
+ tab?: T;
24
+ tabsLabel?: string;
25
+ /** The side rail. Docked at 48rem and wider, a sheet behind one row under that. */
26
+ rail?: Snippet;
27
+ /** The sheet's title, read out by screen readers. Defaults to the steps label. */
28
+ railTitle?: string;
29
+ /** Fills the narrow row's bar. Leave it out for a row with no progress. */
30
+ railProgress?: { done: number; total: number } | null;
31
+ railOpen?: boolean;
32
+ children: Snippet;
33
+ labels?: Partial<RecordLabels>;
34
+ class?: string;
35
+ }
36
+ let {
37
+ header,
38
+ banner,
39
+ tabs,
40
+ tab = $bindable(),
41
+ tabsLabel,
42
+ rail,
43
+ railTitle,
44
+ railProgress = null,
45
+ railOpen = $bindable(false),
46
+ children,
47
+ labels,
48
+ class: className,
49
+ }: Props = $props();
50
50
 
51
- // =========================
52
- // DERIVED
53
- // =========================
54
- let t = $derived(resolveLabels(labels));
55
- let percent = $derived(
56
- railProgress && railProgress.total > 0 ? (railProgress.done / railProgress.total) * 100 : 0,
57
- );
51
+ // =========================
52
+ // DERIVED
53
+ // =========================
54
+ let t = $derived(resolveLabels(labels));
55
+ let percent = $derived(
56
+ railProgress && railProgress.total > 0 ? (railProgress.done / railProgress.total) * 100 : 0,
57
+ );
58
58
  </script>
59
59
 
60
60
  <!-- =========================== -->
@@ -1,31 +1,31 @@
1
1
  <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import { cn } from "../lib/utils.js";
6
- import type { PanelStat } from "../panel/side-panel-types.js";
7
- import { gutterClass } from "./gutter.js";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import { cn } from "../lib/utils.js";
6
+ import type { PanelStat } from "../panel/side-panel-types.js";
7
+ import { gutterClass } from "./gutter.js";
8
8
 
9
- // =========================
10
- // PROPS
11
- // =========================
12
- interface Props {
13
- stats: PanelStat[];
14
- /** Sits flush with the record's gutter. Off inside a card. */
15
- gutter?: boolean;
16
- class?: string;
17
- }
18
- let { stats, gutter = true, class: className }: Props = $props();
9
+ // =========================
10
+ // PROPS
11
+ // =========================
12
+ interface Props {
13
+ stats: PanelStat[];
14
+ /** Sits flush with the record's gutter. Off inside a card. */
15
+ gutter?: boolean;
16
+ class?: string;
17
+ }
18
+ let { stats, gutter = true, class: className }: Props = $props();
19
19
 
20
- // =========================
21
- // DERIVED
22
- // =========================
23
- const TONE = {
24
- default: "",
25
- muted: "text-muted-foreground",
26
- good: "text-success",
27
- bad: "text-destructive",
28
- } as const;
20
+ // =========================
21
+ // DERIVED
22
+ // =========================
23
+ const TONE = {
24
+ default: "",
25
+ muted: "text-muted-foreground",
26
+ good: "text-success",
27
+ bad: "text-destructive",
28
+ } as const;
29
29
  </script>
30
30
 
31
31
  <!-- =========================== -->