@heyramzi/ui 2.9.0 → 2.10.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/dist/agents/helpers.js +52 -0
  2. package/dist/agents/index.js +3 -0
  3. package/dist/agents/page-state.js +65 -0
  4. package/dist/agents/types.js +1 -0
  5. package/dist/agents/vendor/color.js +149 -0
  6. package/dist/agents/vendor/draw.js +548 -0
  7. package/dist/agents/vendor/engine.js +632 -0
  8. package/dist/agents/vendor/mesh.js +727 -0
  9. package/dist/agents/vendor/plastic.js +3273 -0
  10. package/dist/agents/vendor/presets.js +171 -0
  11. package/dist/agents/vendor/shapes.js +24 -0
  12. package/dist/agents/vendor/ticker.js +56 -0
  13. package/dist/agents/vendor/types.js +0 -0
  14. package/dist/agents/vendor/wear.js +1525 -0
  15. package/dist/ai/ai-button/index.js +2 -0
  16. package/dist/ai/confirmation/confirmation-context.svelte.js +12 -0
  17. package/dist/ai/confirmation/index.js +9 -0
  18. package/dist/ai/generation-progress/index.js +2 -0
  19. package/dist/ai/index.js +11 -0
  20. package/dist/ai/loading-state/index.js +3 -0
  21. package/dist/ai/prompt-input/index.js +6 -0
  22. package/dist/ai/reasoning/index.js +4 -0
  23. package/dist/ai/reasoning/reasoning-context.svelte.js +12 -0
  24. package/dist/ai/response/index.js +3 -0
  25. package/dist/ai/suggestion/index.js +3 -0
  26. package/dist/ai/thinking-dots/index.js +3 -0
  27. package/dist/ai/typing-dots/index.js +2 -0
  28. package/dist/ai/voice-waveform/index.js +3 -0
  29. package/dist/animations/text-swap.js +30 -0
  30. package/dist/brand/index.js +65 -0
  31. package/dist/canvas/card/resize.js +10 -0
  32. package/dist/canvas/edges/edge-path.js +284 -0
  33. package/dist/canvas/editor/context-menu.js +42 -0
  34. package/dist/canvas/editor/dispatch.svelte.js +157 -0
  35. package/dist/canvas/editor/editor-context.js +8 -0
  36. package/dist/canvas/editor/editor-mode.js +0 -0
  37. package/dist/canvas/editor/frame.js +87 -0
  38. package/dist/canvas/editor/hand-context.js +8 -0
  39. package/dist/canvas/editor/history-core.js +221 -0
  40. package/dist/canvas/editor/keymap.js +193 -0
  41. package/dist/canvas/editor/layout-actions.js +170 -0
  42. package/dist/canvas/editor/lock.js +65 -0
  43. package/dist/canvas/editor/rebase.js +279 -0
  44. package/dist/canvas/editor/style.js +174 -0
  45. package/dist/canvas/editor/text-field-context.js +8 -0
  46. package/dist/canvas/editor/z-order.js +82 -0
  47. package/dist/canvas/headless/alignment.js +20 -0
  48. package/dist/canvas/headless/break-apart-find.js +324 -0
  49. package/dist/canvas/headless/break-apart-pieces.js +74 -0
  50. package/dist/canvas/headless/break-apart-plan.js +378 -0
  51. package/dist/canvas/headless/break-apart-style.js +93 -0
  52. package/dist/canvas/headless/break-apart.js +206 -0
  53. package/dist/canvas/headless/canvas-cleanup.js +70 -0
  54. package/dist/canvas/headless/edge-anchors.js +134 -0
  55. package/dist/canvas/headless/edge-memo.js +28 -0
  56. package/dist/canvas/headless/edge-serialization.js +31 -0
  57. package/dist/canvas/headless/figure-geometry.js +331 -0
  58. package/dist/canvas/headless/fit-viewport.js +82 -0
  59. package/dist/canvas/headless/grid.js +96 -0
  60. package/dist/canvas/headless/icon-node.js +45 -0
  61. package/dist/canvas/headless/index.js +1 -0
  62. package/dist/canvas/headless/linkify.js +25 -0
  63. package/dist/canvas/headless/pen-marks.js +115 -0
  64. package/dist/canvas/headless/sketch.js +250 -0
  65. package/dist/canvas/headless/snap-guides.js +100 -0
  66. package/dist/canvas/headless/svg-figure.js +137 -0
  67. package/dist/canvas/headless/table-layout.js +161 -0
  68. package/dist/canvas/index.js +25 -0
  69. package/dist/canvas/lod/lod-mode.svelte.js +22 -0
  70. package/dist/canvas/lod/render-all.svelte.js +45 -0
  71. package/dist/canvas/ops/arrange.js +83 -0
  72. package/dist/canvas/ops/ops.js +318 -0
  73. package/dist/canvas/pen/colors.js +41 -0
  74. package/dist/canvas/pen/eraser.js +35 -0
  75. package/dist/canvas/pen/geometry.js +143 -0
  76. package/dist/canvas/pen/index.js +9 -0
  77. package/dist/canvas/pen/ops.js +15 -0
  78. package/dist/canvas/pen/pen-session.js +171 -0
  79. package/dist/canvas/pen/plugin.js +129 -0
  80. package/dist/canvas/pen/shapes.js +80 -0
  81. package/dist/canvas/pen/stroke-path.js +37 -0
  82. package/dist/canvas/pen/types.js +30 -0
  83. package/dist/canvas/pen/ui.js +20 -0
  84. package/dist/canvas/plugin.js +51 -0
  85. package/dist/canvas/screen/glyphs.js +24 -0
  86. package/dist/canvas/screen/labels.js +24 -0
  87. package/dist/canvas/screen/parts.js +140 -0
  88. package/dist/canvas/screen/plugin.js +71 -0
  89. package/dist/canvas/screen/types.js +44 -0
  90. package/dist/canvas/screen/ui.js +13 -0
  91. package/dist/canvas/test-utils.js +18 -0
  92. package/dist/canvas/types.js +0 -0
  93. package/dist/canvas/ui/forward-wheel.js +27 -0
  94. package/dist/canvas/ui/icons.js +133 -0
  95. package/dist/canvas/ui/place-panel.js +57 -0
  96. package/dist/canvas/ui/shortcut-tip.js +18 -0
  97. package/dist/canvas/ui/tools.js +411 -0
  98. package/dist/charts/dashboard/chart.js +1 -0
  99. package/dist/charts/dashboard/data-table.js +7 -0
  100. package/dist/charts/dashboard/index.js +13 -0
  101. package/dist/charts/dashboard/windows.js +5 -0
  102. package/dist/charts/edge-fade.js +7 -0
  103. package/dist/charts/hover-band.js +13 -0
  104. package/dist/charts/index.js +19 -0
  105. package/dist/charts/motion.js +15 -0
  106. package/dist/charts/primitives/chart-utils.js +22 -0
  107. package/dist/charts/primitives/index.js +4 -0
  108. package/dist/components/copy/clipboard.js +38 -0
  109. package/dist/components/copy/copied.svelte.js +26 -0
  110. package/dist/components/copy/index.js +3 -0
  111. package/dist/components/data-table/data-table.js +55 -0
  112. package/dist/components/data-table/index.js +7 -0
  113. package/dist/components/docs/index.js +3 -0
  114. package/dist/components/docs/scroll-spy.svelte.js +71 -0
  115. package/dist/components/field-row/index.js +1 -0
  116. package/dist/components/hold-to-confirm/index.js +1 -0
  117. package/dist/components/icon/index.js +1 -0
  118. package/dist/components/language-flag/index.js +1 -0
  119. package/dist/components/list-page/filter-section.js +4 -0
  120. package/dist/components/list-page/index.js +25 -0
  121. package/dist/components/list-page/list-table.js +5 -0
  122. package/dist/components/list-page/queue-rail.js +3 -0
  123. package/dist/components/list-page/section-host.js +8 -0
  124. package/dist/components/list-page/step.js +5 -0
  125. package/dist/components/list-page/types.js +0 -0
  126. package/dist/components/locale-switch/index.js +1 -0
  127. package/dist/components/mark/index.js +1 -0
  128. package/dist/components/open-in/index.js +1 -0
  129. package/dist/components/picker/index.js +3 -0
  130. package/dist/components/picker/trigger.js +33 -0
  131. package/dist/components/picker/types.js +0 -0
  132. package/dist/components/section-card/index.js +1 -0
  133. package/dist/components/section-header/index.js +2 -0
  134. package/dist/components/segmented/collapse.js +32 -0
  135. package/dist/components/segmented/index.js +8 -0
  136. package/dist/components/segmented/segmented.js +50 -0
  137. package/dist/components/star-rating-input/index.js +2 -0
  138. package/dist/components/svg/index.js +1 -0
  139. package/dist/components/table-view/index.js +1 -0
  140. package/dist/components/table-view/types.js +0 -0
  141. package/dist/components/tags-input/index.js +2 -0
  142. package/dist/components/tags-input/tags.js +90 -0
  143. package/dist/components/theme/index.js +3 -0
  144. package/dist/components/toolbar/index.js +3 -0
  145. package/dist/components/toolbar/toolbar-trigger.js +4 -0
  146. package/dist/editor/anchors.js +17 -0
  147. package/dist/editor/block-id.js +62 -0
  148. package/dist/editor/block-ops.js +105 -0
  149. package/dist/editor/doc-blocks.js +181 -0
  150. package/dist/editor/extensions.js +200 -0
  151. package/dist/editor/index.js +4 -0
  152. package/dist/editor/markdown-test-helpers.js +6 -0
  153. package/dist/editor/markdown.js +486 -0
  154. package/dist/editor/serializer.js +268 -0
  155. package/dist/editor/slash-items.js +104 -0
  156. package/dist/editor/youtube-url.js +27 -0
  157. package/dist/effects/animated-beam/index.js +1 -0
  158. package/dist/effects/animated-beam/path-calculator.svelte.js +42 -0
  159. package/dist/effects/animated-beam/types.js +0 -0
  160. package/dist/effects/border-beam/css.js +420 -0
  161. package/dist/effects/border-beam/presets.js +1792 -0
  162. package/dist/effects/border-beam/register-properties.js +39 -0
  163. package/dist/effects/border-beam/types.js +0 -0
  164. package/dist/effects/image-comparison/compare.js +4 -0
  165. package/dist/effects/image-reveal/index.js +1 -0
  166. package/dist/effects/image-reveal/types.js +0 -0
  167. package/dist/effects/magnetic/magnetic.js +54 -0
  168. package/dist/effects/reveal/index.js +4 -0
  169. package/dist/effects/reveal/observe.js +43 -0
  170. package/dist/effects/reveal/types.js +0 -0
  171. package/dist/effects/shader-gradient/color.js +34 -0
  172. package/dist/effects/shader-gradient/index.js +1 -0
  173. package/dist/effects/shader-gradient/renderer.js +320 -0
  174. package/dist/effects/shader-gradient/shaders.js +280 -0
  175. package/dist/effects/shader-gradient/types.js +0 -0
  176. package/dist/icons/apple-symbol-exports.js +126 -0
  177. package/dist/icons/sf-symbols.js +465 -0
  178. package/dist/icons/trades.js +34 -0
  179. package/dist/inbox/index.js +4 -0
  180. package/dist/inbox/logic.js +156 -0
  181. package/dist/inbox/types.js +46 -0
  182. package/dist/index.js +18 -0
  183. package/dist/lib/arrive.js +13 -0
  184. package/dist/lib/icon-types.js +0 -0
  185. package/dist/lib/initials.js +17 -0
  186. package/dist/lib/layout.js +1 -0
  187. package/dist/lib/panel.js +3 -0
  188. package/dist/lib/result.js +26 -0
  189. package/dist/lib/sliding-pill.js +45 -0
  190. package/dist/lib/theme-transition.js +53 -0
  191. package/dist/lib/tone.js +32 -0
  192. package/dist/lib/utils.js +5 -0
  193. package/dist/onboarding/define-tours.js +28 -0
  194. package/dist/onboarding/index.js +4 -0
  195. package/dist/onboarding/onboarding.svelte.js +15 -0
  196. package/dist/onboarding/pick.js +16 -0
  197. package/dist/onboarding/stores.js +50 -0
  198. package/dist/onboarding/types.js +1 -0
  199. package/dist/onboarding/wait.js +25 -0
  200. package/dist/panel/index.js +1 -0
  201. package/dist/panel/side-panel-types.js +0 -0
  202. package/dist/record/demo-data.js +512 -0
  203. package/dist/record/fields.js +148 -0
  204. package/dist/record/format.js +97 -0
  205. package/dist/record/gutter.js +1 -0
  206. package/dist/record/index.js +15 -0
  207. package/dist/record/kinds.js +110 -0
  208. package/dist/record/logic.js +255 -0
  209. package/dist/record/types.js +52 -0
  210. package/dist/shadcn/accordion/index.js +5 -0
  211. package/dist/shadcn/alert/index.js +5 -0
  212. package/dist/shadcn/alert-dialog/index.js +12 -0
  213. package/dist/shadcn/attachment/index.js +10 -0
  214. package/dist/shadcn/avatar/index.js +7 -0
  215. package/dist/shadcn/badge/index.js +2 -0
  216. package/dist/shadcn/breadcrumb/index.js +8 -0
  217. package/dist/shadcn/bubble/index.js +5 -0
  218. package/dist/shadcn/button/index.js +2 -0
  219. package/dist/shadcn/calendar/index.js +19 -0
  220. package/dist/shadcn/card/index.js +8 -0
  221. package/dist/shadcn/checkbox/index.js +2 -0
  222. package/dist/shadcn/code-block/index.js +9 -0
  223. package/dist/shadcn/collapsible/index.js +4 -0
  224. package/dist/shadcn/command/index.js +13 -0
  225. package/dist/shadcn/context-menu/index.js +17 -0
  226. package/dist/shadcn/dialog/index.js +11 -0
  227. package/dist/shadcn/dropdown-menu/index.js +19 -0
  228. package/dist/shadcn/dropdown-menu/menu-item.js +23 -0
  229. package/dist/shadcn/empty/index.js +7 -0
  230. package/dist/shadcn/field/index.js +12 -0
  231. package/dist/shadcn/hooks/highlighter.js +92 -0
  232. package/dist/shadcn/hooks/is-mobile.svelte.js +7 -0
  233. package/dist/shadcn/hooks/use-highlighted-code.svelte.js +65 -0
  234. package/dist/shadcn/hover-card/index.js +5 -0
  235. package/dist/shadcn/input/index.js +3 -0
  236. package/dist/shadcn/input-otp/index.js +5 -0
  237. package/dist/shadcn/kbd/index.js +3 -0
  238. package/dist/shadcn/label/index.js +2 -0
  239. package/dist/shadcn/marker/index.js +4 -0
  240. package/dist/shadcn/message/index.js +7 -0
  241. package/dist/shadcn/message-scroller/index.js +8 -0
  242. package/dist/shadcn/popover/index.js +9 -0
  243. package/dist/shadcn/progress/index.js +2 -0
  244. package/dist/shadcn/progress/tone.js +11 -0
  245. package/dist/shadcn/radio-group/index.js +3 -0
  246. package/dist/shadcn/scroll-area/index.js +3 -0
  247. package/dist/shadcn/select/index.js +12 -0
  248. package/dist/shadcn/separator/index.js +2 -0
  249. package/dist/shadcn/sheet/index.js +11 -0
  250. package/dist/shadcn/sidebar/constants.js +6 -0
  251. package/dist/shadcn/sidebar/context.svelte.js +38 -0
  252. package/dist/shadcn/sidebar/index.js +25 -0
  253. package/dist/shadcn/skeleton/index.js +2 -0
  254. package/dist/shadcn/slider/index.js +2 -0
  255. package/dist/shadcn/sonner/index.js +1 -0
  256. package/dist/shadcn/spinner/index.js +1 -0
  257. package/dist/shadcn/switch/index.js +2 -0
  258. package/dist/shadcn/table/index.js +9 -0
  259. package/dist/shadcn/tabs/index.js +5 -0
  260. package/dist/shadcn/textarea/index.js +2 -0
  261. package/dist/shadcn/toggle/index.js +3 -0
  262. package/dist/shadcn/toggle-group/index.js +3 -0
  263. package/dist/shadcn/tooltip/context.js +8 -0
  264. package/dist/shadcn/tooltip/index.js +6 -0
  265. package/dist/shadcn/trace/index.js +9 -0
  266. package/dist/slides/bridges.js +66 -0
  267. package/dist/slides/deck-controller.svelte.js +221 -0
  268. package/dist/slides/export/capture.js +81 -0
  269. package/dist/slides/export/ffmpeg.js +85 -0
  270. package/dist/slides/export/index.js +4 -0
  271. package/dist/slides/export/launch.js +8 -0
  272. package/dist/slides/export/pdf.js +78 -0
  273. package/dist/slides/export/types.js +0 -0
  274. package/dist/slides/index.js +7 -0
  275. package/dist/slides/labels.js +26 -0
  276. package/dist/slides/layout.js +47 -0
  277. package/dist/slides/model.js +9 -0
  278. package/dist/slides/navigation.js +89 -0
  279. package/dist/slides/types.js +6 -0
  280. package/dist/status/define-statuses.js +16 -0
  281. package/dist/status/index.js +4 -0
  282. package/dist/status/status-types.js +0 -0
  283. package/dist/test-setup.js +26 -0
  284. package/dist/thread/index.js +3 -0
  285. package/dist/thread/logic.js +94 -0
  286. package/dist/thread/types.js +23 -0
  287. package/dist/work/board-order.js +109 -0
  288. package/dist/work/dates.js +19 -0
  289. package/dist/work/detail/detail-types.js +20 -0
  290. package/dist/work/docs/edited.js +39 -0
  291. package/dist/work/docs/index.js +7 -0
  292. package/dist/work/docs/labels.js +38 -0
  293. package/dist/work/docs/menu.js +121 -0
  294. package/dist/work/docs/outline.js +39 -0
  295. package/dist/work/gantt/logic.js +260 -0
  296. package/dist/work/inbox/inbox-logic.js +94 -0
  297. package/dist/work/inbox/inbox-statuses.js +86 -0
  298. package/dist/work/inbox/inbox-types.js +0 -0
  299. package/dist/work/inbox/index.js +1 -0
  300. package/dist/work/index.js +21 -0
  301. package/dist/work/kanban-drag.js +25 -0
  302. package/dist/work/kanban-types.js +0 -0
  303. package/dist/work/presets/sales.js +35 -0
  304. package/dist/work/reorder.js +10 -0
  305. package/dist/work/task-types.js +0 -0
  306. package/dist/work/time/duration.js +60 -0
  307. package/dist/work/time/time-types.js +25 -0
  308. package/dist/work/use-kanban-dnd.svelte.js +128 -0
  309. package/package.json +45 -3
  310. package/src/components/toolbar/toolbar-trigger.ts +8 -2
  311. package/src/record/demo-data.ts +4 -4
  312. package/src/record/record-demo.svelte +2 -2
