@heyramzi/ui 2.11.0 → 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 (267) 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/components/tags-input/tags-input.svelte +2 -0
  267. package/src/editor/markdown.ts +30 -35
@@ -1 +1,5 @@
1
+ /**
2
+ * The height of every dashboard chart, half width or full, so a trend is one
3
+ * size on every page.
4
+ */
1
5
  export const CHART_HEIGHT = 240;
@@ -1,7 +1,18 @@
1
+ /**
2
+ * The one skin for a dashboard data table. Classes, not a component: two
3
+ * dashboards share nothing but the look, and each drew its own slightly different one.
4
+ */
5
+ // Heads aren't uppercase. `SectionLabel` already is, and a nine-column head in
6
+ // the same style fights the panel title and reads worse where density matters.
1
7
  export const dataTableClass = "w-full text-sm";
2
8
  export const dataTheadClass = "bg-muted/40 text-xs text-muted-foreground";
3
9
  export const dataThClass = "px-4 py-2.5 text-left font-medium";
10
+ /** A head over numbers. Right-aligned to the digits under it. */
4
11
  export const dataThNumClass = "px-4 py-2.5 text-right font-medium";
5
12
  export const dataTrClass = "border-b border-border transition-colors last:border-0 hover:bg-muted/40";
6
13
  export const dataTdClass = "px-4 py-2.5 align-top";
14
+ /**
15
+ * A cell holding a number. `tabular-nums` is required: proportional digits make
16
+ * a spend column ragged, and then nobody can scan it.
17
+ */
7
18
  export const dataTdNumClass = "px-4 py-2.5 text-right align-top tabular-nums";
@@ -1,5 +1,10 @@
1
+ /**
2
+ * Day windows a strip offers: seven, thirty, ninety, the three every dataset
3
+ * has. A page may add an option its data carries; labels translate at the
4
+ * call site.
5
+ */
1
6
  export const DAY_WINDOWS = [
2
- 7,
3
- 30,
4
- 90
7
+ 7,
8
+ 30,
9
+ 90
5
10
  ];
@@ -1,7 +1,12 @@
1
- export const EDGE_FADE_DEFAULT = 0.08;
1
+ /** `true` fades this much of the width at each side. */
2
+ export const EDGE_FADE_DEFAULT = .08;
3
+ /**
4
+ * Turns an `edgeFade` prop into the fraction of the width to fade at each side.
5
+ * `false` (or 0) means no mask is rendered at all.
6
+ */
2
7
  export function resolveEdgeFade(value, fallback = EDGE_FADE_DEFAULT) {
3
- if (value === false) return 0;
4
- if (value === true || value == null) return fallback;
5
- if (!Number.isFinite(value)) return 0;
6
- return Math.min(0.45, Math.max(0, value));
8
+ if (value === false) return 0;
9
+ if (value === true || value == null) return fallback;
10
+ if (!Number.isFinite(value)) return 0;
11
+ return Math.min(.45, Math.max(0, value));
7
12
  }
@@ -1,13 +1,18 @@
1
- export const HOVER_BAND_REACH = 0.8;
1
+ /** How far the hover band reaches each side of the point, as a share of the gap to its neighbour. */
2
+ export const HOVER_BAND_REACH = .8;
3
+ /**
4
+ * The hovered segment's clip, centered on the point at `xs[i]`.
5
+ * Both sides take the shorter neighbour gap, so the dot always sits in the middle.
6
+ */
2
7
  export function hoverBand(xs, i, width, reach = HOVER_BAND_REACH) {
3
- if (i < 0 || i >= xs.length) return null;
4
- const at = xs[i];
5
- const before = i > 0 ? at - xs[i - 1] : Infinity;
6
- const after = i < xs.length - 1 ? xs[i + 1] - at : Infinity;
7
- const gap = Math.min(before, after);
8
- const half = (Number.isFinite(gap) ? gap : width) * reach;
9
- return {
10
- x: at - half,
11
- width: half * 2
12
- };
8
+ if (i < 0 || i >= xs.length) return null;
9
+ const at = xs[i];
10
+ const before = i > 0 ? at - xs[i - 1] : Infinity;
11
+ const after = i < xs.length - 1 ? xs[i + 1] - at : Infinity;
12
+ const gap = Math.min(before, after);
13
+ const half = (Number.isFinite(gap) ? gap : width) * reach;
14
+ return {
15
+ x: at - half,
16
+ width: half * 2
17
+ };
13
18
  }
