@heyramzi/ui 1.54.2 → 2.0.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 (361) hide show
  1. package/COMPONENTS.md +3 -2
  2. package/README.md +34 -9
  3. package/package.json +198 -6
  4. package/src/ai/generation-progress/GenerationProgress.svelte +12 -12
  5. package/src/ambient.d.ts +8 -0
  6. package/src/brand/brand.test.ts +67 -0
  7. package/src/brand/index.ts +137 -0
  8. package/src/canvas/README.md +236 -0
  9. package/src/canvas/card/card-frame.svelte +61 -0
  10. package/src/canvas/card/card.css +39 -0
  11. package/src/canvas/card/resize-handles.svelte +119 -0
  12. package/src/canvas/card/resize.ts +33 -0
  13. package/src/canvas/chrome/chrome-zoom.svelte +120 -0
  14. package/src/canvas/chrome/chrome.css +231 -0
  15. package/src/canvas/chrome/lock-badge.svelte +18 -0
  16. package/src/canvas/chrome/side-handles.svelte +30 -0
  17. package/src/canvas/chrome/size-chip.svelte +45 -0
  18. package/src/canvas/chrome/snap-guides.svelte +69 -0
  19. package/src/canvas/components/icons/ToolIcon.svelte +30 -0
  20. package/src/canvas/components/shared/svg/PreviewSvg.svelte +67 -0
  21. package/src/canvas/components/shared/svg/ShapeOutline.svelte +76 -0
  22. package/src/canvas/components/shared/svg/StrokeNode.svelte +69 -0
  23. package/src/canvas/components/shared/svg/TextRule.svelte +37 -0
  24. package/src/canvas/edges/bend-handle.svelte +76 -0
  25. package/src/canvas/edges/edge-gradient.svelte +30 -0
  26. package/src/canvas/edges/edge-path.test.ts +58 -0
  27. package/src/canvas/edges/edge-path.ts +293 -0
  28. package/src/canvas/edges/gradient-edge.svelte +43 -0
  29. package/src/canvas/editor/context-menu.test.ts +65 -0
  30. package/src/canvas/editor/context-menu.ts +48 -0
  31. package/src/canvas/editor/dispatch.svelte.test.ts +243 -0
  32. package/src/canvas/editor/dispatch.svelte.ts +205 -0
  33. package/src/canvas/editor/editor-context.ts +62 -0
  34. package/src/canvas/editor/editor-mode.ts +67 -0
  35. package/src/canvas/editor/frame.ts +98 -0
  36. package/src/canvas/editor/hand-context.ts +15 -0
  37. package/src/canvas/editor/history-core.test.ts +239 -0
  38. package/src/canvas/editor/history-core.ts +247 -0
  39. package/src/canvas/editor/keymap.test.ts +161 -0
  40. package/src/canvas/editor/keymap.ts +151 -0
  41. package/src/canvas/editor/layout-actions.test.ts +156 -0
  42. package/src/canvas/editor/layout-actions.ts +219 -0
  43. package/src/canvas/editor/lock.test.ts +68 -0
  44. package/src/canvas/editor/lock.ts +87 -0
  45. package/src/canvas/editor/rebase.test.ts +208 -0
  46. package/src/canvas/editor/rebase.ts +272 -0
  47. package/src/canvas/editor/style.ts +184 -0
  48. package/src/canvas/editor/text-field-context.ts +41 -0
  49. package/src/canvas/editor/z-order.test.ts +75 -0
  50. package/src/canvas/editor/z-order.ts +87 -0
  51. package/src/canvas/headless/alignment.test.ts +59 -0
  52. package/src/canvas/headless/alignment.ts +23 -0
  53. package/src/canvas/headless/break-apart-find.ts +418 -0
  54. package/src/canvas/headless/break-apart-pieces.ts +104 -0
  55. package/src/canvas/headless/break-apart-plan.ts +439 -0
  56. package/src/canvas/headless/break-apart-style.ts +106 -0
  57. package/src/canvas/headless/break-apart.ts +244 -0
  58. package/src/canvas/headless/canvas-cleanup.test.ts +181 -0
  59. package/src/canvas/headless/canvas-cleanup.ts +102 -0
  60. package/src/canvas/headless/edge-anchors.test.ts +70 -0
  61. package/src/canvas/headless/edge-anchors.ts +143 -0
  62. package/src/canvas/headless/edge-memo.test.ts +58 -0
  63. package/src/canvas/headless/edge-memo.ts +32 -0
  64. package/src/canvas/headless/edge-serialization.test.ts +55 -0
  65. package/src/canvas/headless/edge-serialization.ts +32 -0
  66. package/src/canvas/headless/figure-geometry.ts +293 -0
  67. package/src/canvas/headless/fit-viewport.test.ts +85 -0
  68. package/src/canvas/headless/fit-viewport.ts +137 -0
  69. package/src/canvas/headless/grid.test.ts +34 -0
  70. package/src/canvas/headless/grid.ts +94 -0
  71. package/src/canvas/headless/icon-node.test.ts +23 -0
  72. package/src/canvas/headless/icon-node.ts +66 -0
  73. package/src/canvas/headless/index.ts +9 -0
  74. package/src/canvas/headless/linkify.test.ts +23 -0
  75. package/src/canvas/headless/linkify.ts +25 -0
  76. package/src/canvas/headless/pen-marks.ts +133 -0
  77. package/src/canvas/headless/sketch.test.ts +99 -0
  78. package/src/canvas/headless/sketch.ts +302 -0
  79. package/src/canvas/headless/snap-guides.test.ts +61 -0
  80. package/src/canvas/headless/snap-guides.ts +146 -0
  81. package/src/canvas/headless/svg-figure.test.ts +49 -0
  82. package/src/canvas/headless/svg-figure.ts +176 -0
  83. package/src/canvas/headless/table-layout.test.ts +158 -0
  84. package/src/canvas/headless/table-layout.ts +309 -0
  85. package/src/canvas/index.ts +35 -0
  86. package/src/canvas/lod/lod-mode.svelte.ts +30 -0
  87. package/src/canvas/lod/lod-switch.svelte +140 -0
  88. package/src/canvas/lod/render-all.svelte.ts +52 -0
  89. package/src/canvas/ops/arrange.ts +108 -0
  90. package/src/canvas/ops/ops.test.ts +98 -0
  91. package/src/canvas/ops/ops.ts +347 -0
  92. package/src/canvas/pen/PenField.svelte +128 -0
  93. package/src/canvas/pen/PenField.test.ts +87 -0
  94. package/src/canvas/pen/PenOverlay.svelte +172 -0
  95. package/src/canvas/pen/ShapeNode.svelte +64 -0
  96. package/src/canvas/pen/TextNode.svelte +97 -0
  97. package/src/canvas/pen/colors.ts +28 -0
  98. package/src/canvas/pen/eraser.ts +46 -0
  99. package/src/canvas/pen/geometry.ts +148 -0
  100. package/src/canvas/pen/host-field.test.svelte +13 -0
  101. package/src/canvas/pen/index.ts +40 -0
  102. package/src/canvas/pen/ops.ts +16 -0
  103. package/src/canvas/pen/pen-session.ts +177 -0
  104. package/src/canvas/pen/pen.css +149 -0
  105. package/src/canvas/pen/plugin.ts +139 -0
  106. package/src/canvas/pen/shapes.ts +104 -0
  107. package/src/canvas/pen/stroke-path.ts +54 -0
  108. package/src/canvas/pen/types.ts +96 -0
  109. package/src/canvas/pen/ui.ts +26 -0
  110. package/src/canvas/plugin.test.ts +45 -0
  111. package/src/canvas/plugin.ts +159 -0
  112. package/src/canvas/screen/GlyphIcon.svelte +25 -0
  113. package/src/canvas/screen/PartView.svelte +176 -0
  114. package/src/canvas/screen/ScreenBody.svelte +142 -0
  115. package/src/canvas/screen/ScreenCard.svelte +32 -0
  116. package/src/canvas/screen/glyphs/button.svg +1 -0
  117. package/src/canvas/screen/glyphs/close.svg +1 -0
  118. package/src/canvas/screen/glyphs/down.svg +1 -0
  119. package/src/canvas/screen/glyphs/header.svg +1 -0
  120. package/src/canvas/screen/glyphs/hills.svg +1 -0
  121. package/src/canvas/screen/glyphs/image.svg +1 -0
  122. package/src/canvas/screen/glyphs/input.svg +1 -0
  123. package/src/canvas/screen/glyphs/list.svg +1 -0
  124. package/src/canvas/screen/glyphs/modal.svg +1 -0
  125. package/src/canvas/screen/glyphs/plus.svg +1 -0
  126. package/src/canvas/screen/glyphs/up.svg +1 -0
  127. package/src/canvas/screen/glyphs.ts +40 -0
  128. package/src/canvas/screen/labels.ts +41 -0
  129. package/src/canvas/screen/parts.ts +164 -0
  130. package/src/canvas/screen/plugin.ts +67 -0
  131. package/src/canvas/screen/screen.css +593 -0
  132. package/src/canvas/screen/types.ts +47 -0
  133. package/src/canvas/screen/ui.ts +13 -0
  134. package/src/canvas/test-utils.ts +18 -0
  135. package/src/canvas/types.ts +53 -0
  136. package/src/canvas/ui/CanvasToolbar.svelte +169 -0
  137. package/src/canvas/ui/FrameLayer.svelte +116 -0
  138. package/src/canvas/ui/PenLayer.svelte +39 -0
  139. package/src/canvas/ui/StylePanel.svelte +268 -0
  140. package/src/canvas/ui/canvas-grid.svelte +156 -0
  141. package/src/canvas/ui/canvas-ui.css +350 -0
  142. package/src/canvas/ui/forward-wheel.ts +29 -0
  143. package/src/canvas/ui/icons.ts +124 -0
  144. package/src/canvas/ui/place-panel.ts +80 -0
  145. package/src/canvas/ui/shortcut-tip.test.ts +16 -0
  146. package/src/canvas/ui/shortcut-tip.ts +17 -0
  147. package/src/canvas/ui/tools.ts +146 -0
  148. package/src/charts/README.md +104 -0
  149. package/src/charts/ambient.d.ts +4 -0
  150. package/src/charts/animated-value.css +7 -0
  151. package/src/charts/animated-value.svelte +110 -0
  152. package/src/charts/area-chart.css +29 -0
  153. package/src/charts/area-chart.svelte +204 -0
  154. package/src/charts/bar-chart.css +119 -0
  155. package/src/charts/bar-chart.svelte +275 -0
  156. package/src/charts/charts.css +201 -0
  157. package/src/charts/crosshair.svelte +35 -0
  158. package/src/charts/dashboard/chart.ts +5 -0
  159. package/src/charts/dashboard/dashboard-empty.svelte +55 -0
  160. package/src/charts/dashboard/dashboard-section.svelte +58 -0
  161. package/src/charts/dashboard/data-panel.svelte +81 -0
  162. package/src/charts/dashboard/data-sort-head.svelte +102 -0
  163. package/src/charts/dashboard/data-table.svelte +278 -0
  164. package/src/charts/dashboard/data-table.ts +26 -0
  165. package/src/charts/dashboard/index.ts +25 -0
  166. package/src/charts/dashboard/panel-grid.svelte +43 -0
  167. package/src/charts/dashboard/period-filter.svelte +53 -0
  168. package/src/charts/dashboard/rating-chip.svelte +35 -0
  169. package/src/charts/dashboard/stat-band.svelte +47 -0
  170. package/src/charts/dashboard/stat-tile.svelte +176 -0
  171. package/src/charts/dashboard/windows.ts +8 -0
  172. package/src/charts/donut-chart.css +56 -0
  173. package/src/charts/donut-chart.svelte +158 -0
  174. package/src/charts/edge-fade.svelte +43 -0
  175. package/src/charts/edge-fade.ts +16 -0
  176. package/src/charts/figure.css +100 -0
  177. package/src/charts/figure.svelte +122 -0
  178. package/src/charts/gauge-chart.css +94 -0
  179. package/src/charts/gauge-chart.svelte +302 -0
  180. package/src/charts/hover-band.test.ts +30 -0
  181. package/src/charts/hover-band.ts +21 -0
  182. package/src/charts/hover-focus.svelte +85 -0
  183. package/src/charts/index.ts +31 -0
  184. package/src/charts/legend.css +136 -0
  185. package/src/charts/legend.svelte +112 -0
  186. package/src/charts/line-chart.css +26 -0
  187. package/src/charts/line-chart.svelte +256 -0
  188. package/src/charts/motion.ts +19 -0
  189. package/src/charts/number-chart.css +179 -0
  190. package/src/charts/number-chart.svelte +187 -0
  191. package/src/charts/primitives/chart-container.svelte +57 -0
  192. package/src/charts/primitives/chart-style.svelte +36 -0
  193. package/src/charts/primitives/chart-tooltip.svelte +161 -0
  194. package/src/charts/primitives/chart-utils.ts +54 -0
  195. package/src/charts/primitives/index.ts +11 -0
  196. package/src/charts/reference-line.css +18 -0
  197. package/src/charts/reference-line.svelte +31 -0
  198. package/src/charts/reveal.svelte +61 -0
  199. package/src/charts/ring-chart.css +104 -0
  200. package/src/charts/ring-chart.svelte +166 -0
  201. package/src/charts/sparkline.css +45 -0
  202. package/src/charts/sparkline.svelte +141 -0
  203. package/src/charts/stat-card.css +105 -0
  204. package/src/charts/stat-card.svelte +267 -0
  205. package/src/charts/time-series-chart.css +28 -0
  206. package/src/charts/time-series-chart.svelte +179 -0
  207. package/src/charts/trend-pill.css +38 -0
  208. package/src/charts/trend-pill.svelte +48 -0
  209. package/src/components/copy/copy-button.svelte +1 -1
  210. package/src/components/docs/doc-page.svelte +1 -1
  211. package/src/components/docs/on-this-page.svelte +2 -2
  212. package/src/components/list-page/empty-state.svelte +1 -1
  213. package/src/components/list-page/filter-popover.svelte +1 -1
  214. package/src/components/list-page/section-label.svelte +1 -1
  215. package/src/components/segmented/segment.svelte +4 -4
  216. package/src/components/theme/theme-provider.svelte +6 -4
  217. package/src/components/toolbar/index.ts +5 -0
  218. package/src/components/toolbar/toolbar.svelte +168 -0
  219. package/src/components/toolbar/toolbar.test.ts +66 -0
  220. package/src/editor/README.md +58 -0
  221. package/src/editor/ambient.d.ts +1 -0
  222. package/src/editor/block-id.ts +77 -0
  223. package/src/editor/block-ops.test.ts +237 -0
  224. package/src/editor/block-ops.ts +157 -0
  225. package/src/editor/content.css +418 -0
  226. package/src/editor/editor-toolbar.svelte +303 -0
  227. package/src/editor/editor.svelte +462 -0
  228. package/src/editor/extensions.ts +145 -0
  229. package/src/editor/image-toolbar.svelte +123 -0
  230. package/src/editor/index.ts +17 -0
  231. package/src/editor/inline-editor.svelte +261 -0
  232. package/src/editor/markdown-escaping.test.ts +158 -0
  233. package/src/editor/markdown-marks.test.ts +89 -0
  234. package/src/editor/markdown-test-helpers.ts +13 -0
  235. package/src/editor/markdown-youtube.test.ts +93 -0
  236. package/src/editor/markdown.test.ts +250 -0
  237. package/src/editor/markdown.ts +511 -0
  238. package/src/editor/renderer.svelte +92 -0
  239. package/src/editor/serializer.test.ts +133 -0
  240. package/src/editor/serializer.ts +370 -0
  241. package/src/editor/slash-items.ts +115 -0
  242. package/src/editor/slash-menu.svelte +159 -0
  243. package/src/editor/youtube-url.ts +35 -0
  244. package/src/effects/interactive-hover-button/InteractiveHoverButton.svelte +13 -13
  245. package/src/lib/result.ts +19 -0
  246. package/src/onboarding/README.md +120 -0
  247. package/src/onboarding/define-tours.test.ts +50 -0
  248. package/src/onboarding/define-tours.ts +31 -0
  249. package/src/onboarding/demo.svelte +86 -0
  250. package/src/onboarding/index.ts +12 -0
  251. package/src/onboarding/onboarding-provider.svelte +120 -0
  252. package/src/onboarding/onboarding.svelte.ts +24 -0
  253. package/src/onboarding/pick.test.ts +79 -0
  254. package/src/onboarding/pick.ts +36 -0
  255. package/src/onboarding/stores.test.ts +103 -0
  256. package/src/onboarding/stores.ts +58 -0
  257. package/src/onboarding/tour-card.svelte +70 -0
  258. package/src/onboarding/tour-runner.svelte +197 -0
  259. package/src/onboarding/types.ts +34 -0
  260. package/src/onboarding/wait.ts +29 -0
  261. package/src/shadcn/command/command-input.svelte +1 -1
  262. package/src/slides/Controls.svelte +138 -0
  263. package/src/slides/Deck.svelte +271 -0
  264. package/src/slides/Fragment.svelte +71 -0
  265. package/src/slides/Overview.svelte +113 -0
  266. package/src/slides/Presenter.svelte +292 -0
  267. package/src/slides/README.md +89 -0
  268. package/src/slides/Slide.svelte +179 -0
  269. package/src/slides/SlideList.svelte +19 -0
  270. package/src/slides/ambient.d.ts +1 -0
  271. package/src/slides/bridges.ts +69 -0
  272. package/src/slides/deck-controller.svelte.ts +292 -0
  273. package/src/slides/demo-slides.svelte +358 -0
  274. package/src/slides/demo.svelte +176 -0
  275. package/src/slides/export/capture.ts +113 -0
  276. package/src/slides/export/ffmpeg.ts +120 -0
  277. package/src/slides/export/index.ts +9 -0
  278. package/src/slides/export/launch.ts +10 -0
  279. package/src/slides/export/pdf.ts +152 -0
  280. package/src/slides/export/types.ts +48 -0
  281. package/src/slides/index.ts +19 -0
  282. package/src/slides/labels.ts +48 -0
  283. package/src/slides/layout.ts +51 -0
  284. package/src/slides/model.ts +47 -0
  285. package/src/slides/navigation.ts +100 -0
  286. package/src/slides/theme.css +205 -0
  287. package/src/slides/types.ts +86 -0
  288. package/src/tasks/README.md +312 -0
  289. package/src/tasks/ambient.d.ts +3 -0
  290. package/src/tasks/board-order.ts +137 -0
  291. package/src/tasks/dates.ts +31 -0
  292. package/src/tasks/define-statuses.ts +32 -0
  293. package/src/tasks/demo.svelte +389 -0
  294. package/src/tasks/detail/detail-demo.svelte +186 -0
  295. package/src/tasks/detail/detail-types.ts +54 -0
  296. package/src/tasks/detail/task-chip.svelte +28 -0
  297. package/src/tasks/detail/task-detail.svelte +315 -0
  298. package/src/tasks/detail/task-property.svelte +17 -0
  299. package/src/tasks/detail/task-thread.svelte +258 -0
  300. package/src/tasks/gantt/gantt-bar.svelte +243 -0
  301. package/src/tasks/gantt/gantt-grid.svelte +94 -0
  302. package/src/tasks/gantt/gantt-group-row.svelte +55 -0
  303. package/src/tasks/gantt/gantt-header.svelte +71 -0
  304. package/src/tasks/gantt/gantt-task-row.svelte +186 -0
  305. package/src/tasks/gantt/gantt.css +22 -0
  306. package/src/tasks/gantt/gantt.test.ts +77 -0
  307. package/src/tasks/gantt/logic.test.ts +163 -0
  308. package/src/tasks/gantt/logic.ts +248 -0
  309. package/src/tasks/inbox/inbox-card.svelte +283 -0
  310. package/src/tasks/inbox/inbox-draft.svelte +84 -0
  311. package/src/tasks/inbox/inbox-logic.ts +55 -0
  312. package/src/tasks/inbox/inbox-statuses.ts +48 -0
  313. package/src/tasks/inbox/inbox-trace.svelte +45 -0
  314. package/src/tasks/inbox/inbox-types.ts +108 -0
  315. package/src/tasks/inbox/inbox.svelte +203 -0
  316. package/src/tasks/inbox/index.ts +23 -0
  317. package/src/tasks/index.ts +85 -0
  318. package/src/tasks/kanban-board.svelte +561 -0
  319. package/src/tasks/kanban-column.svelte +117 -0
  320. package/src/tasks/kanban-drag.ts +38 -0
  321. package/src/tasks/kanban-types.ts +65 -0
  322. package/src/tasks/presets/sales.ts +16 -0
  323. package/src/tasks/record/demo-data.ts +286 -0
  324. package/src/tasks/record/field-section.svelte +53 -0
  325. package/src/tasks/record/fields.ts +142 -0
  326. package/src/tasks/record/format.ts +87 -0
  327. package/src/tasks/record/gutter.ts +2 -0
  328. package/src/tasks/record/index.ts +54 -0
  329. package/src/tasks/record/info-list.svelte +316 -0
  330. package/src/tasks/record/kinds.ts +42 -0
  331. package/src/tasks/record/logic.ts +324 -0
  332. package/src/tasks/record/next-up.svelte +70 -0
  333. package/src/tasks/record/project-list.svelte +133 -0
  334. package/src/tasks/record/record-demo.svelte +324 -0
  335. package/src/tasks/record/record-header.svelte +70 -0
  336. package/src/tasks/record/record-shell.svelte +138 -0
  337. package/src/tasks/record/record-stats.svelte +48 -0
  338. package/src/tasks/record/record-timeline.svelte +231 -0
  339. package/src/tasks/record/record.test.ts +350 -0
  340. package/src/tasks/record/steps-rail.svelte +474 -0
  341. package/src/tasks/record/types.ts +280 -0
  342. package/src/tasks/record/where-we-are.svelte +121 -0
  343. package/src/tasks/reorder.ts +19 -0
  344. package/src/tasks/side-panel-types.ts +16 -0
  345. package/src/tasks/side-panel.svelte +193 -0
  346. package/src/tasks/status-badge.svelte +63 -0
  347. package/src/tasks/status-dot.svelte +228 -0
  348. package/src/tasks/status-icon.svelte +30 -0
  349. package/src/tasks/status-types.ts +40 -0
  350. package/src/tasks/task-gantt.svelte +230 -0
  351. package/src/tasks/task-list.svelte +470 -0
  352. package/src/tasks/task-types.ts +33 -0
  353. package/src/tasks/time/duration.ts +69 -0
  354. package/src/tasks/time/time-chip.svelte +62 -0
  355. package/src/tasks/time/time-dot.svelte +58 -0
  356. package/src/tasks/time/time-ring.svelte +74 -0
  357. package/src/tasks/time/time-tracker.svelte +565 -0
  358. package/src/tasks/time/time-types.ts +74 -0
  359. package/src/tasks/use-kanban-dnd.svelte.ts +173 -0
  360. package/src/tokens/base.css +5 -5
  361. package/src/tokens/palette.css +115 -120
