@heyramzi/ui 2.11.1 → 2.11.2

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 (266) hide show
  1. package/dist/agents/helpers.js +38 -38
  2. package/dist/agents/page-state.js +49 -50
  3. package/dist/agents/vendor/color.js +146 -127
  4. package/dist/agents/vendor/draw.js +617 -508
  5. package/dist/agents/vendor/engine.js +698 -599
  6. package/dist/agents/vendor/mesh.js +653 -683
  7. package/dist/agents/vendor/plastic.js +3913 -3112
  8. package/dist/agents/vendor/presets.js +163 -164
  9. package/dist/agents/vendor/shapes.js +21 -20
  10. package/dist/agents/vendor/ticker.js +41 -42
  11. package/dist/agents/vendor/types.js +1 -0
  12. package/dist/agents/vendor/wear.js +1421 -1448
  13. package/dist/ai/confirmation/confirmation-context.svelte.js +6 -6
  14. package/dist/ai/confirmation/index.js +3 -1
  15. package/dist/ai/index.js +33 -0
  16. package/dist/ai/prompt-input/index.js +3 -1
  17. package/dist/ai/reasoning/index.js +3 -1
  18. package/dist/ai/reasoning/reasoning-context.svelte.js +6 -6
  19. package/dist/ai/suggestion/index.js +3 -1
  20. package/dist/ai/typing-dots/index.js +3 -1
  21. package/dist/animations/text-swap.js +24 -24
  22. package/dist/brand/index.js +54 -48
  23. package/dist/canvas/card/resize.js +5 -3
  24. package/dist/canvas/edges/edge-path.js +255 -242
  25. package/dist/canvas/editor/context-menu.js +39 -36
  26. package/dist/canvas/editor/dispatch.svelte.js +154 -151
  27. package/dist/canvas/editor/editor-context.js +7 -2
  28. package/dist/canvas/editor/editor-mode.js +1 -0
  29. package/dist/canvas/editor/frame.js +73 -76
  30. package/dist/canvas/editor/hand-context.js +5 -2
  31. package/dist/canvas/editor/history-core.js +163 -191
  32. package/dist/canvas/editor/keymap.js +95 -169
  33. package/dist/canvas/editor/layout-actions.js +143 -144
  34. package/dist/canvas/editor/lock.js +52 -51
  35. package/dist/canvas/editor/rebase.js +217 -240
  36. package/dist/canvas/editor/style.js +132 -137
  37. package/dist/canvas/editor/text-field-context.js +7 -2
  38. package/dist/canvas/editor/z-order.js +65 -70
  39. package/dist/canvas/headless/alignment.js +22 -16
  40. package/dist/canvas/headless/break-apart-find.js +286 -279
  41. package/dist/canvas/headless/break-apart-pieces.js +62 -58
  42. package/dist/canvas/headless/break-apart-plan.js +299 -325
  43. package/dist/canvas/headless/break-apart-style.js +75 -70
  44. package/dist/canvas/headless/break-apart.js +188 -181
  45. package/dist/canvas/headless/canvas-cleanup.js +78 -58
  46. package/dist/canvas/headless/edge-anchors.js +115 -116
  47. package/dist/canvas/headless/edge-memo.js +24 -22
  48. package/dist/canvas/headless/edge-serialization.js +17 -27
  49. package/dist/canvas/headless/figure-geometry.js +232 -264
  50. package/dist/canvas/headless/fit-viewport.js +84 -67
  51. package/dist/canvas/headless/grid.js +88 -86
  52. package/dist/canvas/headless/icon-node.js +44 -36
  53. package/dist/canvas/headless/index.js +1 -0
  54. package/dist/canvas/headless/linkify.js +17 -19
  55. package/dist/canvas/headless/pen-marks.js +101 -95
  56. package/dist/canvas/headless/sketch.js +231 -209
  57. package/dist/canvas/headless/snap-guides.js +99 -83
  58. package/dist/canvas/headless/svg-figure.js +133 -117
  59. package/dist/canvas/headless/table-layout.js +146 -107
  60. package/dist/canvas/index.js +2 -0
  61. package/dist/canvas/lod/lod-mode.svelte.js +20 -18
  62. package/dist/canvas/lod/render-all.svelte.js +36 -39
  63. package/dist/canvas/ops/arrange.js +78 -71
  64. package/dist/canvas/ops/ops.js +260 -255
  65. package/dist/canvas/pen/colors.js +37 -35
  66. package/dist/canvas/pen/eraser.js +28 -27
  67. package/dist/canvas/pen/geometry.js +120 -130
  68. package/dist/canvas/pen/index.js +1 -0
  69. package/dist/canvas/pen/ops.js +11 -11
  70. package/dist/canvas/pen/pen-session.js +150 -159
  71. package/dist/canvas/pen/plugin.js +107 -104
  72. package/dist/canvas/pen/shapes.js +73 -68
  73. package/dist/canvas/pen/stroke-path.js +32 -30
  74. package/dist/canvas/pen/types.js +21 -21
  75. package/dist/canvas/pen/ui.js +10 -8
  76. package/dist/canvas/plugin.js +46 -46
  77. package/dist/canvas/screen/glyphs.js +12 -11
  78. package/dist/canvas/screen/labels.js +22 -22
  79. package/dist/canvas/screen/parts.js +98 -111
  80. package/dist/canvas/screen/plugin.js +47 -56
  81. package/dist/canvas/screen/types.js +30 -35
  82. package/dist/canvas/screen/ui.js +7 -7
  83. package/dist/canvas/test-utils.js +14 -14
  84. package/dist/canvas/types.js +1 -0
  85. package/dist/canvas/ui/forward-wheel.js +24 -25
  86. package/dist/canvas/ui/icons.js +64 -131
  87. package/dist/canvas/ui/place-panel.js +58 -49
  88. package/dist/canvas/ui/shortcut-tip.js +14 -12
  89. package/dist/canvas/ui/tools.js +300 -385
  90. package/dist/charts/dashboard/chart.js +4 -0
  91. package/dist/charts/dashboard/data-table.js +11 -0
  92. package/dist/charts/dashboard/windows.js +8 -3
  93. package/dist/charts/edge-fade.js +10 -5
  94. package/dist/charts/hover-band.js +16 -11
  95. package/dist/charts/index.js +0 -1
  96. package/dist/charts/motion.js +11 -7
  97. package/dist/charts/primitives/chart-utils.js +12 -12
  98. package/dist/components/copy/clipboard.js +32 -32
  99. package/dist/components/copy/copied.svelte.js +31 -23
  100. package/dist/components/data-table/data-table.js +39 -39
  101. package/dist/components/docs/index.js +4 -0
  102. package/dist/components/docs/scroll-spy.svelte.js +73 -68
  103. package/dist/components/list-page/filter-section.js +4 -1
  104. package/dist/components/list-page/index.js +2 -0
  105. package/dist/components/list-page/list-table.js +7 -2
  106. package/dist/components/list-page/queue-rail.js +14 -0
  107. package/dist/components/list-page/section-host.js +4 -2
  108. package/dist/components/list-page/step.js +4 -3
  109. package/dist/components/list-page/types.js +1 -0
  110. package/dist/components/picker/trigger.js +48 -30
  111. package/dist/components/picker/types.js +1 -0
  112. package/dist/components/segmented/collapse.js +38 -27
  113. package/dist/components/segmented/index.js +8 -0
  114. package/dist/components/segmented/segmented.js +67 -42
  115. package/dist/components/star-rating-input/index.js +3 -1
  116. package/dist/components/table-view/types.js +1 -0
  117. package/dist/components/tags-input/tags.js +81 -69
  118. package/dist/components/theme/index.js +4 -0
  119. package/dist/components/toolbar/index.js +4 -0
  120. package/dist/components/toolbar/toolbar-trigger.js +14 -1
  121. package/dist/editor/anchors.js +17 -13
  122. package/dist/editor/block-id.js +52 -51
  123. package/dist/editor/block-ops.js +108 -80
  124. package/dist/editor/doc-blocks.js +155 -166
  125. package/dist/editor/extensions.js +139 -180
  126. package/dist/editor/index.js +4 -0
  127. package/dist/editor/markdown-test-helpers.js +4 -2
  128. package/dist/editor/markdown.js +420 -408
  129. package/dist/editor/serializer.js +204 -200
  130. package/dist/editor/slash-items.js +89 -101
  131. package/dist/editor/youtube-url.js +25 -20
  132. package/dist/effects/animated-beam/path-calculator.svelte.js +43 -38
  133. package/dist/effects/animated-beam/types.js +1 -0
  134. package/dist/effects/border-beam/css.js +92 -78
  135. package/dist/effects/border-beam/presets.js +1705 -1720
  136. package/dist/effects/border-beam/register-properties.js +43 -33
  137. package/dist/effects/border-beam/types.js +1 -0
  138. package/dist/effects/image-comparison/compare.js +3 -2
  139. package/dist/effects/image-reveal/types.js +1 -0
  140. package/dist/effects/magnetic/magnetic.js +58 -52
  141. package/dist/effects/reveal/observe.js +50 -40
  142. package/dist/effects/reveal/types.js +1 -0
  143. package/dist/effects/shader-gradient/color.js +36 -29
  144. package/dist/effects/shader-gradient/renderer.js +384 -295
  145. package/dist/effects/shader-gradient/shaders.js +31 -0
  146. package/dist/effects/shader-gradient/types.js +1 -0
  147. package/dist/icons/apple-symbol-exports.js +99 -124
  148. package/dist/icons/sf-symbols.js +357 -461
  149. package/dist/icons/trades.js +43 -28
  150. package/dist/inbox/index.js +1 -0
  151. package/dist/inbox/logic.js +126 -122
  152. package/dist/inbox/types.js +43 -43
  153. package/dist/lib/arrive.js +19 -12
  154. package/dist/lib/icon-types.js +1 -0
  155. package/dist/lib/initials.js +16 -10
  156. package/dist/lib/layout.js +5 -0
  157. package/dist/lib/panel.js +13 -0
  158. package/dist/lib/result.js +22 -22
  159. package/dist/lib/sliding-pill.js +59 -44
  160. package/dist/lib/theme-transition.js +43 -43
  161. package/dist/lib/tone.js +31 -24
  162. package/dist/lib/utils.js +3 -1
  163. package/dist/onboarding/define-tours.js +24 -23
  164. package/dist/onboarding/onboarding.svelte.js +16 -12
  165. package/dist/onboarding/pick.js +10 -6
  166. package/dist/onboarding/stores.js +40 -41
  167. package/dist/onboarding/wait.js +24 -23
  168. package/dist/panel/side-panel-types.js +1 -0
  169. package/dist/record/demo-data.js +459 -486
  170. package/dist/record/fields.js +129 -131
  171. package/dist/record/format.js +70 -82
  172. package/dist/record/gutter.js +1 -0
  173. package/dist/record/kinds.js +95 -105
  174. package/dist/record/logic.js +238 -219
  175. package/dist/record/types.js +49 -48
  176. package/dist/shadcn/accordion/index.js +3 -1
  177. package/dist/shadcn/alert/index.js +3 -1
  178. package/dist/shadcn/alert-dialog/index.js +3 -1
  179. package/dist/shadcn/attachment/index.js +3 -1
  180. package/dist/shadcn/avatar/index.js +3 -1
  181. package/dist/shadcn/breadcrumb/index.js +3 -1
  182. package/dist/shadcn/bubble/index.js +3 -1
  183. package/dist/shadcn/button/index.js +3 -1
  184. package/dist/shadcn/calendar/index.js +3 -1
  185. package/dist/shadcn/card/index.js +3 -1
  186. package/dist/shadcn/checkbox/index.js +3 -1
  187. package/dist/shadcn/code-block/index.js +3 -1
  188. package/dist/shadcn/collapsible/index.js +3 -1
  189. package/dist/shadcn/command/index.js +4 -1
  190. package/dist/shadcn/context-menu/index.js +3 -1
  191. package/dist/shadcn/dialog/index.js +3 -1
  192. package/dist/shadcn/dropdown-menu/menu-item.js +40 -20
  193. package/dist/shadcn/empty/index.js +3 -1
  194. package/dist/shadcn/field/index.js +3 -1
  195. package/dist/shadcn/hooks/highlighter.js +77 -75
  196. package/dist/shadcn/hooks/is-mobile.svelte.js +3 -3
  197. package/dist/shadcn/hooks/use-highlighted-code.svelte.js +62 -62
  198. package/dist/shadcn/input/index.js +3 -1
  199. package/dist/shadcn/kbd/index.js +3 -1
  200. package/dist/shadcn/label/index.js +3 -1
  201. package/dist/shadcn/marker/index.js +3 -1
  202. package/dist/shadcn/message/index.js +3 -1
  203. package/dist/shadcn/message-scroller/index.js +3 -1
  204. package/dist/shadcn/popover/index.js +3 -1
  205. package/dist/shadcn/progress/index.js +3 -1
  206. package/dist/shadcn/progress/tone.js +8 -7
  207. package/dist/shadcn/radio-group/index.js +3 -1
  208. package/dist/shadcn/scroll-area/index.js +3 -1
  209. package/dist/shadcn/select/index.js +3 -1
  210. package/dist/shadcn/separator/index.js +3 -1
  211. package/dist/shadcn/sheet/index.js +3 -1
  212. package/dist/shadcn/sidebar/context.svelte.js +42 -28
  213. package/dist/shadcn/sidebar/index.js +3 -1
  214. package/dist/shadcn/skeleton/index.js +3 -1
  215. package/dist/shadcn/slider/index.js +3 -1
  216. package/dist/shadcn/switch/index.js +3 -1
  217. package/dist/shadcn/table/index.js +3 -1
  218. package/dist/shadcn/tabs/index.js +3 -1
  219. package/dist/shadcn/textarea/index.js +3 -1
  220. package/dist/shadcn/toggle/index.js +3 -1
  221. package/dist/shadcn/toggle-group/index.js +3 -1
  222. package/dist/shadcn/tooltip/context.js +7 -2
  223. package/dist/shadcn/tooltip/index.js +3 -1
  224. package/dist/shadcn/trace/index.js +3 -1
  225. package/dist/slides/bridges.js +65 -59
  226. package/dist/slides/deck-controller.svelte.js +210 -207
  227. package/dist/slides/export/capture.js +66 -71
  228. package/dist/slides/export/ffmpeg.js +76 -72
  229. package/dist/slides/export/index.js +3 -0
  230. package/dist/slides/export/launch.js +9 -6
  231. package/dist/slides/export/pdf.js +60 -54
  232. package/dist/slides/export/types.js +1 -0
  233. package/dist/slides/labels.js +24 -24
  234. package/dist/slides/layout.js +39 -41
  235. package/dist/slides/model.js +9 -7
  236. package/dist/slides/navigation.js +78 -73
  237. package/dist/slides/types.js +2 -2
  238. package/dist/status/define-statuses.js +18 -14
  239. package/dist/status/status-types.js +1 -0
  240. package/dist/test-setup.js +24 -20
  241. package/dist/thread/logic.js +73 -77
  242. package/dist/thread/types.js +21 -21
  243. package/dist/work/board-order.js +94 -90
  244. package/dist/work/dates.js +16 -9
  245. package/dist/work/detail/detail-types.js +18 -18
  246. package/dist/work/docs/edited.js +32 -29
  247. package/dist/work/docs/index.js +4 -0
  248. package/dist/work/docs/labels.js +34 -34
  249. package/dist/work/docs/menu.js +113 -106
  250. package/dist/work/docs/outline.js +39 -29
  251. package/dist/work/gantt/logic.js +211 -235
  252. package/dist/work/inbox/inbox-logic.js +71 -75
  253. package/dist/work/inbox/inbox-statuses.js +75 -72
  254. package/dist/work/inbox/inbox-types.js +1 -0
  255. package/dist/work/kanban-drag.js +27 -22
  256. package/dist/work/kanban-types.js +1 -0
  257. package/dist/work/presets/sales.js +36 -32
  258. package/dist/work/reorder.js +9 -8
  259. package/dist/work/task-types.js +1 -0
  260. package/dist/work/time/duration.js +51 -46
  261. package/dist/work/time/time-types.js +23 -23
  262. package/dist/work/use-kanban-dnd.svelte.js +126 -125
  263. package/package.json +3 -1
  264. package/src/components/list-page/master-detail.svelte +3 -1
  265. package/src/components/list-page/rail-row.svelte +4 -4
  266. package/src/editor/markdown.ts +30 -35