@@ -8,7 +8,6 @@ export { default as TimeSeriesChart } from "../../src/charts/time-series-chart.s
8
8
  export { default as GaugeChart } from "../../src/charts/gauge-chart.svelte";
9
9
  export { default as RingChart } from "../../src/charts/ring-chart.svelte";
10
10
  export { default as Legend } from "../../src/charts/legend.svelte";
11
- export { } from "../../src/charts/reference-line.svelte";
12
11
  export { default as StatCard } from "../../src/charts/stat-card.svelte";
13
12
  export { default as NumberChart } from "../../src/charts/number-chart.svelte";
14
13
  export { default as Figure } from "../../src/charts/figure.svelte";
@@ -1,15 +1,19 @@
1
1
  import { cubicInOut } from "svelte/easing";
2
+ /** Mirrors `--chart-motion-reveal` in charts.css, for when no element is mounted to read it from. */
2
3
  export const REVEAL_MS = 1100;
4
+ /** Rescales the axes when the data changes; the first render never animates. */
3
5
  export const DOMAIN_MOTION = {
4
- type: "tween",
5
- duration: 500,
6
- easing: cubicInOut
6
+ type: "tween",
7
+ duration: 500,
8
+ easing: cubicInOut
7
9
  };
10
+ /** The tooltip sits beside the hovered point at the top of the plot, so it never chases the cursor. */
8
11
  export const TOOLTIP_TOP = 8;
9
12
  export const TOOLTIP_GAP = 16;
13
+ /** A chart duration token in ms, read off a mounted element; both `ms` and `s` parse. */
10
14
  export function tokenMs(el, name, fallback) {
11
- const raw = getComputedStyle(el).getPropertyValue(name).trim();
12
- const n = Number.parseFloat(raw);
13
- if (Number.isNaN(n)) return fallback;
14
- return raw.endsWith("ms") ? n : n * 1000;
15
+ const raw = getComputedStyle(el).getPropertyValue(name).trim();
16
+ const n = Number.parseFloat(raw);
17
+ if (Number.isNaN(n)) return fallback;
18
+ return raw.endsWith("ms") ? n : n * 1e3;
15
19
  }
@@ -1,22 +1,22 @@
1
1
  import { getContext, setContext } from "svelte";
2
2
  export const THEMES = {
3
- light: "",
4
- dark: ".dark"
3
+ light: "",
4
+ dark: ".dark"
5
5
  };
6
6
  export function getPayloadConfigFromPayload(config, item, key) {
7
- if (typeof item !== "object" || item === null) return undefined;
8
- let configLabelKey = key;
9
- if (item.key === key) {
10
- configLabelKey = item.key;
11
- } else if (item.label === key) {
12
- configLabelKey = item.label;
13
- }
14
- return configLabelKey in config ? config[configLabelKey] : config[key];
7
+ if (typeof item !== "object" || item === null) return undefined;
8
+ let configLabelKey = key;
9
+ if (item.key === key) {
10
+ configLabelKey = item.key;
11
+ } else if (item.label === key) {
12
+ configLabelKey = item.label;
13
+ }
14
+ return configLabelKey in config ? config[configLabelKey] : config[key];
15
15
  }
16
16
  const chartContextKey = Symbol("chart-context");
17
17
  export function setChartContext(value) {
18
- return setContext(chartContextKey, value);
18
+ return setContext(chartContextKey, value);
19
19
  }
20
20
  export function useChart() {
21
- return getContext(chartContextKey);
21
+ return getContext(chartContextKey);
22
22
  }