@@ -0,0 +1,32 @@
1
+ <script lang="ts">
2
+ // =========================
3
+ // IMPORTS
4
+ // =========================
5
+ import type { NodeProps } from "@xyflow/svelte";
6
+ import ScreenBody from "./ScreenBody.svelte";
7
+ import type { ScreenLabels } from "./labels.js";
8
+ import { SCREEN_WIDTH } from "./types.js";
9
+ import "./screen.css";
10
+
11
+ // The Screen card with its own frame, for a host that has none. A host with a card frame of
12
+ // its own wraps `ScreenBody` in it instead.
13
+
14
+ // =========================
15
+ // TYPES
16
+ // =========================
17
+ interface Props extends NodeProps {
18
+ labels?: Partial<ScreenLabels>;
19
+ }
20
+
21
+ // =========================
22
+ // PROPS
23
+ // =========================
24
+ let { id, data, selected, width, labels }: Props = $props();
25
+ </script>
26
+
27
+ <!-- =========================== -->
28
+ <!-- MARKUP -->
29
+ <!-- =========================== -->
30
+ <div class="cv-frame" class:is-selected={selected} style="width:{width ?? SCREEN_WIDTH}px">
31
+ <ScreenBody {id} {data} {labels} />
32
+ </div>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 20" fill="none" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M8 4h12a6 6 0 0 1 0 12H8A6 6 0 0 1 8 4z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12" fill="none" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3l6 6M9 3L3 9"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12" fill="none" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M2.5 4.5L6 8l3.5-3.5"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 20" fill="none" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 4h22a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 60" fill="black"><path d="M26 44L42 22l10 12 8-8 14 18z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 20" fill="none" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M4 2h20a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V5a3 3 0 0 1 3-3z"/><path d="M1 16l8-7 6 5 5-4 7 6"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 20" fill="none" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4h20a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H4a3 3 0 0 1-3-3V7a3 3 0 0 1 3-3z"/><path d="M5 10h8"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 20" fill="none" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M1 4h26M1 10h26M1 16h18"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 20" fill="none" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 1h18a4 4 0 0 1 4 4v10a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4V5a4 4 0 0 1 4-4z"/><path d="M8 13h12"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12" fill="none" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2v8M2 6h8"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12" fill="none" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M2.5 7.5L6 4l3.5 3.5"/></svg>
@@ -0,0 +1,40 @@
1
+ // The card's small drawings, as files a CSS mask paints in the current colour.
2
+
3
+ import header from "./glyphs/header.svg";
4
+ import button from "./glyphs/button.svg";
5
+ import input from "./glyphs/input.svg";
6
+ import image from "./glyphs/image.svg";
7
+ import list from "./glyphs/list.svg";
8
+ import modal from "./glyphs/modal.svg";
9
+ import up from "./glyphs/up.svg";
10
+ import down from "./glyphs/down.svg";
11
+ import close from "./glyphs/close.svg";
12
+ import plus from "./glyphs/plus.svg";
13
+ import hills from "./glyphs/hills.svg";
14
+
15
+ export type GlyphName =
16
+ | "header"
17
+ | "button"
18
+ | "input"
19
+ | "image"
20
+ | "list"
21
+ | "modal"
22
+ | "up"
23
+ | "down"
24
+ | "close"
25
+ | "plus"
26
+ | "hills";
27
+
28
+ export const GLYPHS: Record<GlyphName, string> = {
29
+ header,
30
+ button,
31
+ input,
32
+ image,
33
+ list,
34
+ modal,
35
+ up,
36
+ down,
37
+ close,
38
+ plus,
39
+ hills,
40
+ };
@@ -0,0 +1,41 @@
1
+ // The card's own words, each one a label. The package ships English; a host passes its own
2
+ // translations to the card and keeps this module out of its bundle's way.
3
+
4
+ import type { PartKind, ScreenMode } from "./types.js";
5
+
6
+ export type ScreenLabels = {
7
+ name: string;
8
+ mode: string;
9
+ modes: Record<ScreenMode, string>;
10
+ kinds: Record<PartKind, string>;
11
+ addPart: string;
12
+ moveUp: string;
13
+ moveDown: string;
14
+ remove: string;
15
+ primary: string;
16
+ secondary: string;
17
+ addItem: string;
18
+ removeItem: string;
19
+ };
20
+
21
+ export const DEFAULT_SCREEN_LABELS: ScreenLabels = {
22
+ name: "Screen",
23
+ mode: "Mode",
24
+ modes: { wireframe: "Wireframe", mockup: "Mockup" },
25
+ kinds: {
26
+ header: "Header",
27
+ button: "Button",
28
+ input: "Input",
29
+ image: "Image",
30
+ list: "List",
31
+ modal: "Modal",
32
+ },
33
+ addPart: "Add part",
34
+ moveUp: "Move up",
35
+ moveDown: "Move down",
36
+ remove: "Remove",
37
+ primary: "Primary",
38
+ secondary: "Secondary",
39
+ addItem: "Add item",
40
+ removeItem: "Remove item",
41
+ };
@@ -0,0 +1,164 @@
1
+ // Pure edits on a screen's parts, so the card and the CLI op change a screen the same way.
2
+ // Each function returns a new list.
3
+
4
+ import {
5
+ MAX_LABEL,
6
+ MAX_LIST_ITEMS,
7
+ MAX_PARTS,
8
+ PART_DEFAULTS,
9
+ PART_KINDS,
10
+ SCREEN_MODES,
11
+ SCREEN_NODE,
12
+ SCREEN_WIDTH,
13
+ type PartKind,
14
+ type ScreenData,
15
+ type ScreenPart,
16
+ } from "./types.js";
17
+
18
+ const isKind = (value: unknown): value is PartKind =>
19
+ typeof value === "string" && (PART_KINDS as readonly string[]).includes(value);
20
+
21
+ const text = (value: unknown, fallback = ""): string =>
22
+ typeof value === "string" ? value.slice(0, MAX_LABEL) : fallback;
23
+
24
+ /** The next id no part of this screen holds: p1, p2, and so on. */
25
+ export function nextPartId(parts: readonly ScreenPart[]): string {
26
+ const taken = new Set(parts.map((part) => part.id));
27
+ let n = parts.length + 1;
28
+ while (taken.has(`p${n}`)) n += 1;
29
+ return `p${n}`;
30
+ }
31
+
32
+ /** A part of this kind with its starting words. */
33
+ export function createPart(
34
+ kind: PartKind,
35
+ parts: readonly ScreenPart[],
36
+ seed: Partial<Omit<ScreenPart, "id" | "kind">> = {},
37
+ ): ScreenPart {
38
+ const base = PART_DEFAULTS[kind];
39
+ const part: ScreenPart = { ...base, ...seed, id: nextPartId(parts), kind };
40
+ if (part.items) part.items = [...part.items];
41
+ return part;
42
+ }
43
+
44
+ /** A stored id kept when it is a new string, else the next free one. */
45
+ function uniquePartId(raw: unknown, parts: ScreenPart[], seen: Set<string>): string {
46
+ const id = typeof raw === "string" && raw ? raw : "";
47
+ const unique = !id || seen.has(id) ? nextPartId(parts) : id;
48
+ seen.add(unique);
49
+ return unique;
50
+ }
51
+
52
+ /** What only one kind of part carries: a list's rows, a modal's body, a button's weight. */
53
+ function kindFields(kind: PartKind, part: Record<string, unknown>): Partial<ScreenPart> {
54
+ if (kind === "list") {
55
+ const items = Array.isArray(part.items) ? part.items : PART_DEFAULTS.list.items;
56
+ return { items: (items ?? []).slice(0, MAX_LIST_ITEMS).map((item) => text(item)) };
57
+ }
58
+ if (kind === "modal") return { text: text(part.text, PART_DEFAULTS.modal.text) };
59
+ if (kind === "button") return { variant: part.variant === "secondary" ? "secondary" : "primary" };
60
+ return {};
61
+ }
62
+
63
+ /** One stored part as a part that draws, or null when its kind is not one we know. */
64
+ function normalizePart(entry: unknown, parts: ScreenPart[], seen: Set<string>): ScreenPart | null {
65
+ const part = (entry ?? {}) as Record<string, unknown>;
66
+ if (!isKind(part.kind)) return null;
67
+ const kind = part.kind;
68
+ const id = uniquePartId(part.id, parts, seen);
69
+ return {
70
+ id,
71
+ kind,
72
+ label: text(part.label, PART_DEFAULTS[kind].label),
73
+ ...kindFields(kind, part),
74
+ };
75
+ }
76
+
77
+ /** A screen as stored may be old, hand-written or half-typed: this one always draws. */
78
+ export function normalizeScreen(data: unknown): ScreenData {
79
+ const raw = (data ?? {}) as Record<string, unknown>;
80
+ const seen = new Set<string>();
81
+ const parts: ScreenPart[] = [];
82
+ const rawParts = Array.isArray(raw.parts) ? raw.parts : [];
83
+ for (const entry of rawParts.slice(0, MAX_PARTS)) {
84
+ const next = normalizePart(entry, parts, seen);
85
+ if (next) parts.push(next);
86
+ }
87
+ return {
88
+ label: text(raw.label),
89
+ mode: (SCREEN_MODES as readonly unknown[]).includes(raw.mode)
90
+ ? (raw.mode as ScreenData["mode"])
91
+ : "wireframe",
92
+ parts,
93
+ };
94
+ }
95
+
96
+ export function addPart(
97
+ parts: readonly ScreenPart[],
98
+ kind: PartKind,
99
+ seed?: Partial<Omit<ScreenPart, "id" | "kind">>,
100
+ index = parts.length,
101
+ ): ScreenPart[] {
102
+ if (parts.length >= MAX_PARTS) return [...parts];
103
+ const at = Math.max(0, Math.min(index, parts.length));
104
+ return [...parts.slice(0, at), createPart(kind, parts, seed), ...parts.slice(at)];
105
+ }
106
+
107
+ export function removePart(parts: readonly ScreenPart[], id: string): ScreenPart[] {
108
+ return parts.filter((part) => part.id !== id);
109
+ }
110
+
111
+ /** One step up (-1) or down (1). The ends stay put. */
112
+ export function movePart(parts: readonly ScreenPart[], id: string, step: -1 | 1): ScreenPart[] {
113
+ const from = parts.findIndex((part) => part.id === id);
114
+ const to = from + step;
115
+ if (from < 0 || to < 0 || to >= parts.length) return [...parts];
116
+ const next = [...parts];
117
+ [next[from], next[to]] = [next[to], next[from]];
118
+ return next;
119
+ }
120
+
121
+ export function patchPart(
122
+ parts: readonly ScreenPart[],
123
+ id: string,
124
+ patch: Partial<Omit<ScreenPart, "id" | "kind">>,
125
+ ): ScreenPart[] {
126
+ return parts.map((part) => (part.id === id ? { ...part, ...patch } : part));
127
+ }
128
+
129
+ /** A list row's words. Rows are added and cut at the end, never reordered. */
130
+ export function setItem(items: readonly string[], index: number, value: string): string[] {
131
+ return items.map((item, i) => (i === index ? value : item));
132
+ }
133
+
134
+ export function addItem(items: readonly string[]): string[] {
135
+ return items.length >= MAX_LIST_ITEMS ? [...items] : [...items, "Item"];
136
+ }
137
+
138
+ export function removeItem(items: readonly string[], index: number): string[] {
139
+ return items.filter((_, i) => i !== index);
140
+ }
141
+
142
+ /** A ready node for a board, as the CLI and the palette both build it. */
143
+ export function buildScreenNode(options: {
144
+ id: string;
145
+ position: { x: number; y: number };
146
+ label?: string;
147
+ mode?: ScreenData["mode"];
148
+ parts?: ReadonlyArray<{ kind: PartKind } & Partial<Omit<ScreenPart, "id" | "kind">>>;
149
+ width?: number;
150
+ }) {
151
+ let parts: ScreenPart[] = [];
152
+ for (const entry of options.parts ?? []) {
153
+ const { kind, ...seed } = entry;
154
+ parts = addPart(parts, kind, seed);
155
+ }
156
+ const data = normalizeScreen({ label: options.label, mode: options.mode, parts });
157
+ return {
158
+ id: options.id,
159
+ type: SCREEN_NODE,
160
+ position: options.position,
161
+ data,
162
+ style: { width: options.width ?? SCREEN_WIDTH },
163
+ };
164
+ }
@@ -0,0 +1,67 @@
1
+ // The Screen card's kind and its add op, with no DOM, so the CLI can put a screen on a board.
2
+ // ui.ts adds the renderer.
3
+
4
+ import { z } from "zod";
5
+ import { CanvasOpError } from "../ops/ops.js";
6
+ import { definePlugin, type NodeKind, type OpState } from "../plugin.js";
7
+ import { buildScreenNode } from "./parts.js";
8
+ import {
9
+ MAX_LABEL,
10
+ MAX_LIST_ITEMS,
11
+ MAX_PARTS,
12
+ PART_KINDS,
13
+ SCREEN_MODES,
14
+ SCREEN_NODE,
15
+ SCREEN_WIDTH,
16
+ } from "./types.js";
17
+
18
+ const partSchema = z.object({
19
+ kind: z.enum(PART_KINDS),
20
+ label: z.string().max(MAX_LABEL).optional(),
21
+ items: z.array(z.string().max(MAX_LABEL)).max(MAX_LIST_ITEMS).optional(),
22
+ text: z.string().max(MAX_LABEL).optional(),
23
+ variant: z.enum(["primary", "secondary"]).optional(),
24
+ });
25
+
26
+ const addScreenOp = z.object({
27
+ kind: z.literal("addScreen"),
28
+ id: z.string().min(1).optional(),
29
+ at: z.object({ x: z.number(), y: z.number() }),
30
+ label: z.string().max(MAX_LABEL).optional(),
31
+ mode: z.enum(SCREEN_MODES).optional(),
32
+ parts: z.array(partSchema).max(MAX_PARTS).optional(),
33
+ width: z.number().positive().optional(),
34
+ });
35
+
36
+ type AddScreen = z.infer<typeof addScreenOp>;
37
+
38
+ function applyAddScreen(state: OpState, op: AddScreen, opIndex: number): void {
39
+ const id = op.id ?? crypto.randomUUID();
40
+ if (state.nodes.some((node) => node.id === id)) {
41
+ throw new CanvasOpError(`Node id already exists: ${id}`, opIndex);
42
+ }
43
+ const node = buildScreenNode({
44
+ id,
45
+ position: op.at,
46
+ label: op.label,
47
+ mode: op.mode,
48
+ parts: op.parts,
49
+ width: op.width,
50
+ });
51
+ state.nodes = [...state.nodes, node];
52
+ }
53
+
54
+ /** Content-sized: the parts decide the height, so a layout never pins a stale one. */
55
+ export const screenKind: NodeKind = {
56
+ type: SCREEN_NODE,
57
+ label: "Screen",
58
+ sizing: "content-sized",
59
+ size: { width: SCREEN_WIDTH },
60
+ createData: () => ({ label: "", mode: "wireframe", parts: [] }),
61
+ };
62
+
63
+ export const screenPlugin = definePlugin({
64
+ id: "screen",
65
+ nodes: [screenKind],
66
+ ops: [{ kind: "addScreen", schema: addScreenOp, apply: applyAddScreen }],
67
+ });