@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,13 +1,13 @@
1
1
  // Fake data for the demo and the screenshots. Every name here is invented.
2
2
  import type {
3
- FieldDef,
4
- FieldSectionDef,
5
- FieldValue,
6
- RecordEvent,
7
- RecordSummary,
8
- StageDef,
9
- StepDef,
10
- StepGroupDef,
3
+ FieldDef,
4
+ FieldSectionDef,
5
+ FieldValue,
6
+ RecordEvent,
7
+ RecordSummary,
8
+ StageDef,
9
+ StepDef,
10
+ StepGroupDef,
11
11
  } from "./types.js";
12
12
 
13
13
  /** The demo's clock, so a screenshot reads the same on any day. */
@@ -15,272 +15,338 @@ export const DEMO_NOW = new Date("2026-10-21T12:00:00Z");
15
15
  export const DEMO_TODAY = "2026-10-21";
16
16
 
17
17
  export const stages: StageDef[] = [
18
- { key: "lead", label: "Lead", tone: "neutral", category: "open" },
19
- { key: "sent", label: "Proposal sent", tone: "info", category: "proposal" },
20
- { key: "build", label: "In build", tone: "accent", category: "ongoing" },
21
- { key: "paid", label: "Paid", tone: "success", category: "completed" },
22
- { key: "lost", label: "Lost", tone: "destructive", category: "lost" },
18
+ { key: "lead", label: "Lead", tone: "neutral", category: "open" },
19
+ { key: "sent", label: "Proposal sent", tone: "info", category: "proposal" },
20
+ { key: "build", label: "In build", tone: "accent", category: "ongoing" },
21
+ { key: "paid", label: "Paid", tone: "success", category: "completed" },
22
+ { key: "lost", label: "Lost", tone: "destructive", category: "lost" },
23
23
  ];
24
24
 
25
25
  export const stepGroups: StepGroupDef[] = [
26
- { key: "sales", label: "Sales" },
27
- { key: "delivery", label: "Delivery" },
26
+ { key: "sales", label: "Sales" },
27
+ { key: "delivery", label: "Delivery" },
28
28
  ];
29
29
 
30
30
  export const websiteSteps: StepDef[] = [
31
- { id: "s1", label: "Discovery call", done: true, group: "sales", start: "2026-10-03" },
32
- { id: "s2", label: "Proposal", done: false, group: "sales", start: "2026-10-21" },
33
- { id: "s3", label: "Kickoff", done: false, group: "delivery", start: "2026-10-26" },
34
- { id: "s4", label: "Design review", done: false, group: "delivery", start: "2026-11-10" },
35
- { id: "s5", label: "Build", done: false, group: "delivery", start: "2026-11-12", end: "2026-12-04" },
36
- { id: "s6", label: "Cutover", done: false, group: "delivery", start: "2026-12-12", end: "2026-12-13" },
37
- { id: "s7", label: "Training", done: false, group: "delivery", hidden: true },
31
+ { id: "s1", label: "Discovery call", done: true, group: "sales", start: "2026-10-03" },
32
+ { id: "s2", label: "Proposal", done: false, group: "sales", start: "2026-10-21" },
33
+ { id: "s3", label: "Kickoff", done: false, group: "delivery", start: "2026-10-26" },
34
+ { id: "s4", label: "Design review", done: false, group: "delivery", start: "2026-11-10" },
35
+ {
36
+ id: "s5",
37
+ label: "Build",
38
+ done: false,
39
+ group: "delivery",
40
+ start: "2026-11-12",
41
+ end: "2026-12-04",
42
+ },
43
+ {
44
+ id: "s6",
45
+ label: "Cutover",
46
+ done: false,
47
+ group: "delivery",
48
+ start: "2026-12-12",
49
+ end: "2026-12-13",
50
+ },
51
+ { id: "s7", label: "Training", done: false, group: "delivery", hidden: true },
38
52
  ];
39
53
 
40
54
  export const projectSections: FieldSectionDef[] = [
41
- { key: "scope", label: "Scope" },
42
- { key: "loss", label: "Loss" },
55
+ { key: "scope", label: "Scope" },
56
+ { key: "loss", label: "Loss" },
43
57
  ];
44
58
 