@@ -0,0 +1,96 @@
1
+ import { STROKE_NODE } from "../pen/types.js";
2
+ export const GRID_SIZE = 20;
3
+ export const SNAP_GRID = [
4
+ GRID_SIZE,
5
+ GRID_SIZE
6
+ ];
7
+ export const FREEHAND_NODE_TYPES = new Set([
8
+ STROKE_NODE
9
+ ]);
10
+ export function snapToGrid(value) {
11
+ return GRID_SIZE * Math.round(value / GRID_SIZE);
12
+ }
13
+ function pointOf(value) {
14
+ if (typeof value !== "object" || value === null) return null;
15
+ const { x, y } = value;
16
+ return typeof x === "number" && typeof y === "number" && Number.isFinite(x) && Number.isFinite(y) ? {
17
+ x,
18
+ y
19
+ } : null;
20
+ }
21
+ function stringOf(value) {
22
+ return typeof value === "string" ? value : undefined;
23
+ }
24
+ export function snapWrittenNodes(before, after, exempt = FREEHAND_NODE_TYPES) {
25
+ const prior = new Map();
26
+ for (const node of before){
27
+ const id = stringOf(node.id);
28
+ if (id) prior.set(id, {
29
+ at: pointOf(node.position),
30
+ parent: stringOf(node.parentId)
31
+ });
32
+ }
33
+ const byId = new Map();
34
+ for (const node of after){
35
+ const id = stringOf(node.id);
36
+ if (id) byId.set(id, node);
37
+ }
38
+ const written = (node)=>{
39
+ const was = prior.get(String(node.id));
40
+ const at = pointOf(node.position);
41
+ const type = stringOf(node.type);
42
+ if (!at || type !== undefined && exempt.has(type)) return false;
43
+ if (!was?.at) return true;
44
+ return was.at.x !== at.x || was.at.y !== at.y || was.parent !== stringOf(node.parentId);
45
+ };
46
+ const settled = new Map();
47
+ const visiting = new Set();
48
+ const settle = (node)=>{
49
+ const id = String(node.id);
50
+ const done = settled.get(id);
51
+ if (done) return done;
52
+ const local = pointOf(node.position) ?? {
53
+ x: 0,
54
+ y: 0
55
+ };
56
+ const parentId = stringOf(node.parentId);
57
+ const parent = parentId ? byId.get(parentId) : undefined;
58
+ let origin = {
59
+ x: 0,
60
+ y: 0
61
+ };
62
+ if (parent && !visiting.has(id)) {
63
+ visiting.add(id);
64
+ origin = settle(parent).page;
65
+ visiting.delete(id);
66
+ }
67
+ const page = {
68
+ x: origin.x + local.x,
69
+ y: origin.y + local.y
70
+ };
71
+ const result = written(node) ? {
72
+ local: {
73
+ x: snapToGrid(page.x) - origin.x,
74
+ y: snapToGrid(page.y) - origin.y
75
+ },
76
+ page: {
77
+ x: snapToGrid(page.x),
78
+ y: snapToGrid(page.y)
79
+ }
80
+ } : {
81
+ local,
82
+ page
83
+ };
84
+ settled.set(id, result);
85
+ return result;
86
+ };
87
+ return after.map((node)=>{
88
+ if (!stringOf(node.id)) return node;
89
+ const at = pointOf(node.position);
90
+ const next = settle(node).local;
91
+ return at && (next.x !== at.x || next.y !== at.y) ? {
92
+ ...node,
93
+ position: next
94
+ } : node;
95
+ });
96
+ }
@@ -0,0 +1,45 @@
1
+ export const ICON_SIZE = 56;
2
+ export const ICON_W = 120;
3
+ export const ICON_LABEL_GAP = 12;
4
+ export const ICON_LABEL_H = 22;
5
+ export const ICON_LINE_H = 20;
6
+ export const ICON_SIDE_LABEL_H = 28;
7
+ export const ICON_PORT_GAP = 6;
8
+ export function iconGeometry(icon, width = ICON_W) {
9
+ const size = icon.size ?? ICON_SIZE;
10
+ const lines = icon.lines?.length ?? 0;
11
+ const mid = size / 2;
12
+ if (icon.labelSide === "right") {
13
+ const top = Math.max(0, (icon.label ? ICON_SIDE_LABEL_H : 0) - mid);
14
+ return {
15
+ size,
16
+ markX: 0,
17
+ markY: top,
18
+ height: Math.max(top + size, top + mid + ICON_PORT_GAP + lines * ICON_LINE_H),
19
+ left: {
20
+ x: 0,
21
+ y: top + mid
22
+ },
23
+ right: {
24
+ x: Math.max(width, size),
25
+ y: top + mid
26
+ }
27
+ };
28
+ }
29
+ const markX = (Math.max(width, size) - size) / 2;
30
+ const text = (icon.label ? ICON_LABEL_H : 0) + lines * ICON_LINE_H;
31
+ return {
32
+ size,
33
+ markX,
34
+ markY: 0,
35
+ height: size + (text ? ICON_LABEL_GAP + text : 0),
36
+ left: {
37
+ x: markX,
38
+ y: mid
39
+ },
40
+ right: {
41
+ x: markX + size,
42
+ y: mid
43
+ }
44
+ };
45
+ }
@@ -0,0 +1 @@
1
+ export { compilePenMark, penMarkSchema, placeInFrame } from "./pen-marks.js";
@@ -0,0 +1,25 @@
1
+ const URL_RE = /https?:\/\/[^\s<>"]+/g;
2
+ const TRAILING = /[.,;:!?)\]'”]+$/;
3
+ export function linkify(text) {
4
+ const parts = [];
5
+ let last = 0;
6
+ for (const match of text.matchAll(URL_RE)){
7
+ const start = match.index ?? 0;
8
+ const href = match[0].replace(TRAILING, "");
9
+ if (start > last) parts.push({
10
+ text: text.slice(last, start)
11
+ });
12
+ parts.push({
13
+ text: href,
14
+ href
15
+ });
16
+ last = start + href.length;
17
+ }
18
+ if (last < text.length) parts.push({
19
+ text: text.slice(last)
20
+ });
21
+ return parts;
22
+ }
23
+ export function hasLink(text) {
24
+ return /https?:\/\//.test(text);
25
+ }
@@ -0,0 +1,115 @@
1
+ import { z } from "zod";
2
+ import { buildStrokeNode } from "../pen/geometry.js";
3
+ import { buildShapeNode, buildTextNode } from "../pen/shapes.js";
4
+ import { PEN_COLORS, SHAPE_KINDS } from "../pen/types.js";
5
+ const colorSchema = z.enum(PEN_COLORS);
6
+ const pointSchema = z.tuple([
7
+ z.number(),
8
+ z.number()
9
+ ]);
10
+ const MAX_POINTS = 5000;
11
+ const inFrame = z.string().min(1).optional();
12
+ const strokeMark = z.object({
13
+ kind: z.literal("stroke"),
14
+ id: z.string().min(1).optional(),
15
+ in: inFrame,
16
+ points: z.array(z.tuple([
17
+ z.number(),
18
+ z.number()
19
+ ]).rest(z.number())).min(1).max(MAX_POINTS),
20
+ color: colorSchema.optional(),
21
+ size: z.number().positive().optional(),
22
+ tool: z.enum([
23
+ "pen",
24
+ "highlighter"
25
+ ]).optional()
26
+ });
27
+ const textMark = z.object({
28
+ kind: z.literal("text"),
29
+ id: z.string().min(1).optional(),
30
+ in: inFrame,
31
+ at: pointSchema,
32
+ text: z.string().min(1),
33
+ color: colorSchema.optional(),
34
+ fontSize: z.number().positive().optional()
35
+ });
36
+ const shapeMark = z.object({
37
+ kind: z.literal("shape"),
38
+ id: z.string().min(1).optional(),
39
+ in: inFrame,
40
+ shape: z.enum(SHAPE_KINDS),
41
+ box: z.tuple([
42
+ z.number(),
43
+ z.number(),
44
+ z.number().nonnegative(),
45
+ z.number().nonnegative()
46
+ ]),
47
+ color: colorSchema.optional(),
48
+ filled: z.boolean().optional(),
49
+ label: z.string().optional()
50
+ });
51
+ export const penMarkSchema = z.discriminatedUnion("kind", [
52
+ strokeMark,
53
+ textMark,
54
+ shapeMark
55
+ ]);
56
+ export function placeInFrame(node, frameId, frames, where) {
57
+ const frame = frames.get(frameId);
58
+ if (!frame) throw new Error(`${where}: no frame "${frameId}" to put it in`);
59
+ return {
60
+ ...node,
61
+ parentId: frameId,
62
+ extent: "parent",
63
+ position: {
64
+ x: node.position.x - frame.x,
65
+ y: node.position.y - frame.y
66
+ }
67
+ };
68
+ }
69
+ function toNode(node, id) {
70
+ return {
71
+ id,
72
+ type: node.type,
73
+ position: node.position,
74
+ data: node.data,
75
+ width: node.width,
76
+ height: node.height
77
+ };
78
+ }
79
+ export function compilePenMark(mark, index, frames) {
80
+ const where = `marks[${index}]`;
81
+ const id = mark.id ?? `pen-${index + 1}`;
82
+ let node;
83
+ if (mark.kind === "stroke") {
84
+ const built = buildStrokeNode(mark.points, {
85
+ color: mark.color,
86
+ size: mark.size,
87
+ tool: mark.tool
88
+ });
89
+ if (!built) throw new Error(`${where}: a stroke needs at least one point`);
90
+ node = toNode(built, id);
91
+ } else if (mark.kind === "text") {
92
+ const [x, y] = mark.at;
93
+ node = toNode(buildTextNode({
94
+ x,
95
+ y
96
+ }, {
97
+ text: mark.text,
98
+ color: mark.color,
99
+ fontSize: mark.fontSize
100
+ }), id);
101
+ } else {
102
+ const [x, y, width, height] = mark.box;
103
+ node = toNode(buildShapeNode(mark.shape, {
104
+ x,
105
+ y,
106
+ width,
107
+ height
108
+ }, {
109
+ color: mark.color,
110
+ filled: mark.filled,
111
+ label: mark.label
112
+ }), id);
113
+ }
114
+ return mark.in ? placeInFrame(node, mark.in, frames, where) : node;
115
+ }
@@ -0,0 +1,250 @@
1
+ import rough from "roughjs";
2
+ import { createMemo } from "./edge-memo.js";
3
+ export const SKETCH_ACCENTS = [
4
+ "ink",
5
+ "red",
6
+ "green",
7
+ "blue",
8
+ "violet",
9
+ "amber",
10
+ "teal",
11
+ "indigo",
12
+ "terra"
13
+ ];
14
+ const PALETTE = {
15
+ ink: {
16
+ stroke: "#1e1e1e",
17
+ fill: "#ffffff",
18
+ heading: "#1e1e1e",
19
+ body: "#343a40"
20
+ },
21
+ red: {
22
+ stroke: "#c92a2a",
23
+ fill: "#fff5f5",
24
+ heading: "#a61e4d",
25
+ body: "#3b3b46"
26
+ },
27
+ green: {
28
+ stroke: "#2f9e44",
29
+ fill: "#f4fce3",
30
+ heading: "#2b8a3e",
31
+ body: "#3b3b46"
32
+ },
33
+ blue: {
34
+ stroke: "#1971c2",
35
+ fill: "#e7f5ff",
36
+ heading: "#1864ab",
37
+ body: "#3b3b46"
38
+ },
39
+ violet: {
40
+ stroke: "#6741d9",
41
+ fill: "#f3f0ff",
42
+ heading: "#5f3dc4",
43
+ body: "#3b3b46"
44
+ },
45
+ amber: {
46
+ stroke: "#e8a33d",
47
+ fill: "#fff9db",
48
+ heading: "#b8860b",
49
+ body: "#3b3b46"
50
+ },
51
+ teal: {
52
+ stroke: "#0c8599",
53
+ fill: "#e3fafc",
54
+ heading: "#0b7285",
55
+ body: "#3b3b46"
56
+ },
57
+ indigo: {
58
+ stroke: "#414fd2",
59
+ fill: "#ffffff",
60
+ heading: "#414fd2",
61
+ body: "#414fd2"
62
+ },
63
+ terra: {
64
+ stroke: "#f0503d",
65
+ fill: "#ffffff",
66
+ heading: "#f0503d",
67
+ body: "#f0503d"
68
+ }
69
+ };
70
+ const DARK_PALETTE = {
71
+ ink: {
72
+ stroke: "#e9ecef",
73
+ fill: "#151b30",
74
+ heading: "#f1f3f5",
75
+ body: "#ced4da"
76
+ },
77
+ red: {
78
+ stroke: "#ff8787",
79
+ fill: "#2a1822",
80
+ heading: "#ffa8c5",
81
+ body: "#ced4da"
82
+ },
83
+ green: {
84
+ stroke: "#69db7c",
85
+ fill: "#132a20",
86
+ heading: "#8ce99a",
87
+ body: "#ced4da"
88
+ },
89
+ blue: {
90
+ stroke: "#74c0fc",
91
+ fill: "#12253d",
92
+ heading: "#a5d8ff",
93
+ body: "#ced4da"
94
+ },
95
+ violet: {
96
+ stroke: "#b197fc",
97
+ fill: "#211c3d",
98
+ heading: "#d0bfff",
99
+ body: "#ced4da"
100
+ },
101
+ amber: {
102
+ stroke: "#ffd43b",
103
+ fill: "#2b2612",
104
+ heading: "#ffe066",
105
+ body: "#ced4da"
106
+ },
107
+ teal: {
108
+ stroke: "#66d9e8",
109
+ fill: "#112a30",
110
+ heading: "#99e9f2",
111
+ body: "#ced4da"
112
+ },
113
+ indigo: {
114
+ stroke: "#8b96ff",
115
+ fill: "#151b30",
116
+ heading: "#a5adff",
117
+ body: "#a5adff"
118
+ },
119
+ terra: {
120
+ stroke: "#ff8a78",
121
+ fill: "#151b30",
122
+ heading: "#ff9d8d",
123
+ body: "#ff9d8d"
124
+ }
125
+ };
126
+ export function sketchColors(accent, dark = false) {
127
+ const palette = dark ? DARK_PALETTE : PALETTE;
128
+ return palette[accent ?? "ink"] ?? palette.ink;
129
+ }
130
+ export function isSketchAccent(value) {
131
+ return typeof value === "string" && SKETCH_ACCENTS.includes(value);
132
+ }
133
+ export function seedFrom(id) {
134
+ let hash = 2166136261;
135
+ for(let i = 0; i < id.length; i += 1){
136
+ hash ^= id.charCodeAt(i);
137
+ hash = Math.imul(hash, 16777619);
138
+ }
139
+ return Math.abs(hash) % 2147483647;
140
+ }
141
+ const generator = rough.generator();
142
+ const MEMO_LIMIT = 4000;
143
+ const lines = createMemo(MEMO_LIMIT);
144
+ const heads = createMemo(MEMO_LIMIT);
145
+ function baseOptions(seed, colors, overrides) {
146
+ return {
147
+ seed,
148
+ roughness: 1.1,
149
+ bowing: 1.4,
150
+ stroke: colors.stroke,
151
+ strokeWidth: 1.6,
152
+ fillStyle: "solid",
153
+ ...overrides
154
+ };
155
+ }
156
+ const FORMAL = {
157
+ roughness: 0,
158
+ bowing: 0,
159
+ disableMultiStroke: true,
160
+ strokeWidth: 1.4
161
+ };
162
+ function toPaths(drawable) {
163
+ return generator.toPaths(drawable).map((p)=>({
164
+ d: p.d,
165
+ stroke: p.stroke,
166
+ fill: p.fill ?? "none",
167
+ strokeWidth: p.strokeWidth
168
+ }));
169
+ }
170
+ export function roughCard(width, height, { seed, colors }, { radius = 14, formal = false } = {}) {
171
+ const w = Math.max(width, radius * 2 + 2);
172
+ const h = Math.max(height, radius * 2 + 2);
173
+ const r = Math.min(radius, w / 2 - 1, h / 2 - 1);
174
+ const d = [
175
+ `M ${r} 0`,
176
+ `L ${w - r} 0`,
177
+ `Q ${w} 0 ${w} ${r}`,
178
+ `L ${w} ${h - r}`,
179
+ `Q ${w} ${h} ${w - r} ${h}`,
180
+ `L ${r} ${h}`,
181
+ `Q 0 ${h} 0 ${h - r}`,
182
+ `L 0 ${r}`,
183
+ `Q 0 0 ${r} 0`,
184
+ "Z"
185
+ ].join(" ");
186
+ return toPaths(generator.path(d, baseOptions(seed, colors, {
187
+ fill: colors.fill,
188
+ ...formal ? FORMAL : {}
189
+ })));
190
+ }
191
+ export const ELLIPSE_CARD_SCALE = {
192
+ x: 1.32,
193
+ y: 1.42
194
+ };
195
+ export function roughEllipse(width, height, { seed, colors }, { formal = false } = {}) {
196
+ return toPaths(generator.ellipse(width / 2, height / 2, width, height, baseOptions(seed, colors, {
197
+ roughness: 1.6,
198
+ strokeWidth: 2.2,
199
+ fill: undefined,
200
+ ...formal ? FORMAL : {}
201
+ })));
202
+ }
203
+ export function roughRect(width, height, { seed, colors }, { filled = false, formal = false } = {}) {
204
+ return toPaths(generator.rectangle(1, 1, Math.max(width - 2, 2), Math.max(height - 2, 2), baseOptions(seed, colors, {
205
+ fill: filled ? colors.fill : undefined,
206
+ ...formal ? FORMAL : {}
207
+ })));
208
+ }
209
+ export function roughPolygon(points, { seed, colors }, { formal = false } = {}) {
210
+ return toPaths(generator.polygon(points.map(([x, y])=>[
211
+ x,
212
+ y
213
+ ]), baseOptions(seed, colors, {
214
+ fill: undefined,
215
+ ...formal ? FORMAL : {}
216
+ })));
217
+ }
218
+ export function roughUnderline(width, { seed, colors }, { y = 4, formal = false } = {}) {
219
+ return toPaths(generator.line(0, y, width, y, baseOptions(seed, colors, {
220
+ strokeWidth: 2,
221
+ bowing: 2.4,
222
+ ...formal ? FORMAL : {}
223
+ })));
224
+ }
225
+ export function roughLine(from, to, { seed, colors }, { formal = false } = {}) {
226
+ const hand = formal ? FORMAL : {};
227
+ return lines(`${seed}|${colors.stroke}|${formal}|${from.x},${from.y}|${to.x},${to.y}`, ()=>toPaths(generator.line(from.x, from.y, to.x, to.y, baseOptions(seed, colors, {
228
+ strokeWidth: 1.8,
229
+ ...hand
230
+ }))));
231
+ }
232
+ export function roughHead(tip, angle, { seed, colors }, { formal = false } = {}) {
233
+ const hand = formal ? FORMAL : {};
234
+ const head = 11;
235
+ const spread = 0.42;
236
+ return heads(`${seed}|${colors.stroke}|${formal}|${tip.x},${tip.y}|${angle}`, ()=>[
237
+ angle + Math.PI - spread,
238
+ angle + Math.PI + spread
239
+ ].flatMap((a, i)=>toPaths(generator.line(tip.x, tip.y, tip.x + Math.cos(a) * head, tip.y + Math.sin(a) * head, baseOptions(seed + i + 1, colors, {
240
+ strokeWidth: 1.8,
241
+ roughness: 0.8,
242
+ ...hand
243
+ })))));
244
+ }
245
+ export function roughArrow(from, to, pen, style = {}) {
246
+ return [
247
+ ...roughLine(from, to, pen, style),
248
+ ...roughHead(to, Math.atan2(to.y - from.y, to.x - from.x), pen, style)
249
+ ];
250
+ }
@@ -0,0 +1,100 @@
1
+ export const SNAP_THRESHOLD = 6;
2
+ const SIZE_KEY = {
3
+ x: "width",
4
+ y: "height"
5
+ };
6
+ const CROSS = {
7
+ x: "y",
8
+ y: "x"
9
+ };
10
+ function linesOf(rect, axis) {
11
+ const start = rect[axis];
12
+ const size = rect[SIZE_KEY[axis]];
13
+ return [
14
+ start,
15
+ start + size / 2,
16
+ start + size
17
+ ];
18
+ }
19
+ function snapAxis(dragged, others, axis, threshold) {
20
+ const mine = linesOf(dragged, axis);
21
+ const pairings = others.flatMap((other)=>linesOf(other, axis).flatMap((theirs)=>mine.map((own)=>({
22
+ delta: theirs - own,
23
+ position: theirs
24
+ }))));
25
+ const inRange = pairings.filter((pair)=>Math.abs(pair.delta) <= threshold);
26
+ if (inRange.length === 0) return {
27
+ delta: 0,
28
+ position: null
29
+ };
30
+ return inRange.reduce((a, b)=>Math.abs(b.delta) < Math.abs(a.delta) ? b : a);
31
+ }
32
+ function guideFor(snapped, others, axis, position) {
33
+ const cross = CROSS[axis];
34
+ const size = SIZE_KEY[cross];
35
+ const onLine = others.filter((other)=>linesOf(other, axis).some((line)=>Math.abs(line - position) < 0.5));
36
+ return {
37
+ axis,
38
+ position,
39
+ start: Math.min(snapped[cross], ...onLine.map((o)=>o[cross])),
40
+ end: Math.max(snapped[cross] + snapped[size], ...onLine.map((o)=>o[cross] + o[size]))
41
+ };
42
+ }
43
+ export function computeSnap(dragged, others, threshold = SNAP_THRESHOLD) {
44
+ if (others.length === 0) return {
45
+ x: dragged.x,
46
+ y: dragged.y,
47
+ guides: []
48
+ };
49
+ const horizontal = snapAxis(dragged, others, "x", threshold);
50
+ const vertical = snapAxis(dragged, others, "y", threshold);
51
+ const snapped = {
52
+ ...dragged,
53
+ x: dragged.x + horizontal.delta,
54
+ y: dragged.y + vertical.delta
55
+ };
56
+ const guides = [];
57
+ if (horizontal.position !== null) {
58
+ guides.push(guideFor(snapped, others, "x", horizontal.position));
59
+ }
60
+ if (vertical.position !== null) {
61
+ guides.push(guideFor(snapped, others, "y", vertical.position));
62
+ }
63
+ return {
64
+ x: snapped.x,
65
+ y: snapped.y,
66
+ guides
67
+ };
68
+ }
69
+ export function snapDraggedNode(nodes, held, zoom) {
70
+ const rect = freeNodeRect(held);
71
+ if (!held || !rect) return {
72
+ nodes,
73
+ guides: []
74
+ };
75
+ const others = nodes.filter((n)=>n.id !== held.id).map(freeNodeRect).filter((r)=>r !== null);
76
+ const snap = computeSnap(rect, others, SNAP_THRESHOLD / (zoom || 1));
77
+ const moved = snap.x !== held.position.x || snap.y !== held.position.y;
78
+ const next = moved ? nodes.map((n)=>n.id === held.id ? {
79
+ ...n,
80
+ position: {
81
+ x: snap.x,
82
+ y: snap.y
83
+ }
84
+ } : n) : nodes;
85
+ return {
86
+ nodes: next,
87
+ guides: snap.guides
88
+ };
89
+ }
90
+ function freeNodeRect(node) {
91
+ if (!node || node.parentId || node.hidden) return null;
92
+ const { width, height } = node.measured ?? {};
93
+ if (!width || !height) return null;
94
+ return {
95
+ x: node.position.x,
96
+ y: node.position.y,
97
+ width,
98
+ height
99
+ };
100
+ }