@heyramzi/ui 2.9.0 → 2.10.1

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 (313) 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
  313. package/src/status/status-dot.svelte +0 -2
@@ -0,0 +1,66 @@
1
+ import { parseSlideParam } from "./navigation.js";
2
+ export function attachUrlSync(deck, { param = "s", write } = {}) {
3
+ const initial = parseSlideParam(new URL(location.href).searchParams.get(param), deck.entries.length);
4
+ if (initial !== null) deck.go(initial, 0, "remote");
5
+ return deck.listen(({ index })=>{
6
+ const url = new URL(location.href);
7
+ url.searchParams.set(param, String(index + 1));
8
+ if (write) write(url);
9
+ else history.replaceState(history.state, "", url);
10
+ });
11
+ }
12
+ export function attachBridge(deck, { origin = location.origin } = {}) {
13
+ const onMessage = (event)=>{
14
+ if (origin !== "*" && event.origin !== origin) return;
15
+ const data = event.data;
16
+ if (data?.type === "deck:nav") {
17
+ if (data.delta === -1) deck.prev();
18
+ else if (data.delta === 1) deck.next();
19
+ } else if (data?.type === "deck:go" && Number.isInteger(data.index)) {
20
+ deck.go(data.index, Number.isInteger(data.step) ? data.step : 0, "remote");
21
+ }
22
+ };
23
+ window.addEventListener("message", onMessage);
24
+ const stop = deck.listen(({ index, step }, source)=>{
25
+ if (source === "local" && window.parent !== window) {
26
+ window.parent.postMessage({
27
+ type: "deck:change",
28
+ index,
29
+ step,
30
+ total: deck.visible.length
31
+ }, origin);
32
+ }
33
+ });
34
+ return ()=>{
35
+ window.removeEventListener("message", onMessage);
36
+ stop();
37
+ };
38
+ }
39
+ export function attachChannel(deck, name) {
40
+ if (typeof BroadcastChannel === "undefined") return ()=>{};
41
+ const channel = new BroadcastChannel(`heyramzi-slides:${name}`);
42
+ const self = crypto.randomUUID();
43
+ const send = (message)=>channel.postMessage(message);
44
+ const state = ({ index, step } = deck.position)=>({
45
+ type: "state",
46
+ from: self,
47
+ index,
48
+ step
49
+ });
50
+ channel.onmessage = ({ data })=>{
51
+ if (data.from === self) return;
52
+ if (data.type === "hello") send(state());
53
+ else deck.go(data.index, data.step, "remote");
54
+ };
55
+ const stop = deck.listen((position, source)=>{
56
+ if (source === "local") send(state(position));
57
+ });
58
+ send({
59
+ type: "hello",
60
+ from: self
61
+ });
62
+ return ()=>{
63
+ stop();
64
+ channel.close();
65
+ };
66
+ }
@@ -0,0 +1,221 @@
1
+ import { untrack } from "svelte";
2
+ import { fitBox, gridBoxes } from "./layout.js";
3
+ import { keyAction, nextPosition, ordinalOf, prevPosition, progressOf, settle, visibleIndexes } from "./navigation.js";
4
+ import { resolveLabels } from "./labels.js";
5
+ import { DEFAULT_CANVAS } from "./types.js";
6
+ const live = new Set();
7
+ let active = null;
8
+ const claimKeys = (deck)=>{
9
+ active = deck;
10
+ };
11
+ const CONTROLS_ROOM = 56;
12
+ const MORPH_MS = 900;
13
+ const clamp = (value, min, max)=>Math.min(max, Math.max(min, value));
14
+ function isEditable(el) {
15
+ return el.isContentEditable || [
16
+ "INPUT",
17
+ "TEXTAREA",
18
+ "SELECT"
19
+ ].includes(el.tagName);
20
+ }
21
+ const byDocumentOrder = (a, b)=>a.el && b.el ? a.el.compareDocumentPosition(b.el) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1 : 0;
22
+ export class DeckController {
23
+ entries = $state.raw([]);
24
+ view = $state("slide");
25
+ cursor = $state(0);
26
+ direction = $state(1);
27
+ morphing = $state(false);
28
+ size = $state({
29
+ w: 0,
30
+ h: 0
31
+ });
32
+ options;
33
+ #listeners = new Set();
34
+ #timer;
35
+ constructor(options){
36
+ this.options = options;
37
+ }
38
+ canvas = $derived(this.options.canvas ?? DEFAULT_CANVAS);
39
+ labels = $derived(resolveLabels(this.options.labels));
40
+ slides = $derived(this.entries.map((entry)=>({
41
+ steps: this.options.fragments === false ? 0 : entry.steps,
42
+ hidden: entry.hidden
43
+ })));
44
+ visible = $derived(visibleIndexes(this.slides));
45
+ position = $derived(settle(this.slides, {
46
+ index: this.options.index,
47
+ step: this.options.step
48
+ }));
49
+ ordinal = $derived(ordinalOf(this.slides, this.position.index));
50
+ progress = $derived(progressOf(this.slides, this.position));
51
+ current = $derived(this.entries[this.position.index]);
52
+ upcoming = $derived(this.visible.find((i)=>i > this.position.index) ?? null);
53
+ overview = $derived(this.view === "overview");
54
+ revealAll = $derived(this.overview || this.options.fragments === false);
55
+ atStart = $derived(prevPosition(this.slides, this.position, this.options.loop) === null);
56
+ atEnd = $derived(nextPosition(this.slides, this.position, this.options.loop) === null);
57
+ fit = $derived(fitBox(this.size, this.canvas));
58
+ grid = $derived(gridBoxes({
59
+ w: this.size.w,
60
+ h: Math.max(0, this.size.h - (this.options.controls === false ? 0 : CONTROLS_ROOM))
61
+ }, this.canvas, this.visible.length));
62
+ boxOf(index) {
63
+ if (!this.overview) return this.fit;
64
+ return this.grid.boxes[this.visible.indexOf(index)];
65
+ }
66
+ register(entry) {
67
+ untrack(()=>{
68
+ this.entries = [
69
+ ...this.entries,
70
+ entry
71
+ ].sort(byDocumentOrder);
72
+ });
73
+ return ()=>untrack(()=>{
74
+ this.entries = this.entries.filter((candidate)=>candidate !== entry);
75
+ this.#write(this.position);
76
+ });
77
+ }
78
+ listen(listener) {
79
+ this.#listeners.add(listener);
80
+ return ()=>this.#listeners.delete(listener);
81
+ }
82
+ activate() {
83
+ claimKeys(this);
84
+ }
85
+ mount() {
86
+ live.add(this);
87
+ claimKeys(this);
88
+ return ()=>{
89
+ live.delete(this);
90
+ if (active === this) active = null;
91
+ clearTimeout(this.#timer);
92
+ };
93
+ }
94
+ get listening() {
95
+ if (this.options.keyboard === false) return false;
96
+ const eligible = [
97
+ ...live
98
+ ].filter((deck)=>deck.options.keyboard !== false);
99
+ return eligible.length <= 1 || active === this;
100
+ }
101
+ #write(position) {
102
+ this.options.index = position.index;
103
+ this.options.step = position.step;
104
+ }
105
+ commit(target, source = "local") {
106
+ if (!target) return;
107
+ const from = this.position;
108
+ const to = settle(this.slides, target);
109
+ if (to.index === from.index && to.step === from.step) return;
110
+ this.direction = to.index >= from.index ? 1 : -1;
111
+ const apply = ()=>this.#write(to);
112
+ if (to.index !== from.index && this.options.transition) this.options.transition(apply);
113
+ else apply();
114
+ for (const listener of this.#listeners)listener(to, source);
115
+ }
116
+ next() {
117
+ this.commit(nextPosition(this.slides, this.position, this.options.loop));
118
+ }
119
+ prev() {
120
+ this.commit(prevPosition(this.slides, this.position, this.options.loop));
121
+ }
122
+ go(index, step = 0, source = "local") {
123
+ this.commit({
124
+ index,
125
+ step
126
+ }, source);
127
+ }
128
+ toggleOverview(force) {
129
+ const open = force ?? !this.overview;
130
+ if (open === this.overview) return;
131
+ if (open) this.cursor = Math.max(0, this.visible.indexOf(this.position.index));
132
+ this.view = open ? "overview" : "slide";
133
+ this.#morph();
134
+ }
135
+ select(index) {
136
+ this.toggleOverview(false);
137
+ const keep = index === this.position.index;
138
+ this.go(index, keep ? this.position.step : 0);
139
+ }
140
+ #morph() {
141
+ this.morphing = true;
142
+ clearTimeout(this.#timer);
143
+ this.#timer = setTimeout(()=>this.morphing = false, MORPH_MS);
144
+ }
145
+ #moveCursor(to) {
146
+ this.cursor = clamp(to, 0, this.visible.length - 1);
147
+ }
148
+ handleKey(event) {
149
+ if (event.defaultPrevented || !this.listening || event.altKey || event.ctrlKey || event.metaKey) return null;
150
+ const target = event.target instanceof HTMLElement ? event.target : null;
151
+ if (target && isEditable(target)) return null;
152
+ const action = keyAction(event.key, event.shiftKey);
153
+ if (!action) return null;
154
+ if ((event.key === " " || event.key === "Enter") && target?.closest("button, a, summary")) return null;
155
+ const handled = this.overview ? this.#overviewKey(action) : this.#slideKey(action);
156
+ if (!handled) return null;
157
+ event.preventDefault();
158
+ return action;
159
+ }
160
+ #slideKey(action) {
161
+ switch(action){
162
+ case "next":
163
+ this.next();
164
+ return true;
165
+ case "prev":
166
+ this.prev();
167
+ return true;
168
+ case "first":
169
+ this.go(this.visible[0] ?? 0);
170
+ return true;
171
+ case "last":
172
+ this.go(this.visible[this.visible.length - 1] ?? 0);
173
+ return true;
174
+ case "overview":
175
+ this.toggleOverview(true);
176
+ return true;
177
+ case "fullscreen":
178
+ return true;
179
+ case "presenter":
180
+ return !!this.options.presenter;
181
+ default:
182
+ return false;
183
+ }
184
+ }
185
+ #overviewKey(action) {
186
+ const cols = this.grid.cols;
187
+ switch(action){
188
+ case "next":
189
+ this.#moveCursor(this.cursor + 1);
190
+ return true;
191
+ case "prev":
192
+ this.#moveCursor(this.cursor - 1);
193
+ return true;
194
+ case "down":
195
+ this.#moveCursor(this.cursor + cols);
196
+ return true;
197
+ case "up":
198
+ this.#moveCursor(this.cursor - cols);
199
+ return true;
200
+ case "first":
201
+ this.#moveCursor(0);
202
+ return true;
203
+ case "last":
204
+ this.#moveCursor(this.visible.length - 1);
205
+ return true;
206
+ case "select":
207
+ this.select(this.visible[this.cursor] ?? this.position.index);
208
+ return true;
209
+ case "close":
210
+ case "overview":
211
+ this.toggleOverview(false);
212
+ return true;
213
+ case "fullscreen":
214
+ return true;
215
+ case "presenter":
216
+ return !!this.options.presenter;
217
+ default:
218
+ return false;
219
+ }
220
+ }
221
+ }
@@ -0,0 +1,81 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ export const DEFAULT_WAITS = [
4
+ "networkidle",
5
+ "load"
6
+ ];
7
+ export async function gotoSettled(page, url, options = {}) {
8
+ const waits = options.waits ?? DEFAULT_WAITS;
9
+ const timeout = options.timeout ?? 45_000;
10
+ let failure;
11
+ for (const waitUntil of waits){
12
+ try {
13
+ return await page.goto(url, {
14
+ waitUntil,
15
+ timeout
16
+ });
17
+ } catch (error) {
18
+ failure = error;
19
+ }
20
+ }
21
+ throw failure ?? new Error("gotoSettled needs at least one wait condition.");
22
+ }
23
+ export async function capturePage(browser, source, options) {
24
+ const page = await browser.newPage({
25
+ viewport: options.size,
26
+ deviceScaleFactor: options.scale ?? 1
27
+ });
28
+ try {
29
+ if ("url" in source) await gotoSettled(page, source.url, options.goto);
30
+ else await page.setContent(source.html, {
31
+ waitUntil: "load"
32
+ });
33
+ if (options.style) await page.addStyleTag({
34
+ content: options.style
35
+ });
36
+ if (options.wait) await page.waitForTimeout(options.wait);
37
+ const { selector } = options;
38
+ let bytes;
39
+ if (selector) {
40
+ const target = page.locator(selector).first();
41
+ await target.waitFor({
42
+ state: "visible"
43
+ });
44
+ bytes = await target.screenshot({
45
+ type: "png"
46
+ });
47
+ } else {
48
+ bytes = await page.screenshot({
49
+ type: "png",
50
+ fullPage: options.fullPage
51
+ });
52
+ }
53
+ if (options.path) {
54
+ await mkdir(path.dirname(options.path), {
55
+ recursive: true
56
+ });
57
+ await writeFile(options.path, bytes);
58
+ }
59
+ return bytes;
60
+ } finally{
61
+ await page.close();
62
+ }
63
+ }
64
+ export function frameName(index, digits = 2, extension = "png") {
65
+ return `${String(index + 1).padStart(digits, "0")}.${extension}`;
66
+ }
67
+ export async function captureEach(elements, dir, options = {}) {
68
+ await mkdir(dir, {
69
+ recursive: true
70
+ });
71
+ const count = await elements.count();
72
+ const files = [];
73
+ for(let index = 0; index < count; index++){
74
+ const file = path.join(dir, frameName(index, options.digits));
75
+ await elements.nth(index).screenshot({
76
+ path: file
77
+ });
78
+ files.push(file);
79
+ }
80
+ return files;
81
+ }
@@ -0,0 +1,85 @@
1
+ import { spawn } from "node:child_process";
2
+ import { mkdir, rm } from "node:fs/promises";
3
+ import path from "node:path";
4
+ export function runFfmpeg(args, ffmpeg = "ffmpeg") {
5
+ return new Promise((resolve, reject)=>{
6
+ const child = spawn(ffmpeg, args, {
7
+ stdio: [
8
+ "ignore",
9
+ "ignore",
10
+ "pipe"
11
+ ]
12
+ });
13
+ let stderr = "";
14
+ child.stderr.on("data", (chunk)=>{
15
+ stderr += chunk;
16
+ });
17
+ child.on("error", (error)=>reject(new Error(`${ffmpeg} could not be started (${error.message}). Video export needs ffmpeg on PATH, or pass its path.`)));
18
+ child.on("close", (code)=>code === 0 ? resolve() : reject(new Error(`${ffmpeg} exited ${code}:\n${stderr.slice(-1500)}`)));
19
+ });
20
+ }
21
+ export function encodeArgs(dir, outFile, options) {
22
+ return [
23
+ "-y",
24
+ "-hide_banner",
25
+ "-loglevel",
26
+ "error",
27
+ "-framerate",
28
+ String(options.fps),
29
+ "-i",
30
+ path.join(dir, options.pattern ?? "%04d.png"),
31
+ "-an",
32
+ "-vf",
33
+ "format=yuv420p",
34
+ "-c:v",
35
+ "libx264",
36
+ "-preset",
37
+ options.preset ?? "slow",
38
+ "-crf",
39
+ String(options.crf ?? 23),
40
+ "-movflags",
41
+ "+faststart",
42
+ outFile
43
+ ];
44
+ }
45
+ export async function encodeFrames(dir, outFile, options) {
46
+ await mkdir(path.dirname(outFile), {
47
+ recursive: true
48
+ });
49
+ await runFfmpeg(encodeArgs(dir, outFile, options), options.ffmpeg);
50
+ }
51
+ export function loopFrameCount(seconds, fps) {
52
+ return Math.max(1, Math.round(seconds * fps));
53
+ }
54
+ export async function renderLoop(outFile, options) {
55
+ const fps = options.fps ?? 30;
56
+ const seconds = options.seconds ?? 2.5;
57
+ const frames = loopFrameCount(seconds, fps);
58
+ const work = `${outFile}.frames`;
59
+ await mkdir(work, {
60
+ recursive: true
61
+ });
62
+ try {
63
+ for(let index = 0; index < frames; index++){
64
+ await options.drawFrame({
65
+ index,
66
+ t: index / frames,
67
+ file: path.join(work, `${String(index).padStart(4, "0")}.png`)
68
+ });
69
+ }
70
+ await encodeFrames(work, outFile, {
71
+ ...options,
72
+ fps
73
+ });
74
+ } finally{
75
+ await rm(work, {
76
+ recursive: true,
77
+ force: true
78
+ });
79
+ }
80
+ return {
81
+ file: outFile,
82
+ frames,
83
+ seconds
84
+ };
85
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./capture.js";
2
+ export * from "./ffmpeg.js";
3
+ export * from "./launch.js";
4
+ export * from "./pdf.js";
@@ -0,0 +1,8 @@
1
+ export async function launchChromium(chromium) {
2
+ return chromium.launch({
3
+ headless: true
4
+ }).catch(()=>chromium.launch({
5
+ headless: true,
6
+ channel: "chrome"
7
+ }));
8
+ }
@@ -0,0 +1,78 @@
1
+ import { readFile } from "node:fs/promises";
2
+ export const RESET_CSS = `*{margin:0;padding:0;box-sizing:border-box;}
3
+ html{-webkit-print-color-adjust:exact;print-color-adjust:exact;}`;
4
+ export function pageRule(size) {
5
+ return `@page{size:${size.width}px ${size.height}px;margin:0;}`;
6
+ }
7
+ export function imagePagesCss(size, background) {
8
+ return `${RESET_CSS}
9
+ ${background ? `body{background:${background};}\n` : ""}${pageRule(size)}
10
+ .page{display:block;width:${size.width}px;height:${size.height}px;break-after:page;}
11
+ .page:last-child{break-after:auto;}`;
12
+ }
13
+ export function imagePagesHtml(dataUrls, size, background) {
14
+ const pages = dataUrls.map((dataUrl)=>`<img class="page" src="${dataUrl}" alt="">`).join("");
15
+ return `<!doctype html><html><head><meta charset="utf-8"><style>
16
+ ${imagePagesCss(size, background)}
17
+ </style></head><body>${pages}</body></html>`;
18
+ }
19
+ export function countPdfPages(pdf) {
20
+ return (Buffer.from(pdf).toString("latin1").match(/\/Type\s*\/Page[^s]/g) ?? []).length;
21
+ }
22
+ export async function printPage(page, options = {}) {
23
+ const { ready } = options;
24
+ if (typeof ready === "string") {
25
+ await page.waitForSelector(ready, {
26
+ timeout: options.readyTimeout ?? 60_000
27
+ });
28
+ } else if (ready) {
29
+ await ready(page);
30
+ }
31
+ await page.evaluate(()=>document.fonts.ready.then(()=>undefined));
32
+ if (options.settle) await page.waitForTimeout(options.settle);
33
+ const brokenImages = await page.evaluate(()=>[
34
+ ...document.images
35
+ ].filter((img)=>!img.complete || img.naturalWidth === 0).map((img)=>img.src));
36
+ const { size } = options;
37
+ const pdf = await page.pdf({
38
+ path: options.path,
39
+ printBackground: true,
40
+ ...size ? {
41
+ width: `${size.width}px`,
42
+ height: `${size.height}px`
43
+ } : {
44
+ preferCSSPageSize: true
45
+ },
46
+ pageRanges: options.pageRanges
47
+ });
48
+ return {
49
+ pdf,
50
+ pages: countPdfPages(pdf),
51
+ brokenImages
52
+ };
53
+ }
54
+ export async function printUrl(page, url, options = {}) {
55
+ const response = await page.goto(url, {
56
+ waitUntil: options.waitUntil ?? "load"
57
+ });
58
+ options.check?.(response, page);
59
+ return printPage(page, options);
60
+ }
61
+ export async function printHtml(page, html, options = {}) {
62
+ await page.setContent(html, {
63
+ waitUntil: options.waitUntil ?? "load"
64
+ });
65
+ return printPage(page, options);
66
+ }
67
+ export async function imagesToPdf(page, images, options) {
68
+ if (images.length === 0) throw new Error("imagesToPdf needs at least one image.");
69
+ const dataUrls = await Promise.all(images.map(async (image)=>{
70
+ const bytes = typeof image === "string" ? await readFile(image) : image;
71
+ return `data:image/png;base64,${Buffer.from(bytes).toString("base64")}`;
72
+ }));
73
+ const { pdf } = await printHtml(page, imagePagesHtml(dataUrls, options.size, options.background), {
74
+ size: options.size,
75
+ pageRanges: `1-${images.length}`
76
+ });
77
+ return pdf;
78
+ }
File without changes
@@ -0,0 +1,7 @@
1
+ export { default as Deck } from "../../src/slides/Deck.svelte";
2
+ export { default as Slide } from "../../src/slides/Slide.svelte";
3
+ export { default as Fragment } from "../../src/slides/Fragment.svelte";
4
+ export { default as Presenter } from "../../src/slides/Presenter.svelte";
5
+ export { DeckController } from "./deck-controller.svelte.js";
6
+ export { defaultLabels } from "./labels.js";
7
+ export { DEFAULT_CANVAS } from "./types.js";
@@ -0,0 +1,26 @@
1
+ export const defaultLabels = {
2
+ deck: "Slide deck",
3
+ deckRole: "presentation",
4
+ slideRole: "slide",
5
+ previous: "Previous slide",
6
+ next: "Next slide",
7
+ fullscreen: "Toggle fullscreen",
8
+ overview: "Slide overview",
9
+ presenter: "Open presenter view",
10
+ progress: "Deck progress",
11
+ slide: (n, of)=>`Slide ${n} of ${of}`,
12
+ upcoming: "Next",
13
+ current: "Now",
14
+ notes: "Notes",
15
+ noNotes: "No notes for this slide.",
16
+ endOfDeck: "End of deck.",
17
+ timer: "Elapsed time",
18
+ startTimer: "Start timer",
19
+ pauseTimer: "Pause timer",
20
+ resetTimer: "Reset timer",
21
+ unknownType: (type)=>`Unknown slide type: ${type}`
22
+ };
23
+ export const resolveLabels = (labels)=>({
24
+ ...defaultLabels,
25
+ ...labels
26
+ });
@@ -0,0 +1,47 @@
1
+ export function fitBox(view, canvas) {
2
+ const scale = Math.max(0, Math.min(view.w / canvas.w, view.h / canvas.h));
3
+ return {
4
+ x: (view.w - canvas.w * scale) / 2,
5
+ y: (view.h - canvas.h * scale) / 2,
6
+ scale
7
+ };
8
+ }
9
+ function scaleFor(cols, count, view, canvas, pad, gap) {
10
+ const rows = Math.ceil(count / cols);
11
+ const byWidth = (view.w - 2 * pad - (cols - 1) * gap) / cols / canvas.w;
12
+ const byHeight = (view.h - 2 * pad - (rows - 1) * gap) / rows / canvas.h;
13
+ return Math.min(byWidth, byHeight);
14
+ }
15
+ export function gridBoxes(view, canvas, count) {
16
+ if (count === 0 || view.w <= 0 || view.h <= 0) return {
17
+ cols: 1,
18
+ boxes: []
19
+ };
20
+ const pad = Math.min(56, Math.max(16, view.w * 0.04));
21
+ const gap = Math.max(26, pad * 0.6);
22
+ let cols = 1;
23
+ let scale = scaleFor(1, count, view, canvas, pad, gap);
24
+ for(let c = 2; c <= count; c++){
25
+ const candidate = scaleFor(c, count, view, canvas, pad, gap);
26
+ if (candidate > scale) [cols, scale] = [
27
+ c,
28
+ candidate
29
+ ];
30
+ }
31
+ const cellW = canvas.w * scale;
32
+ const cellH = canvas.h * scale;
33
+ const rows = Math.ceil(count / cols);
34
+ const left = (view.w - (cols * cellW + (cols - 1) * gap)) / 2;
35
+ const top = (view.h - (rows * cellH + (rows - 1) * gap)) / 2;
36
+ const boxes = Array.from({
37
+ length: count
38
+ }, (_, i)=>({
39
+ x: left + i % cols * (cellW + gap),
40
+ y: top + Math.floor(i / cols) * (cellH + gap),
41
+ scale
42
+ }));
43
+ return {
44
+ cols,
45
+ boxes
46
+ };
47
+ }
@@ -0,0 +1,9 @@
1
+ export const defineSlideType = (definition)=>definition;
2
+ export function defineRegistry(types) {
3
+ const registry = {};
4
+ for (const slide of types){
5
+ if (slide.type in registry) throw new Error(`Slide type "${slide.type}" is registered twice.`);
6
+ registry[slide.type] = slide;
7
+ }
8
+ return registry;
9
+ }