45
59
  export const projectFields: FieldDef[] = [
46
- {
47
- key: "type",
48
- label: "Type",
49
- type: "choice",
50
- section: "scope",
51
- options: [
52
- { key: "strategy", label: "Strategy" },
53
- { key: "implementation", label: "Implementation" },
54
- ],
55
- },
56
- { key: "budget", label: "Budget", type: "money", section: "scope" },
57
- {
58
- key: "urgency",
59
- label: "Urgency",
60
- type: "choice",
61
- section: "scope",
62
- options: [
63
- { key: "low", label: "Low", tone: "neutral" },
64
- { key: "high", label: "High", tone: "warning" },
65
- ],
66
- },
67
- { key: "goLive", label: "Go live", type: "date", section: "scope" },
68
- { key: "context", label: "Context", type: "longtext", section: "scope" },
69
- {
70
- key: "reason",
71
- label: "Reason",
72
- type: "choice",
73
- section: "loss",
74
- showWhen: { key: "lossReasonOn", equals: "yes" },
75
- options: [{ key: "price", label: "Price" }],
76
- },
60
+ {
61
+ key: "type",
62
+ label: "Type",
63
+ type: "choice",
64
+ section: "scope",
65
+ options: [
66
+ { key: "strategy", label: "Strategy" },
67
+ { key: "implementation", label: "Implementation" },
68
+ ],
69
+ },
70
+ { key: "budget", label: "Budget", type: "money", section: "scope" },
71
+ {
72
+ key: "urgency",
73
+ label: "Urgency",
74
+ type: "choice",
75
+ section: "scope",
76
+ options: [
77
+ { key: "low", label: "Low", tone: "neutral" },
78
+ { key: "high", label: "High", tone: "warning" },
79
+ ],
80
+ },
81
+ { key: "goLive", label: "Go live", type: "date", section: "scope" },
82
+ { key: "context", label: "Context", type: "longtext", section: "scope" },
83
+ {
84
+ key: "reason",
85
+ label: "Reason",
86
+ type: "choice",
87
+ section: "loss",
88
+ showWhen: { key: "lossReasonOn", equals: "yes" },
89
+ options: [{ key: "price", label: "Price" }],
90
+ },
77
91
  ];
78
92
 
79
93
  export const clientSections: FieldSectionDef[] = [{ key: "about", label: "About" }];
80
94
 
81
95
  export const clientFields: FieldDef[] = [
82
- { key: "business", label: "Business", type: "longtext", section: "about" },
83
- {
84
- key: "size",
85
- label: "Size",
86
- type: "choice",
87
- section: "about",
88
- options: [
89
- { key: "1-10", label: "1 to 10" },
90
- { key: "11-50", label: "11 to 50" },
91
- { key: "51+", label: "51 or more" },
92
- ],
93
- },
94
- {
95
- key: "sector",
96
- label: "Sector",
97
- type: "choice",
98
- section: "about",
99
- options: ["Dental", "Legal", "Retail", "Logistics", "Education", "Finance", "Health", "Hospitality", "Media"].map(
100
- (label) => ({ key: label.toLowerCase(), label }),
101
- ),
102
- },
103
- {
104
- key: "needs",
105
- label: "Needs",
106
- type: "multi",
107
- section: "about",
108
- options: [
109
- { key: "site", label: "Website" },
110
- { key: "booking", label: "Booking" },
111
- { key: "crm", label: "CRM" },
112
- { key: "old", label: "Print", archived: true },
113
- ],
114
- },
115
- { key: "site", label: "Website", type: "url", section: "about" },
116
- { key: "email", label: "Email", type: "email", section: "about" },
117
- { key: "tz", label: "Time zone", type: "timezone", section: "about" },
96
+ { key: "business", label: "Business", type: "longtext", section: "about" },
97
+ {
98
+ key: "size",
99
+ label: "Size",
100
+ type: "choice",
101
+ section: "about",
102
+ options: [
103
+ { key: "1-10", label: "1 to 10" },
104
+ { key: "11-50", label: "11 to 50" },
105
+ { key: "51+", label: "51 or more" },
106
+ ],
107
+ },
108
+ {
109
+ key: "sector",
110
+ label: "Sector",
111
+ type: "choice",
112
+ section: "about",
113
+ options: [
114
+ "Dental",
115
+ "Legal",
116
+ "Retail",
117
+ "Logistics",
118
+ "Education",
119
+ "Finance",
120
+ "Health",
121
+ "Hospitality",
122
+ "Media",
123
+ ].map((label) => ({ key: label.toLowerCase(), label })),
124
+ },
125
+ {
126
+ key: "needs",
127
+ label: "Needs",
128
+ type: "multi",
129
+ section: "about",
130
+ options: [
131
+ { key: "site", label: "Website" },
132
+ { key: "booking", label: "Booking" },
133
+ { key: "crm", label: "CRM" },
134
+ { key: "old", label: "Print", archived: true },
135
+ ],
136
+ },
137
+ { key: "site", label: "Website", type: "url", section: "about" },
138
+ { key: "email", label: "Email", type: "email", section: "about" },
139
+ { key: "tz", label: "Time zone", type: "timezone", section: "about" },
118
140
  ];