@@ -1,260 +1,236 @@
1
1
  import { dayNumber, isoFromDay, weekday } from "../dates.js";
2
- const DAY_MS = 86_400_000;
2
+ const DAY_MS = 864e5;
3
3
  export function dayX(layout, day) {
4
- return (day - layout.first) * layout.dayWidth;
4
+ return (day - layout.first) * layout.dayWidth;
5
5
  }
6
+ /** The task's own dates. A lone date stands for a one-day task. */
6
7
  export function spanOf(item, { getStart, getEnd }) {
7
- const first = getStart(item) || getEnd(item);
8
- const last = getEnd(item) || getStart(item);
9
- if (!first || !last) return null;
10
- const start = dayNumber(first);
11
- return {
12
- start,
13
- end: Math.max(start, dayNumber(last))
14
- };
8
+ const first = getStart(item) || getEnd(item);
9
+ const last = getEnd(item) || getStart(item);
10
+ if (!first || !last) return null;
11
+ const start = dayNumber(first);
12
+ return {
13
+ start,
14
+ end: Math.max(start, dayNumber(last))
15
+ };
15
16
  }
17
+ /** A bar's span, with the live drag applied. Resizes never cross the other edge. */
16
18
  export function liveSpan(span, drag, id) {
17
- if (!span || drag?.id !== id || drag.dx === 0) return span;
18
- if (drag.mode === "move") return {
19
- start: span.start + drag.dx,
20
- end: span.end + drag.dx
21
- };
22
- if (drag.mode === "start") return {
23
- start: Math.min(span.start + drag.dx, span.end),
24
- end: span.end
25
- };
26
- return {
27
- start: span.start,
28
- end: Math.max(span.end + drag.dx, span.start)
29
- };
19
+ if (!span || drag?.id !== id || drag.dx === 0) return span;
20
+ if (drag.mode === "move") return {
21
+ start: span.start + drag.dx,
22
+ end: span.end + drag.dx
23
+ };
24
+ if (drag.mode === "start") return {
25
+ start: Math.min(span.start + drag.dx, span.end),
26
+ end: span.end
27
+ };
28
+ return {
29
+ start: span.start,
30
+ end: Math.max(span.end + drag.dx, span.start)
31
+ };
30
32
  }