@@ -1,38 +1,38 @@
1
+ /**
2
+ * The one write to the clipboard. A rejected permission, a missing gesture and
3
+ * an insecure origin get one answer: false. The caller decides what to show.
4
+ */
5
+ /** Plain text. Returns false when the browser refused the write. */
1
6
  export async function copyText(value) {
2
- try {
3
- await navigator.clipboard.writeText(value);
4
- return true;
5
- } catch {
6
- return false;
7
- }
7
+ try {
8
+ await navigator.clipboard.writeText(value);
9
+ return true;
10
+ } catch {
11
+ return false;
12
+ }
8
13
  }
14
+ /**
15
+ * HTML plus a plain-text twin: Gmail keeps the formatting, Cmd+Shift+V stays
16
+ * readable. Falls back to plain text.
17
+ */
9
18
  export async function copyRichText(html, plainText) {
10
- const text = plainText ?? htmlToPlainText(html);
11
- try {
12
- await navigator.clipboard.write([
13
- new ClipboardItem({
14
- "text/html": new Blob([
15
- html
16
- ], {
17
- type: "text/html"
18
- }),
19
- "text/plain": new Blob([
20
- text
21
- ], {
22
- type: "text/plain"
23
- })
24
- })
25
- ]);
26
- return true;
27
- } catch {
28
- return copyText(text);
29
- }
19
+ const text = plainText ?? htmlToPlainText(html);
20
+ try {
21
+ await navigator.clipboard.write([new ClipboardItem({
22
+ "text/html": new Blob([html], { type: "text/html" }),
23
+ "text/plain": new Blob([text], { type: "text/plain" })
24
+ })]);
25
+ return true;
26
+ } catch {
27
+ return copyText(text);
28
+ }
30
29
  }