119
141
 
120
142
  export const websiteValues: Record<string, FieldValue> = {
121
- type: "implementation",
122
- budget: 12000,
123
- urgency: "high",
124
- goLive: "2026-12-14",
125
- context: "Replace the brochure site. Booking stays on the old tool until spring.",
143
+ type: "implementation",
144
+ budget: 12000,
145
+ urgency: "high",
146
+ goLive: "2026-12-14",
147
+ context: "Replace the brochure site. Booking stays on the old tool until spring.",
126
148
  };
127
149
 
128
150
  export const clientValues: Record<string, FieldValue> = {
129
- business: "A dental group in Lyon with 3 clinics and a shared front desk.",
130
- size: "11-50",
131
- sector: "dental",
132
- needs: ["site", "booking", "old"],
133
- site: "https://marlowfinch.example",
134
- email: "jane@marlowfinch.example",
135
- tz: "Europe/Paris",
151
+ business: "A dental group in Lyon with 3 clinics and a shared front desk.",
152
+ size: "11-50",
153
+ sector: "dental",
154
+ needs: ["site", "booking", "old"],
155
+ site: "https://marlowfinch.example",
156
+ email: "jane@marlowfinch.example",
157
+ tz: "Europe/Paris",
136
158
  };
137
159
 
138
160
  const note = (id: string, at: string, title: string, body?: string): RecordEvent => ({
139
- id,
140
- kind: "note",
141
- occurredAt: at,
142
- title,
143
- body,
144
- actor: "Ramzi",
161
+ id,
162
+ kind: "note",
163
+ occurredAt: at,
164
+ title,
165
+ body,
166
+ actor: "Ramzi",
145
167
  });
146
168
 