31
33
  function join(a, b) {
32
- return !a ? b : !b ? a : {
33
- start: Math.min(a.start, b.start),
34
- end: Math.max(a.end, b.end)
35
- };
34
+ return !a ? b : !b ? a : {
35
+ start: Math.min(a.start, b.start),
36
+ end: Math.max(a.end, b.end)
37
+ };
36
38
  }
39
+ /** The days shown: the tasks' span, padded, starting on a Monday, unless `from` and `to` say otherwise. */
37
40
  export function rangeOf(items, source, todayDay, from, to) {
38
- let lo = todayDay;
39
- let hi = todayDay;
40
- for (const item of items){
41
- const span = spanOf(item, source);
42
- if (!span) continue;
43
- lo = Math.min(lo, span.start);
44
- hi = Math.max(hi, span.end);
45
- }
46
- let first = from ? dayNumber(from) : lo - 7;
47
- if (!from) first -= (weekday(first) + 6) % 7;
48
- const last = to ? dayNumber(to) : hi + 21;
49
- return {
50
- first,
51
- last: Math.max(last, first)
52
- };
41
+ let lo = todayDay;
42
+ let hi = todayDay;
43
+ for (const item of items) {
44
+ const span = spanOf(item, source);
45
+ if (!span) continue;
46
+ lo = Math.min(lo, span.start);
47
+ hi = Math.max(hi, span.end);
48
+ }
49
+ let first = from ? dayNumber(from) : lo - 7;
50
+ // Day 0 was a Thursday. Step back to the Monday on or before `first`.
51
+ if (!from) first -= (weekday(first) + 6) % 7;
52
+ const last = to ? dayNumber(to) : hi + 21;
53
+ return {
54
+ first,
55
+ last: Math.max(last, first)
56
+ };
53
57
  }