30
+ /** The plain-text twin of an HTML fragment. */
31
31
  export function htmlToPlainText(html) {
32
- if (typeof document === "undefined") {
33
- return html.replace(/<br\s*\/?>/gi, "\n").replace(/<\/p>/gi, "\n\n").replace(/<\/li>/gi, "\n").replace(/<li>/gi, "• ").replace(/<[^>]+>/g, "").replace(/&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").trim();
34
- }
35
- const div = document.createElement("div");
36
- div.innerHTML = html;
37
- return div.textContent || div.innerText || "";
32
+ if (typeof document === "undefined") {
33
+ return html.replace(/<br\s*\/?>/gi, "\n").replace(/<\/p>/gi, "\n\n").replace(/<\/li>/gi, "\n").replace(/<li>/gi, "• ").replace(/<[^>]+>/g, "").replace(/&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").trim();
34
+ }
35
+ const div = document.createElement("div");
36
+ div.innerHTML = html;
37
+ return div.textContent || div.innerText || "";
38
38
  }
@@ -1,26 +1,34 @@
1
1
  import { copyText } from "./clipboard.js";
2
+ /**
3
+ * The copied flag for a surface whose affordance is a whole row or card rather
4
+ * than a `CopyButton`. Keyed, so one instance serves a list of rows.
5
+ */
2
6
  export class Copied {
3
- key = $state(null);
4
- #timer = null;
5
- is(key = "") {
6
- return this.key === key;
7
- }
8
- async copy(value, key = "", hold = 1400) {
9
- if (!await copyText(value)) return false;
10
- this.mark(key, hold);
11
- return true;
12
- }
13
- mark(key = "", hold = 1400) {
14
- this.key = key;
15
- if (this.#timer) clearTimeout(this.#timer);
16
- this.#timer = setTimeout(()=>{
17
- this.key = null;
18
- this.#timer = null;
19
- }, hold);
20
- }
21
- reset() {
22
- if (this.#timer) clearTimeout(this.#timer);
23
- this.#timer = null;
24
- this.key = null;
25
- }
7
+ /** The key that last copied, or null. Empty string when a surface has one. */
8
+ key = $state(null);
9
+ #timer = null;
10
+ /** True when anything copied; with a key, true only for that one. */
11
+ is(key = "") {
12
+ return this.key === key;
13
+ }
14
+ /** Writes, then holds the check for `hold` ms. False when the write failed. */
15
+ async copy(value, key = "", hold = 1400) {
16
+ if (!await copyText(value)) return false;
17
+ this.mark(key, hold);
18
+ return true;
19
+ }
20
+ /** The confirmation without the write, for a copy this class did not make. */
21
+ mark(key = "", hold = 1400) {
22
+ this.key = key;
23
+ if (this.#timer) clearTimeout(this.#timer);
24
+ this.#timer = setTimeout(() => {
25
+ this.key = null;
26
+ this.#timer = null;
27
+ }, hold);
28
+ }
29
+ reset() {
30
+ if (this.#timer) clearTimeout(this.#timer);
31
+ this.#timer = null;
32
+ this.key = null;
33
+ }
26
34
  }
@@ -1,55 +1,55 @@
1
1
  export function normalizeQuery(query) {
2
- return query.trim().toLowerCase();
2
+ return query.trim().toLowerCase();
3
3
  }
4
4
  export function containsQuery(value, query) {
5
- if (!query) return true;
6
- return value?.toLowerCase().includes(query) ?? false;
5
+ if (!query) return true;
6
+ return value?.toLowerCase().includes(query) ?? false;
7
7
  }
8
8
  export function anyContainsQuery(values, query) {
9
- return values.some((value)=>containsQuery(value, query));
9
+ return values.some((value) => containsQuery(value, query));
10
10
  }
11
+ // ========================= FUNCTIONS
11
12
  export function toggleSort(current, key) {
12
- if (current?.key !== key) return {
13
- key,
14
- direction: "asc"
15
- };
16
- if (current.direction === "asc") return {
17
- key,
18
- direction: "desc"
19
- };
20
- return null;
13
+ if (current?.key !== key) return {
14
+ key,
15
+ direction: "asc"
16
+ };
17
+ if (current.direction === "asc") return {
18
+ key,
19
+ direction: "desc"
20
+ };
21
+ return null;
21
22
  }
22
23
  export function sortRows(rows, sort) {
23
- if (!sort) return rows;
24
- const { key, direction } = sort;
25
- const multiplier = direction === "asc" ? 1 : -1;
26
- return [
27
- ...rows
28
- ].sort((a, b)=>{
29
- const aVal = a[key];
30
- const bVal = b[key];
31
- if (aVal == null && bVal == null) return 0;
32
- if (aVal == null) return 1;
33
- if (bVal == null) return -1;
34
- if (typeof aVal === "string" && typeof bVal === "string") {
35
- return multiplier * aVal.localeCompare(bVal);
36
- }
37
- if (typeof aVal === "number" && typeof bVal === "number") {
38
- return multiplier * (aVal - bVal);
39
- }
40
- return multiplier * String(aVal).localeCompare(String(bVal));
41
- });
24
+ if (!sort) return rows;
25
+ const { key, direction } = sort;
26
+ const multiplier = direction === "asc" ? 1 : -1;
27
+ return [...rows].sort((a, b) => {
28
+ const aVal = a[key];
29
+ const bVal = b[key];
30
+ if (aVal == null && bVal == null) return 0;
31
+ if (aVal == null) return 1;
32
+ if (bVal == null) return -1;
33
+ if (typeof aVal === "string" && typeof bVal === "string") {
34
+ return multiplier * aVal.localeCompare(bVal);
35
+ }
36
+ if (typeof aVal === "number" && typeof bVal === "number") {
37
+ return multiplier * (aVal - bVal);
38
+ }
39
+ return multiplier * String(aVal).localeCompare(String(bVal));
40
+ });
42
41
  }
43
42
  export function paginateRows(rows, page, pageSize) {
44
- const start = (page - 1) * pageSize;
45
- return rows.slice(start, start + pageSize);
43
+ const start = (page - 1) * pageSize;
44
+ return rows.slice(start, start + pageSize);
46
45
  }
47
46
  export function totalPages(total, pageSize) {
48
- return Math.max(1, Math.ceil(total / pageSize));
47
+ return Math.max(1, Math.ceil(total / pageSize));
49
48
  }
50
49
  export function formatCount(visible, total, label) {
51
- const singular = label.endsWith("s") ? label.slice(0, -1) : label;
52
- const noun = total === 1 ? singular : label;
53
- if (visible === total) return `${total.toLocaleString()} ${noun}`;
54
- return `${visible.toLocaleString()} of ${total.toLocaleString()} ${noun}`;
50
+ const singular = label.endsWith("s") ? label.slice(0, -1) : label;
51
+ // The noun agrees with the total: "0 of 1 item", not "0 of 1 items".
52
+ const noun = total === 1 ? singular : label;
53
+ if (visible === total) return `${total.toLocaleString()} ${noun}`;
54
+ return `${visible.toLocaleString()} of ${total.toLocaleString()} ${noun}`;
55
55
  }
@@ -1,3 +1,7 @@
1
+ /**
2
+ * The documentation anatomy: one page component and the contents rail inside it.
3
+ * A surface supplies a title and a body, and never lays a document out itself.
4
+ */
1
5
  export { default as DocPage } from "../../../src/components/docs/doc-page.svelte";
2
6
  export { default as OnThisPage } from "../../../src/components/docs/on-this-page.svelte";
3
7
  export { ScrollSpy, READ_LINE } from "./scroll-spy.svelte.js";
@@ -1,71 +1,76 @@
1
+ /** How far below the scroller's top edge a heading counts as "reached" and lands. */
1
2
  export const READ_LINE = 80;
2
3
  export class ScrollSpy {
3
- active = $state("");
4
- #scroller = null;
5
- #items = [];
6
- #locate;
7
- #frame = 0;
8
- attach(scroller, items, locate) {
9
- this.#scroller = scroller;
10
- this.#items = items;
11
- this.#locate = locate;
12
- if (!scroller || items.length === 0) {
13
- this.active = "";
14
- return ()=>{};
15
- }
16
- const schedule = ()=>{
17
- this.#frame ||= requestAnimationFrame(()=>{
18
- this.#frame = 0;
19
- this.update();
20
- });
21
- };
22
- scroller.addEventListener("scroll", schedule, {
23
- passive: true
24
- });
25
- const observer = new ResizeObserver(schedule);
26
- observer.observe(scroller);
27
- for (const child of scroller.children)observer.observe(child);
28
- this.update();
29
- return ()=>{
30
- scroller.removeEventListener("scroll", schedule);
31
- observer.disconnect();
32
- if (this.#frame) cancelAnimationFrame(this.#frame);
33
- this.#frame = 0;
34
- };
35
- }
36
- topOf(id) {
37
- const scroller = this.#scroller;
38
- if (!scroller) return null;
39
- if (this.#locate) return this.#locate(id);
40
- const el = scroller.querySelector(`#${CSS.escape(id)}`);
41
- if (!el) return null;
42
- return el.getBoundingClientRect().top - scroller.getBoundingClientRect().top + scroller.scrollTop;
43
- }
44
- update() {
45
- const scroller = this.#scroller;
46
- if (!scroller || this.#items.length === 0) return;
47
- const atEnd = scroller.scrollTop + scroller.clientHeight >= scroller.scrollHeight - 2;
48
- if (atEnd && scroller.scrollTop > 0) {
49
- this.active = this.#items[this.#items.length - 1].id;
50
- return;
51
- }
52
- const line = scroller.scrollTop + READ_LINE + 1;
53
- let current = this.#items[0].id;
54
- for (const item of this.#items){
55
- const top = this.topOf(item.id);
56
- if (top !== null && top <= line) current = item.id;
57
- }
58
- this.active = current;
59
- }
60
- jump(id) {
61
- const scroller = this.#scroller;
62
- const top = this.topOf(id);
63
- if (!scroller || top === null) return;
64
- const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
65
- this.active = id;
66
- scroller.scrollTo({
67
- top: Math.max(0, top - READ_LINE),
68
- behavior: reduced ? "auto" : "smooth"
69
- });
70
- }
4
+ active = $state("");
5
+ #scroller = null;
6
+ #items = [];
7
+ #locate;
8
+ #frame = 0;
9
+ /** Point the spy at a scroller and a list. Returns the cleanup. */
10
+ attach(scroller, items, locate) {
11
+ this.#scroller = scroller;
12
+ this.#items = items;
13
+ this.#locate = locate;
14
+ if (!scroller || items.length === 0) {
15
+ this.active = "";
16
+ return () => {};
17
+ }
18
+ const schedule = () => {
19
+ this.#frame ||= requestAnimationFrame(() => {
20
+ this.#frame = 0;
21
+ this.update();
22
+ });
23
+ };
24
+ scroller.addEventListener("scroll", schedule, { passive: true });
25
+ const observer = new ResizeObserver(schedule);
26
+ observer.observe(scroller);
27
+ // A document that grows after load (a diagram, a chart) moves every
28
+ // heading below it, so watch the content too.
29
+ for (const child of scroller.children) observer.observe(child);
30
+ this.update();
31
+ return () => {
32
+ scroller.removeEventListener("scroll", schedule);
33
+ observer.disconnect();
34
+ if (this.#frame) cancelAnimationFrame(this.#frame);
35
+ this.#frame = 0;
36
+ };
37
+ }
38
+ topOf(id) {
39
+ const scroller = this.#scroller;
40
+ if (!scroller) return null;
41
+ if (this.#locate) return this.#locate(id);
42
+ const el = scroller.querySelector(`#${CSS.escape(id)}`);
43
+ if (!el) return null;
44
+ return el.getBoundingClientRect().top - scroller.getBoundingClientRect().top + scroller.scrollTop;
45
+ }
46
+ update() {
47
+ const scroller = this.#scroller;
48
+ if (!scroller || this.#items.length === 0) return;
49
+ // At the very bottom the last heading wins, or a short final section could
50
+ // never be the current one.
51
+ const atEnd = scroller.scrollTop + scroller.clientHeight >= scroller.scrollHeight - 2;
52
+ if (atEnd && scroller.scrollTop > 0) {
53
+ this.active = this.#items[this.#items.length - 1].id;
54
+ return;
55
+ }
56
+ const line = scroller.scrollTop + READ_LINE + 1;
57
+ let current = this.#items[0].id;
58
+ for (const item of this.#items) {
59
+ const top = this.topOf(item.id);
60
+ if (top !== null && top <= line) current = item.id;
61
+ }
62
+ this.active = current;
63
+ }
64
+ /** Scroll so the heading lands on the read line, instant under reduced motion. */
65
+ jump(id) {
66
+ const scroller = this.#scroller;
67
+ const top = this.topOf(id);
68
+ if (!scroller || top === null) return;
69
+ const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
70
+ this.active = id;
71
+ scroller.scrollTo({
72
+ top: Math.max(0, top - READ_LINE),
73
+ behavior: reduced ? "auto" : "smooth"
74
+ });
75
+ }
71
76
  }
@@ -1,4 +1,7 @@
1
1
  import { cn } from "../../lib/utils.js";
2
+ // The header for each control group in a toolbar popover, in one place so
3
+ // sections can't drift in size or weight.
4
+ // `first:` drops the leading header's top padding, since the popover pads itself.
2
5
  export function filterSectionClass(className) {
3
- return cn("px-2 pt-2 pb-1 text-[11px] font-medium uppercase tracking-wide text-muted-foreground first:pt-1", className);
6
+ return cn("px-2 pt-2 pb-1 text-[11px] font-medium uppercase tracking-wide text-muted-foreground first:pt-1", className);
4
7
  }
@@ -19,6 +19,8 @@ export { filterSectionClass } from "./filter-section.js";
19
19
  export { default as GroupedList } from "../../../src/components/list-page/grouped-list.svelte";
20
20
  export { default as ListPageShell } from "../../../src/components/list-page/list-page-shell.svelte";
21
21
  export { default as MasterDetail } from "../../../src/components/list-page/master-detail.svelte";
22
+ // The segmented control isn't re-exported here. Two import paths drifted, so
23
+ // its one home is `@heyramzi/ui/components/segmented`.
22
24
  export { default as ToolbarSearch } from "../../../src/components/toolbar/toolbar-search.svelte";
23
25
  export { toolbarTriggerClass } from "../toolbar/toolbar-trigger.js";
24
26
  export { listTable } from "./list-table.js";
@@ -1,5 +1,10 @@
1
1
  import { cn } from "../../lib/utils.js";
2
+ /**
3
+ * The list-page table rhythm, Attio's: one 40px row, a hairline under each, a flat hover.
4
+ */
2
5
  export const listTable = {
3
- headRow: (className)=>cn("hover:translate-y-0 hover:bg-transparent hover:shadow-none [&>th]:h-9 [&>th]:border-b [&>th]:border-border", className),
4
- row: (className)=>cn("border-b border-border/50 last:border-b-0 hover:translate-y-0 hover:bg-muted/40 hover:shadow-none [&>td]:h-10 [&>td]:py-0", className)
6
+ /** The header row: 36px, a hairline under it. On the cells: `thead` zeroes its row's border. */
7
+ headRow: (className) => cn("hover:translate-y-0 hover:bg-transparent hover:shadow-none [&>th]:h-9 [&>th]:border-b [&>th]:border-border", className),
8
+ /** A body row. The shared row lifts on hover; a flush list row does not. */
9
+ row: (className) => cn("border-b border-border/50 last:border-b-0 hover:translate-y-0 hover:bg-muted/40 hover:shadow-none [&>td]:h-10 [&>td]:py-0", className)
5
10
  };
@@ -1,3 +1,17 @@
1
+ /**
2
+ * The rail surface of a queue page, passed to `MasterDetail`'s `railClass`.
3
+ * A class, not a wrapper: the shell's `<aside>` already owns the scroll height.
4
+ */
5
+ // Copies of this string drifted to `rounded-lg`, the control radius, not the container one.
1
6
  export const queueRailClass = "flex flex-col overflow-hidden rounded-xl border border-border bg-card";
7
+ /**
8
+ * The rail column track, passed to `MasterDetail`'s `railWidth`. One page ran
9
+ * 20px wider, so the detail pane jumped between pages.
10
+ */
11
+ // 280px fits a two-line thread title. Past 340px the list competes with the record.
2
12
  export const queueRailWidth = "minmax(280px, 340px)";
13
+ /**
14
+ * Context key for the pane `MasterDetail` shows on a phone. Picking a row moves
15
+ * to the record, so `RailRow` reads this instead of each page passing a callback.
16
+ */
3
17
  export const MASTER_DETAIL_PANE = Symbol("master-detail-pane");
@@ -1,8 +1,10 @@
1
1
  import { getContext, setContext } from "svelte";
2
+ // WHY context: the shell decides the strip once and ninety pages draw it, so a prop on every page would be ninety edits.
2
3
  const KEY = Symbol("list-page.section-host");
4
+ /** Call once in the layout that owns the section strip. */
3
5
  export function setSectionHost(host) {
4
- setContext(KEY, host);
6
+ setContext(KEY, host);
5
7
  }
6
8
  export function getSectionHost() {
7
- return getContext(KEY);
9
+ return getContext(KEY);
8
10
  }
@@ -1,5 +1,6 @@
1
+ /** The id `by` rows away from `current`, or null at either end or when `current` isn't listed. */
1
2
  export function stepTo(ids, current, by) {
2
- const index = current ? ids.indexOf(current) : -1;
3
- if (index < 0) return null;
4
- return ids[index + by] ?? null;
3
+ const index = current ? ids.indexOf(current) : -1;
4
+ if (index < 0) return null;
5
+ return ids[index + by] ?? null;
5
6
  }
@@ -0,0 +1 @@
1
+ export {};