147
169
  export const websiteEvents: RecordEvent[] = [
148
- {
149
- id: "w1",
150
- kind: "status",
151
- occurredAt: "2026-10-18T09:30:00Z",
152
- title: "Sent SOW v2, legal is reviewing",
153
- body: "The payment terms have 2 redlines. Sofia answers Monday and asks for the signature by the 26th, so the kickoff holds.",
154
- actor: "Sofia",
155
- },
156
- {
157
- id: "w2",
158
- kind: "call",
159
- occurredAt: "2026-10-21T08:15:00Z",
160
- title: "Scoping review",
161
- body: "45 minutes with Jane and the clinic manager. Booking stays out of scope. They want the new site to carry the clinic pages and nothing else.",
162
- details: [
163
- { label: "Length", value: "45 min" },
164
- { label: "With", value: "Jane Roe, Tom Bell" },
165
- ],
166
- href: "#call-scoping",
167
- },
168
- {
169
- id: "w3",
170
- kind: "stage",
171
- occurredAt: "2026-10-12T14:00:00Z",
172
- title: "Stage set to Proposal sent",
173
- actor: "Sofia",
174
- },
175
- note("w4", "2026-10-09T16:40:00Z", "Their fear is another agency that vanishes", "Said it twice. Lead the proposal with who answers the phone."),
176
- {
177
- id: "w5",
178
- kind: "invoice.sent",
179
- occurredAt: "2026-10-06T10:00:00Z",
180
- title: "Deposit invoice",
181
- amount: 3600,
182
- currency: "EUR",
183
- },
184
- {
185
- id: "w6",
186
- kind: "call",
187
- occurredAt: "2026-10-03T13:00:00Z",
188
- title: "Discovery call",
189
- body: "Jane runs 3 clinics and the site is 4 years old.",
190
- },
191
- { id: "w7", kind: "milestone.done", occurredAt: "2026-10-03T15:00:00Z", title: "Discovery call done" },
192
- ...Array.from({ length: 34 }, (_, i): RecordEvent => ({
193
- id: `old${i}`,
194
- kind: i % 3 === 0 ? "email" : "note",
195
- occurredAt: new Date(Date.UTC(2026, 8, 28 - i, 9, 0)).toISOString(),
196
- title: i % 3 === 0 ? "Re: clinic pages" : "Warm intro follow-up",
197
- actor: "Ramzi",
198
- })),
170
+ {
171
+ id: "w1",
172
+ kind: "status",
173
+ occurredAt: "2026-10-18T09:30:00Z",
174
+ title: "Sent SOW v2, legal is reviewing",
175
+ body: "The payment terms have 2 redlines. Sofia answers Monday and asks for the signature by the 26th, so the kickoff holds.",
176
+ actor: "Sofia",
177
+ },
178
+ {
179
+ id: "w2",
180
+ kind: "call",
181
+ occurredAt: "2026-10-21T08:15:00Z",
182
+ title: "Scoping review",
183
+ body: "45 minutes with Jane and the clinic manager. Booking stays out of scope. They want the new site to carry the clinic pages and nothing else.",
184
+ details: [
185
+ { label: "Length", value: "45 min" },
186
+ { label: "With", value: "Jane Roe, Tom Bell" },
187
+ ],
188
+ href: "#call-scoping",
189
+ },
190
+ {
191
+ id: "w3",
192
+ kind: "stage",
193
+ occurredAt: "2026-10-12T14:00:00Z",
194
+ title: "Stage set to Proposal sent",
195
+ actor: "Sofia",
196
+ },
197
+ note(
198
+ "w4",
199
+ "2026-10-09T16:40:00Z",
200
+ "Their fear is another agency that vanishes",
201
+ "Said it twice. Lead the proposal with who answers the phone.",
202
+ ),
203
+ {
204
+ id: "w5",
205
+ kind: "invoice.sent",
206
+ occurredAt: "2026-10-06T10:00:00Z",
207
+ title: "Deposit invoice",
208
+ amount: 3600,
209
+ currency: "EUR",
210
+ },
211
+ {
212
+ id: "w6",
213
+ kind: "call",
214
+ occurredAt: "2026-10-03T13:00:00Z",
215
+ title: "Discovery call",
216
+ body: "Jane runs 3 clinics and the site is 4 years old.",
217
+ },
218
+ {
219
+ id: "w7",
220
+ kind: "milestone.done",
221
+ occurredAt: "2026-10-03T15:00:00Z",
222
+ title: "Discovery call done",
223
+ },
224
+ ...Array.from({ length: 34 }, (_, i): RecordEvent => ({
225
+ id: `old${i}`,
226
+ kind: i % 3 === 0 ? "email" : "note",
227
+ occurredAt: new Date(Date.UTC(2026, 8, 28 - i, 9, 0)).toISOString(),
228
+ title: i % 3 === 0 ? "Re: clinic pages" : "Warm intro follow-up",
229
+ actor: "Ramzi",
230
+ })),
199
231
  ];
200
232
 
201
233
  export const website: RecordSummary = {
202
- id: "p-web",
203
- name: "Website rebuild",
204
- href: "#project-web",
205
- stageKey: "sent",
206
- value: 12000,
207
- currency: "EUR",
208
- startDate: "2026-11-01",
209
- endDate: "2026-12-15",
210
- whereWeAre: {
211
- text: "Sent SOW v2, legal is reviewing. The payment terms have 2 redlines, Sofia answers Monday and asks for the signature by the 26th, so the kickoff holds.",
212
- at: "2026-10-18T09:30:00Z",
213
- actor: "Sofia",
214
- health: { label: "Waiting", tone: "warning" },
215
- },
216
- next: { label: "Kickoff call", date: "2026-10-26", owner: "Ramzi" },
217
- lastCallAt: "2026-10-21T08:15:00Z",
218
- lastTouchAt: "2026-10-21T08:15:00Z",
219
- progress: { done: 1, total: 6 },
234
+ id: "p-web",
235
+ name: "Website rebuild",
236
+ href: "#project-web",
237
+ stageKey: "sent",
238
+ value: 12000,
239
+ currency: "EUR",
240
+ startDate: "2026-11-01",
241
+ endDate: "2026-12-15",
242
+ whereWeAre: {
243
+ text: "Sent SOW v2, legal is reviewing. The payment terms have 2 redlines, Sofia answers Monday and asks for the signature by the 26th, so the kickoff holds.",
244
+ at: "2026-10-18T09:30:00Z",
245
+ actor: "Sofia",
246
+ health: { label: "Waiting", tone: "warning" },
247
+ },
248
+ next: { label: "Kickoff call", date: "2026-10-26", owner: "Ramzi" },
249
+ lastCallAt: "2026-10-21T08:15:00Z",
250
+ lastTouchAt: "2026-10-21T08:15:00Z",
251
+ progress: { done: 1, total: 6 },
220
252
  };