54
58
  export function buildDays(first, count) {
55
- return Array.from({
56
- length: count
57
- }, (_, i)=>{
58
- const day = first + i;
59
- const wd = weekday(day);
60
- return {
61
- day,
62
- i,
63
- date: Number(isoFromDay(day).slice(8, 10)),
64
- wd,
65
- weekend: wd === 0 || wd === 6
66
- };
67
- });
59
+ return Array.from({ length: count }, (_, i) => {
60
+ const day = first + i;
61
+ const wd = weekday(day);
62
+ return {
63
+ day,
64
+ i,
65
+ date: Number(isoFromDay(day).slice(8, 10)),
66
+ wd,
67
+ weekend: wd === 0 || wd === 6
68
+ };
69
+ });
68
70
  }
69
71
  export function buildMonths(days, format) {
70
- const out = [];
71
- for (const d of days){
72
- const key = isoFromDay(d.day).slice(0, 7);
73
- const last = out.at(-1);
74
- if (last?.key === key) last.length++;
75
- else out.push({
76
- key,
77
- label: format.format(new Date(d.day * DAY_MS)),
78
- i: d.i,
79
- length: 1
80
- });
81
- }
82
- return out;
72
+ const out = [];
73
+ for (const d of days) {
74
+ const key = isoFromDay(d.day).slice(0, 7);
75
+ const last = out.at(-1);
76
+ if (last?.key === key) last.length++;
77
+ else out.push({
78
+ key,
79
+ label: format.format(new Date(d.day * DAY_MS)),
80
+ i: d.i,
81
+ length: 1
82
+ });
83
+ }
84
+ return out;
83
85
  }
86
+ /** Rows in draw order. Groups come first, then their tasks, a parent before its children. */
84
87
  export function buildRows(items, source) {
85
- const { getId, getParent, groups, getGroup, folded, drag } = source;
86
- const out = [];
87
- let order = 0;
88
- const live = (item, id)=>liveSpan(spanOf(item, source), drag, id);
89
- const emit = (members)=>{
90
- if (!getParent) {
91
- for (const item of members){
92
- const id = getId(item);
93
- const own = live(item, id);
94
- out.push({
95
- kind: "task",
96
- item,
97
- id,
98
- order: order++,
99
- depth: 0,
100
- hasChildren: false,
101
- open: true,
102
- own,
103
- span: own
104
- });
105
- }
106
- return;
107
- }
108
- const ids = new Set(members.map((item)=>getId(item)));
109
- const kids = new Map();
110
- for (const item of members){
111
- const parent = getParent(item);
112
- const key = parent && ids.has(parent) ? parent : null;
113
- kids.set(key, [
114
- ...kids.get(key) ?? [],
115
- item
116
- ]);
117
- }
118
- const walk = (item, depth, visible)=>{
119
- const id = getId(item);
120
- const children = kids.get(id) ?? [];
121
- const open = !folded.has(id);
122
- const own = live(item, id);
123
- const row = {
124
- kind: "task",
125
- item,
126
- id,
127
- order: 0,
128
- depth,
129
- hasChildren: children.length > 0,
130
- open,
131
- own,
132
- span: own
133
- };
134
- if (visible) {
135
- row.order = order++;
136
- out.push(row);
137
- }
138
- let below = null;
139
- for (const child of children)below = join(below, walk(child, depth + 1, visible && open));
140
- if (!own) row.span = below;
141
- return join(own, below);
142
- };
143
- for (const item of kids.get(null) ?? [])walk(item, 0, true);
144
- };
145
- if (!groups || !getGroup) {
146
- emit(items);
147
- return out;
148
- }
149
- for (const group of groups){
150
- const members = items.filter((item)=>getGroup(item) === group.id);
151
- let span = null;
152
- for (const item of members)span = join(span, live(item, getId(item)));
153
- out.push({
154
- kind: "group",
155
- group,
156
- count: members.length,
157
- span
158
- });
159
- emit(members);
160
- }
161
- return out;
88
+ const { getId, getParent, groups, getGroup, folded, drag } = source;
89
+ const out = [];
90
+ let order = 0;
91
+ const live = (item, id) => liveSpan(spanOf(item, source), drag, id);
92
+ // Depth first, so a parent's row comes before its children. A parent's span is known
93
+ // only after its children are, so the row is pushed first and filled in after.
94
+ const emit = (members) => {
95
+ if (!getParent) {
96
+ for (const item of members) {
97
+ const id = getId(item);
98
+ const own = live(item, id);
99
+ out.push({
100
+ kind: "task",
101
+ item,
102
+ id,
103
+ order: order++,
104
+ depth: 0,
105
+ hasChildren: false,
106
+ open: true,
107
+ own,
108
+ span: own
109
+ });
110
+ }
111
+ return;
112
+ }
113
+ const ids = new Set(members.map((item) => getId(item)));
114
+ const kids = new Map();
115
+ for (const item of members) {
116
+ const parent = getParent(item);
117
+ const key = parent && ids.has(parent) ? parent : null;
118
+ kids.set(key, [...kids.get(key) ?? [], item]);
119
+ }
120
+ const walk = (item, depth, visible) => {
121
+ const id = getId(item);
122
+ const children = kids.get(id) ?? [];
123
+ const open = !folded.has(id);
124
+ const own = live(item, id);
125
+ const row = {
126
+ kind: "task",
127
+ item,
128
+ id,
129
+ order: 0,
130
+ depth,
131
+ hasChildren: children.length > 0,
132
+ open,
133
+ own,
134
+ span: own
135
+ };
136
+ if (visible) {
137
+ row.order = order++;
138
+ out.push(row);
139
+ }
140
+ let below = null;
141
+ for (const child of children) below = join(below, walk(child, depth + 1, visible && open));
142
+ if (!own) row.span = below;
143
+ return join(own, below);
144
+ };
145
+ for (const item of kids.get(null) ?? []) walk(item, 0, true);
146
+ };
147
+ if (!groups || !getGroup) {
148
+ emit(items);
149
+ return out;
150
+ }
151
+ for (const group of groups) {
152
+ const members = items.filter((item) => getGroup(item) === group.id);
153
+ let span = null;
154
+ for (const item of members) span = join(span, live(item, getId(item)));
155
+ out.push({
156
+ kind: "group",
157
+ group,
158
+ count: members.length,
159
+ span
160
+ });
161
+ emit(members);
162
+ }
163
+ return out;
162
164
  }