221
253
 
222
254
  export const audit: RecordSummary = {
223
- id: "p-audit",
224
- name: "Brand audit",
225
- href: "#project-audit",
226
- stageKey: "lost",
227
- stageDetail: "Price",
228
- value: 4500,
229
- currency: "EUR",
230
- whereWeAre: { text: "They went with a freelancer at half the fee.", at: "2026-06-11T10:00:00Z", actor: "Sofia" },
231
- lastCallAt: "2026-06-04T10:00:00Z",
232
- lastTouchAt: "2026-06-11T10:00:00Z",
233
- progress: { done: 1, total: 3 },
255
+ id: "p-audit",
256
+ name: "Brand audit",
257
+ href: "#project-audit",
258
+ stageKey: "lost",
259
+ stageDetail: "Price",
260
+ value: 4500,
261
+ currency: "EUR",
262
+ whereWeAre: {
263
+ text: "They went with a freelancer at half the fee.",
264
+ at: "2026-06-11T10:00:00Z",
265
+ actor: "Sofia",
266
+ },
267
+ lastCallAt: "2026-06-04T10:00:00Z",
268
+ lastTouchAt: "2026-06-11T10:00:00Z",
269
+ progress: { done: 1, total: 3 },
234
270
  };
235
271
 
236
272
  export const portal: RecordSummary = {
237
- id: "p-portal",
238
- name: "Booking portal",
239
- href: "#project-portal",
240
- stageKey: "paid",
241
- value: 18000,
242
- paid: 18000,
243
- currency: "EUR",
244
- whereWeAre: { text: "Live since March. Final invoice paid.", at: "2026-04-02T10:00:00Z", actor: "Ramzi" },
245
- lastCallAt: "2026-03-20T10:00:00Z",
246
- lastTouchAt: "2026-04-02T10:00:00Z",
247
- progress: { done: 5, total: 5 },
273
+ id: "p-portal",
274
+ name: "Booking portal",
275
+ href: "#project-portal",
276
+ stageKey: "paid",
277
+ value: 18000,
278
+ paid: 18000,
279
+ currency: "EUR",
280
+ whereWeAre: {
281
+ text: "Live since March. Final invoice paid.",
282
+ at: "2026-04-02T10:00:00Z",
283
+ actor: "Ramzi",
284
+ },
285
+ lastCallAt: "2026-03-20T10:00:00Z",
286
+ lastTouchAt: "2026-04-02T10:00:00Z",
287
+ progress: { done: 5, total: 5 },
248
288
  };
249
289
 
250
290
  export const auditEvents: RecordEvent[] = [
251
- {
252
- id: "a1",
253
- kind: "stage",
254
- occurredAt: "2026-06-11T10:00:00Z",
255
- title: "Stage set to Lost",
256
- actor: "Sofia",
257
- details: [
258
- { label: "Reason", value: "Price" },
259
- { label: "In their words", value: "A freelancer quoted half." },
260
- ],
261
- },
262
- { id: "a2", kind: "call", occurredAt: "2026-06-04T10:00:00Z", title: "Proposal walk-through", body: "Jane liked the audit and balked at the fee." },
291
+ {
292
+ id: "a1",
293
+ kind: "stage",
294
+ occurredAt: "2026-06-11T10:00:00Z",
295
+ title: "Stage set to Lost",
296
+ actor: "Sofia",
297
+ details: [
298
+ { label: "Reason", value: "Price" },
299
+ { label: "In their words", value: "A freelancer quoted half." },
300
+ ],
301
+ },
302
+ {
303
+ id: "a2",
304
+ kind: "call",
305
+ occurredAt: "2026-06-04T10:00:00Z",
306
+ title: "Proposal walk-through",
307
+ body: "Jane liked the audit and balked at the fee.",
308
+ },
263
309
  ];
264
310
 