165
+ /** A right-angled path through `points`, each corner rounded by up to `radius`. */
163
166
  export function roundedPath(points, radius) {
164
- let d = `M${points[0][0]},${points[0][1]}`;
165
- for(let i = 1; i < points.length - 1; i++){
166
- const [px, py] = points[i - 1];
167
- const [cx, cy] = points[i];
168
- const [nx, ny] = points[i + 1];
169
- const r = Math.min(radius, Math.hypot(cx - px, cy - py) / 2, Math.hypot(nx - cx, ny - cy) / 2);
170
- const inX = cx - Math.sign(cx - px) * r;
171
- const inY = cy - Math.sign(cy - py) * r;
172
- const outX = cx + Math.sign(nx - cx) * r;
173
- const outY = cy + Math.sign(ny - cy) * r;
174
- d += ` L${inX},${inY} Q${cx},${cy} ${outX},${outY}`;
175
- }
176
- const [lx, ly] = points[points.length - 1];
177
- return `${d} L${lx},${ly}`;
167
+ let d = `M${points[0][0]},${points[0][1]}`;
168
+ for (let i = 1; i < points.length - 1; i++) {
169
+ const [px, py] = points[i - 1];
170
+ const [cx, cy] = points[i];
171
+ const [nx, ny] = points[i + 1];
172
+ const r = Math.min(radius, Math.hypot(cx - px, cy - py) / 2, Math.hypot(nx - cx, ny - cy) / 2);
173
+ const inX = cx - Math.sign(cx - px) * r;
174
+ const inY = cy - Math.sign(cy - py) * r;
175
+ const outX = cx + Math.sign(nx - cx) * r;
176
+ const outY = cy + Math.sign(ny - cy) * r;
177
+ d += ` L${inX},${inY} Q${cx},${cy} ${outX},${outY}`;
178
+ }
179
+ const [lx, ly] = points[points.length - 1];
180
+ return `${d} L${lx},${ly}`;
178
181
  }
182
+ /** Each arrow runs from the end of the task waited on to the start of the one waiting. */
179
183
  export function buildArrows(rows, layout, getDependsOn) {
180
- const { rowHeight, inset } = layout;
181
- const at = new Map();
182
- rows.forEach((row, index)=>{
183
- if (row.kind === "task" && row.span) at.set(row.id, {
184
- row: index,
185
- span: row.span
186
- });
187
- });
188
- const out = [];
189
- for (const row of rows){
190
- if (row.kind !== "task") continue;
191
- const target = at.get(row.id);
192
- if (!target) continue;
193
- for (const dep of getDependsOn(row.item) ?? []){
194
- const source = at.get(dep);
195
- if (!source) continue;
196
- const x1 = dayX(layout, source.span.end + 1) - inset;
197
- const y1 = source.row * rowHeight + rowHeight / 2;
198
- const x2 = dayX(layout, target.span.start) + inset;
199
- const y2 = target.row * rowHeight + rowHeight / 2;
200
- const points = x2 - x1 >= 24 ? [
201
- [
202
- x1,
203
- y1
204
- ],
205
- [
206
- x1 + 12,
207
- y1
208
- ],
209
- [
210
- x1 + 12,
211
- y2
212
- ],
213
- [
214
- x2,
215
- y2
216
- ]
217
- ] : [
218
- [
219
- x1,
220
- y1
221
- ],
222
- [
223
- x1 + 10,
224
- y1
225
- ],
226
- [
227
- x1 + 10,
228
- y1 + (y2 > y1 ? rowHeight / 2 : -rowHeight / 2)
229
- ],
230
- [
231
- x2 - 12,
232
- y1 + (y2 > y1 ? rowHeight / 2 : -rowHeight / 2)
233
- ],
234
- [
235
- x2 - 12,
236
- y2
237
- ],
238
- [
239
- x2,
240
- y2
241
- ]
242
- ];
243
- out.push({
244
- key: `${dep}->${row.id}`,
245
- d: roundedPath(points, 6),
246
- head: `M${x2 - 5},${y2 - 3.5} L${x2},${y2} L${x2 - 5},${y2 + 3.5}`,
247
- from: dep,
248
- to: row.id
249
- });
250
- }
251
- }
252
- return out;
184
+ const { rowHeight, inset } = layout;
185
+ const at = new Map();
186
+ rows.forEach((row, index) => {
187
+ if (row.kind === "task" && row.span) at.set(row.id, {
188
+ row: index,
189
+ span: row.span
190
+ });
191
+ });
192
+ const out = [];
193
+ for (const row of rows) {
194
+ if (row.kind !== "task") continue;
195
+ const target = at.get(row.id);
196
+ if (!target) continue;
197
+ for (const dep of getDependsOn(row.item) ?? []) {
198
+ const source = at.get(dep);
199
+ if (!source) continue;
200
+ const x1 = dayX(layout, source.span.end + 1) - inset;
201
+ const y1 = source.row * rowHeight + rowHeight / 2;
202
+ const x2 = dayX(layout, target.span.start) + inset;
203
+ const y2 = target.row * rowHeight + rowHeight / 2;
204
+ // Room between the bars: one bend in the gap. No room: out, round the row gap, and back in.
205
+ const points = x2 - x1 >= 24 ? [
206
+ [x1, y1],
207
+ [x1 + 12, y1],
208
+ [x1 + 12, y2],
209
+ [x2, y2]
210
+ ] : [
211
+ [x1, y1],
212
+ [x1 + 10, y1],
213
+ [x1 + 10, y1 + (y2 > y1 ? rowHeight / 2 : -rowHeight / 2)],
214
+ [x2 - 12, y1 + (y2 > y1 ? rowHeight / 2 : -rowHeight / 2)],
215
+ [x2 - 12, y2],
216
+ [x2, y2]
217
+ ];
218
+ out.push({
219
+ key: `${dep}->${row.id}`,
220
+ d: roundedPath(points, 6),
221
+ head: `M${x2 - 5},${y2 - 3.5} L${x2},${y2} L${x2 - 5},${y2 + 3.5}`,
222
+ from: dep,
223
+ to: row.id
224
+ });
225
+ }
226
+ }
227
+ return out;
253
228
  }
229
+ /** The pixel width of a span's bar, never under 8px. */
254
230
  export function barWidth(span, layout) {
255
- return Math.max((span.end - span.start + 1) * layout.dayWidth - layout.inset * 2, 8);
231
+ return Math.max((span.end - span.start + 1) * layout.dayWidth - layout.inset * 2, 8);
256
232
  }
257
233
  export function rangeLabel(span, format) {
258
- const length = span.end - span.start + 1;
259
- return `${format.format(new Date(span.start * DAY_MS))} to ${format.format(new Date(span.end * DAY_MS))}, ${length} ${length === 1 ? "day" : "days"}`;
234
+ const length = span.end - span.start + 1;
235
+ return `${format.format(new Date(span.start * DAY_MS))} to ${format.format(new Date(span.end * DAY_MS))}, ${length} ${length === 1 ? "day" : "days"}`;
260
236
  }
@@ -1,94 +1,90 @@
1
+ /** The draft to open on: the agent's pick, else the first. */
1
2
  export function startDraft(drafts, recommended) {
2
- return drafts.find((draft)=>draft.templateId === recommended) ?? drafts[0];
3
+ return drafts.find((draft) => draft.templateId === recommended) ?? drafts[0];
3
4
  }
5
+ /** A person can act on an item only while it waits for them. */
4
6
  export function isOpen(item) {
5
- return item.tier === "ask" && item.state === "proposed";
7
+ return item.tier === "ask" && item.state === "proposed";
6
8
  }
7
9
  export function canUndo(item) {
8
- return item.tier === "auto" && item.state === "done";
10
+ return item.tier === "auto" && item.state === "done";
9
11
  }
12
+ /** Lowercase, one space between words, no trailing ellipsis: what two copies of a line share. */
10
13
  function plain(text) {
11
- return text.replace(/(…|\.{3})\s*$/, "").replace(/\s+/g, " ").trim().toLowerCase();
14
+ return text.replace(/(…|\.{3})\s*$/, "").replace(/\s+/g, " ").trim().toLowerCase();
12
15
  }