265
311
  export const portalEvents: RecordEvent[] = [
266
- { id: "b1", kind: "payment", occurredAt: "2026-04-02T10:00:00Z", title: "Final invoice paid", amount: 9000, currency: "EUR" },
267
- { id: "b2", kind: "payment", occurredAt: "2026-02-10T10:00:00Z", title: "First instalment paid", amount: 9000, currency: "EUR" },
268
- { id: "b3", kind: "call", occurredAt: "2026-03-20T10:00:00Z", title: "Launch check", body: "Live. A few copy fixes after." },
312
+ {
313
+ id: "b1",
314
+ kind: "payment",
315
+ occurredAt: "2026-04-02T10:00:00Z",
316
+ title: "Final invoice paid",
317
+ amount: 9000,
318
+ currency: "EUR",
319
+ },
320
+ {
321
+ id: "b2",
322
+ kind: "payment",
323
+ occurredAt: "2026-02-10T10:00:00Z",
324
+ title: "First instalment paid",
325
+ amount: 9000,
326
+ currency: "EUR",
327
+ },
328
+ {
329
+ id: "b3",
330
+ kind: "call",
331
+ occurredAt: "2026-03-20T10:00:00Z",
332
+ title: "Launch check",
333
+ body: "Live. A few copy fixes after.",
334
+ },
269
335
  ];
270
336
 
271
337
  /** Something that happened before any project existed: it belongs to the client. */
272
338
  export const clientOnlyEvents: RecordEvent[] = [
273
- {
274
- id: "c1",
275
- kind: "form",
276
- occurredAt: "2026-02-01T09:00:00Z",
277
- title: "Contact form: asked about online booking",
278
- body: "Calls pile up at lunch and the front desk can't keep up.",
279
- },
339
+ {
340
+ id: "c1",
341
+ kind: "form",
342
+ occurredAt: "2026-02-01T09:00:00Z",
343
+ title: "Contact form: asked about online booking",
344
+ body: "Calls pile up at lunch and the front desk can't keep up.",
345
+ },
280
346
  ];
281
347
 
282
348
  export const clientProjects = [
283
- { summary: website, events: websiteEvents },
284
- { summary: audit, events: auditEvents },
285
- { summary: portal, events: portalEvents },
349
+ { summary: website, events: websiteEvents },
350
+ { summary: audit, events: auditEvents },
351
+ { summary: portal, events: portalEvents },
286
352
  ];
@@ -1,40 +1,44 @@
1
1
  <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import { cn } from "../lib/utils.js";
6
- import { fieldRow, groupFields, type FieldValues } from "./fields.js";
7
- import InfoList from "./info-list.svelte";
8
- import type {
9
- FieldDef,
10
- FieldSectionDef,
11
- FieldValue,
12
- RecordLabels,
13
- } from "./types.js";
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import { cn } from "../lib/utils.js";
6
+ import { fieldRow, groupFields, type FieldValues } from "./fields.js";
7
+ import InfoList from "./info-list.svelte";
8
+ import type { FieldDef, FieldSectionDef, FieldValue, RecordLabels } from "./types.js";
14
9
 
15
- // =========================
16
- // PROPS
17
- // =========================
18
- interface Props {
19
- /** What a module declares for this record. */
20
- fields: FieldDef[];
21
- /** Section headings in the order to draw them. A section with no field isn't drawn. */
22
- sections?: FieldSectionDef[];
23
- values: FieldValues;
24
- /** Without it every field is read-only and an empty one is left out. */
25
- onSave?: (key: string, value: FieldValue) => void | Promise<void>;
26
- /** For a money field that names none. */
27
- currency?: string;
28
- labels?: Partial<RecordLabels>;
29
- locale?: string;
30
- class?: string;
31
- }
32
- let { fields, sections = [], values, onSave, currency, labels, locale, class: className }: Props = $props();
10
+ // =========================
11
+ // PROPS
12
+ // =========================
13
+ interface Props {
14
+ /** What a module declares for this record. */
15
+ fields: FieldDef[];
16
+ /** Section headings in the order to draw them. A section with no field isn't drawn. */
17
+ sections?: FieldSectionDef[];
18
+ values: FieldValues;
19
+ /** Without it every field is read-only and an empty one is left out. */
20
+ onSave?: (key: string, value: FieldValue) => void | Promise<void>;
21
+ /** For a money field that names none. */
22
+ currency?: string;
23
+ labels?: Partial<RecordLabels>;
24
+ locale?: string;
25
+ class?: string;
26
+ }
27
+ let {
28
+ fields,
29
+ sections = [],
30
+ values,
31
+ onSave,
32
+ currency,
33
+ labels,
34
+ locale,
35
+ class: className,
36
+ }: Props = $props();
33
37
 
34
- // =========================
35
- // DERIVED
36
- // =========================
37
- let groups = $derived(groupFields(fields, sections, values));
38
+ // =========================
39
+ // DERIVED
40
+ // =========================
41
+ let groups = $derived(groupFields(fields, sections, values));
38
42
  </script>
39
43
 
40
44
  <!-- =========================== -->