16
+ /**
17
+ * Each fact once: a line that repeats the title or the command, even clipped, is dropped, and
18
+ * one that starts with the title keeps only the rest.
19
+ */
13
20
  export function cardFacts(item) {
14
- const { command, summary, detail } = item.proposal;
15
- const shown = [
16
- plain(item.title)
17
- ];
18
- if (command) shown.push(plain(`${command.tool}: ${command.text}`), plain(command.text));
19
- const lines = [];
20
- for (const raw of [
21
- summary,
22
- detail
23
- ]){
24
- let line = raw?.trim() ?? "";
25
- const title = item.title.trim();
26
- if (line.toLowerCase().startsWith(title.toLowerCase()) && line.length > title.length) {
27
- line = line.slice(title.length).replace(/^[\s:.,;-]+/, "");
28
- }
29
- const key = plain(line);
30
- if (!key || shown.some((seen)=>seen.includes(key) || key.includes(seen))) continue;
31
- shown.push(key);
32
- lines.push(line);
33
- }
34
- return {
35
- lines,
36
- command
37
- };
21
+ const { command, summary, detail } = item.proposal;
22
+ const shown = [plain(item.title)];
23
+ if (command) shown.push(plain(`${command.tool}: ${command.text}`), plain(command.text));
24
+ const lines = [];
25
+ for (const raw of [summary, detail]) {
26
+ let line = raw?.trim() ?? "";
27
+ const title = item.title.trim();
28
+ if (line.toLowerCase().startsWith(title.toLowerCase()) && line.length > title.length) {
29
+ line = line.slice(title.length).replace(/^[\s:.,;-]+/, "");
30
+ }
31
+ const key = plain(line);
32
+ if (!key || shown.some((seen) => seen.includes(key) || key.includes(seen))) continue;
33
+ shown.push(key);
34
+ lines.push(line);
35
+ }
36
+ return {
37
+ lines,
38
+ command
39
+ };
38
40
  }
41
+ /** The reasoning of a trace when no line of the card already says it. */
39
42
  export function freshReasoning(item, facts) {
40
- const reasoning = item.trace.reasoning.trim();
41
- const key = plain(reasoning);
42
- if (!key) return "";
43
- const said = [
44
- item.title,
45
- item.proposal.summary,
46
- item.proposal.detail ?? "",
47
- ...facts.lines
48
- ].map(plain).filter(Boolean);
49
- return said.some((line)=>line.includes(key) || key.includes(line)) ? "" : reasoning;
43
+ const reasoning = item.trace.reasoning.trim();
44
+ const key = plain(reasoning);
45
+ if (!key) return "";
46
+ const said = [
47
+ item.title,
48
+ item.proposal.summary,
49
+ item.proposal.detail ?? "",
50
+ ...facts.lines
51
+ ].map(plain).filter(Boolean);
52
+ return said.some((line) => line.includes(key) || key.includes(line)) ? "" : reasoning;
50
53
  }
51
54
  const ISO = /^\d{4}-\d{2}-\d{2}T/;
52
55
  const UNITS = [
53
- [
54
- 604_800,
55
- "week"
56
- ],
57
- [
58
- 86_400,
59
- "day"
60
- ],
61
- [
62
- 3_600,
63
- "hour"
64
- ],
65
- [
66
- 60,
67
- "minute"
68
- ]
56
+ [604800, "week"],
57
+ [86400, "day"],
58
+ [3600, "hour"],
59
+ [60, "minute"]
69
60
  ];
61
+ /**
62
+ * A time as a person says it: "3 hr. ago", "Oct 12". A host-formatted string stays as written,
63
+ * and a raw ISO string never reaches the screen.
64
+ */
70
65
  export function whenOf(at, now = Date.now(), locale = "en-GB") {
71
- if (!ISO.test(at)) return at;
72
- const time = Date.parse(at);
73
- if (Number.isNaN(time)) return "";
74
- const seconds = Math.round((time - now) / 1000);
75
- const age = Math.abs(seconds);
76
- if (age >= 4 * 604_800) {
77
- return new Intl.DateTimeFormat(locale, {
78
- month: "short",
79
- day: "numeric"
80
- }).format(time);
81
- }
82
- const rtf = new Intl.RelativeTimeFormat(locale, {
83
- numeric: "auto",
84
- style: "short"
85
- });
86
- for (const [size, unit] of UNITS){
87
- if (age >= size) return rtf.format(Math.round(seconds / size), unit);
88
- }
89
- return rtf.format(0, "second");
66
+ if (!ISO.test(at)) return at;
67
+ const time = Date.parse(at);
68
+ if (Number.isNaN(time)) return "";
69
+ const seconds = Math.round((time - now) / 1e3);
70
+ const age = Math.abs(seconds);
71
+ if (age >= 4 * 604800) {
72
+ return new Intl.DateTimeFormat(locale, {
73
+ month: "short",
74
+ day: "numeric"
75
+ }).format(time);
76
+ }
77
+ const rtf = new Intl.RelativeTimeFormat(locale, {
78
+ numeric: "auto",
79
+ style: "short"
80
+ });
81
+ for (const [size, unit] of UNITS) {
82
+ if (age >= size) return rtf.format(Math.round(seconds / size), unit);
83
+ }
84
+ return rtf.format(0, "second");
90
85
  }
86
+ /** True when a key press belongs to a text field and so isn't a shortcut. */
91
87
  export function isTyping(target) {
92
- if (!(target instanceof HTMLElement)) return false;
93
- return target.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(target.tagName);
88
+ if (!(target instanceof HTMLElement)) return false;
89
+ return target.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(target.tagName);
94
90
  }