@heyramzi/ui 1.54.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (361) hide show
  1. package/COMPONENTS.md +3 -2
  2. package/README.md +34 -9
  3. package/package.json +198 -6
  4. package/src/ai/generation-progress/GenerationProgress.svelte +12 -12
  5. package/src/ambient.d.ts +8 -0
  6. package/src/brand/brand.test.ts +67 -0
  7. package/src/brand/index.ts +137 -0
  8. package/src/canvas/README.md +236 -0
  9. package/src/canvas/card/card-frame.svelte +61 -0
  10. package/src/canvas/card/card.css +39 -0
  11. package/src/canvas/card/resize-handles.svelte +119 -0
  12. package/src/canvas/card/resize.ts +33 -0
  13. package/src/canvas/chrome/chrome-zoom.svelte +120 -0
  14. package/src/canvas/chrome/chrome.css +231 -0
  15. package/src/canvas/chrome/lock-badge.svelte +18 -0
  16. package/src/canvas/chrome/side-handles.svelte +30 -0
  17. package/src/canvas/chrome/size-chip.svelte +45 -0
  18. package/src/canvas/chrome/snap-guides.svelte +69 -0
  19. package/src/canvas/components/icons/ToolIcon.svelte +30 -0
  20. package/src/canvas/components/shared/svg/PreviewSvg.svelte +67 -0
  21. package/src/canvas/components/shared/svg/ShapeOutline.svelte +76 -0
  22. package/src/canvas/components/shared/svg/StrokeNode.svelte +69 -0
  23. package/src/canvas/components/shared/svg/TextRule.svelte +37 -0
  24. package/src/canvas/edges/bend-handle.svelte +76 -0
  25. package/src/canvas/edges/edge-gradient.svelte +30 -0
  26. package/src/canvas/edges/edge-path.test.ts +58 -0
  27. package/src/canvas/edges/edge-path.ts +293 -0
  28. package/src/canvas/edges/gradient-edge.svelte +43 -0
  29. package/src/canvas/editor/context-menu.test.ts +65 -0
  30. package/src/canvas/editor/context-menu.ts +48 -0
  31. package/src/canvas/editor/dispatch.svelte.test.ts +243 -0
  32. package/src/canvas/editor/dispatch.svelte.ts +205 -0
  33. package/src/canvas/editor/editor-context.ts +62 -0
  34. package/src/canvas/editor/editor-mode.ts +67 -0
  35. package/src/canvas/editor/frame.ts +98 -0
  36. package/src/canvas/editor/hand-context.ts +15 -0
  37. package/src/canvas/editor/history-core.test.ts +239 -0
  38. package/src/canvas/editor/history-core.ts +247 -0
  39. package/src/canvas/editor/keymap.test.ts +161 -0
  40. package/src/canvas/editor/keymap.ts +151 -0
  41. package/src/canvas/editor/layout-actions.test.ts +156 -0
  42. package/src/canvas/editor/layout-actions.ts +219 -0
  43. package/src/canvas/editor/lock.test.ts +68 -0
  44. package/src/canvas/editor/lock.ts +87 -0
  45. package/src/canvas/editor/rebase.test.ts +208 -0
  46. package/src/canvas/editor/rebase.ts +272 -0
  47. package/src/canvas/editor/style.ts +184 -0
  48. package/src/canvas/editor/text-field-context.ts +41 -0
  49. package/src/canvas/editor/z-order.test.ts +75 -0
  50. package/src/canvas/editor/z-order.ts +87 -0
  51. package/src/canvas/headless/alignment.test.ts +59 -0
  52. package/src/canvas/headless/alignment.ts +23 -0
  53. package/src/canvas/headless/break-apart-find.ts +418 -0
  54. package/src/canvas/headless/break-apart-pieces.ts +104 -0
  55. package/src/canvas/headless/break-apart-plan.ts +439 -0
  56. package/src/canvas/headless/break-apart-style.ts +106 -0
  57. package/src/canvas/headless/break-apart.ts +244 -0
  58. package/src/canvas/headless/canvas-cleanup.test.ts +181 -0
  59. package/src/canvas/headless/canvas-cleanup.ts +102 -0
  60. package/src/canvas/headless/edge-anchors.test.ts +70 -0
  61. package/src/canvas/headless/edge-anchors.ts +143 -0
  62. package/src/canvas/headless/edge-memo.test.ts +58 -0
  63. package/src/canvas/headless/edge-memo.ts +32 -0
  64. package/src/canvas/headless/edge-serialization.test.ts +55 -0
  65. package/src/canvas/headless/edge-serialization.ts +32 -0
  66. package/src/canvas/headless/figure-geometry.ts +293 -0
  67. package/src/canvas/headless/fit-viewport.test.ts +85 -0
  68. package/src/canvas/headless/fit-viewport.ts +137 -0
  69. package/src/canvas/headless/grid.test.ts +34 -0
  70. package/src/canvas/headless/grid.ts +94 -0
  71. package/src/canvas/headless/icon-node.test.ts +23 -0
  72. package/src/canvas/headless/icon-node.ts +66 -0
  73. package/src/canvas/headless/index.ts +9 -0
  74. package/src/canvas/headless/linkify.test.ts +23 -0
  75. package/src/canvas/headless/linkify.ts +25 -0
  76. package/src/canvas/headless/pen-marks.ts +133 -0
  77. package/src/canvas/headless/sketch.test.ts +99 -0
  78. package/src/canvas/headless/sketch.ts +302 -0
  79. package/src/canvas/headless/snap-guides.test.ts +61 -0
  80. package/src/canvas/headless/snap-guides.ts +146 -0
  81. package/src/canvas/headless/svg-figure.test.ts +49 -0
  82. package/src/canvas/headless/svg-figure.ts +176 -0
  83. package/src/canvas/headless/table-layout.test.ts +158 -0
  84. package/src/canvas/headless/table-layout.ts +309 -0
  85. package/src/canvas/index.ts +35 -0
  86. package/src/canvas/lod/lod-mode.svelte.ts +30 -0
  87. package/src/canvas/lod/lod-switch.svelte +140 -0
  88. package/src/canvas/lod/render-all.svelte.ts +52 -0
  89. package/src/canvas/ops/arrange.ts +108 -0
  90. package/src/canvas/ops/ops.test.ts +98 -0
  91. package/src/canvas/ops/ops.ts +347 -0
  92. package/src/canvas/pen/PenField.svelte +128 -0
  93. package/src/canvas/pen/PenField.test.ts +87 -0
  94. package/src/canvas/pen/PenOverlay.svelte +172 -0
  95. package/src/canvas/pen/ShapeNode.svelte +64 -0
  96. package/src/canvas/pen/TextNode.svelte +97 -0
  97. package/src/canvas/pen/colors.ts +28 -0
  98. package/src/canvas/pen/eraser.ts +46 -0
  99. package/src/canvas/pen/geometry.ts +148 -0
  100. package/src/canvas/pen/host-field.test.svelte +13 -0
  101. package/src/canvas/pen/index.ts +40 -0
  102. package/src/canvas/pen/ops.ts +16 -0
  103. package/src/canvas/pen/pen-session.ts +177 -0
  104. package/src/canvas/pen/pen.css +149 -0
  105. package/src/canvas/pen/plugin.ts +139 -0
  106. package/src/canvas/pen/shapes.ts +104 -0
  107. package/src/canvas/pen/stroke-path.ts +54 -0
  108. package/src/canvas/pen/types.ts +96 -0
  109. package/src/canvas/pen/ui.ts +26 -0
  110. package/src/canvas/plugin.test.ts +45 -0
  111. package/src/canvas/plugin.ts +159 -0
  112. package/src/canvas/screen/GlyphIcon.svelte +25 -0
  113. package/src/canvas/screen/PartView.svelte +176 -0
  114. package/src/canvas/screen/ScreenBody.svelte +142 -0
  115. package/src/canvas/screen/ScreenCard.svelte +32 -0
  116. package/src/canvas/screen/glyphs/button.svg +1 -0
  117. package/src/canvas/screen/glyphs/close.svg +1 -0
  118. package/src/canvas/screen/glyphs/down.svg +1 -0
  119. package/src/canvas/screen/glyphs/header.svg +1 -0
  120. package/src/canvas/screen/glyphs/hills.svg +1 -0
  121. package/src/canvas/screen/glyphs/image.svg +1 -0
  122. package/src/canvas/screen/glyphs/input.svg +1 -0
  123. package/src/canvas/screen/glyphs/list.svg +1 -0
  124. package/src/canvas/screen/glyphs/modal.svg +1 -0
  125. package/src/canvas/screen/glyphs/plus.svg +1 -0
  126. package/src/canvas/screen/glyphs/up.svg +1 -0
  127. package/src/canvas/screen/glyphs.ts +40 -0
  128. package/src/canvas/screen/labels.ts +41 -0
  129. package/src/canvas/screen/parts.ts +164 -0
  130. package/src/canvas/screen/plugin.ts +67 -0
  131. package/src/canvas/screen/screen.css +593 -0
  132. package/src/canvas/screen/types.ts +47 -0
  133. package/src/canvas/screen/ui.ts +13 -0
  134. package/src/canvas/test-utils.ts +18 -0
  135. package/src/canvas/types.ts +53 -0
  136. package/src/canvas/ui/CanvasToolbar.svelte +169 -0
  137. package/src/canvas/ui/FrameLayer.svelte +116 -0
  138. package/src/canvas/ui/PenLayer.svelte +39 -0
  139. package/src/canvas/ui/StylePanel.svelte +268 -0
  140. package/src/canvas/ui/canvas-grid.svelte +156 -0
  141. package/src/canvas/ui/canvas-ui.css +350 -0
  142. package/src/canvas/ui/forward-wheel.ts +29 -0
  143. package/src/canvas/ui/icons.ts +124 -0
  144. package/src/canvas/ui/place-panel.ts +80 -0
  145. package/src/canvas/ui/shortcut-tip.test.ts +16 -0
  146. package/src/canvas/ui/shortcut-tip.ts +17 -0
  147. package/src/canvas/ui/tools.ts +146 -0
  148. package/src/charts/README.md +104 -0
  149. package/src/charts/ambient.d.ts +4 -0
  150. package/src/charts/animated-value.css +7 -0
  151. package/src/charts/animated-value.svelte +110 -0
  152. package/src/charts/area-chart.css +29 -0
  153. package/src/charts/area-chart.svelte +204 -0
  154. package/src/charts/bar-chart.css +119 -0
  155. package/src/charts/bar-chart.svelte +275 -0
  156. package/src/charts/charts.css +201 -0
  157. package/src/charts/crosshair.svelte +35 -0
  158. package/src/charts/dashboard/chart.ts +5 -0
  159. package/src/charts/dashboard/dashboard-empty.svelte +55 -0
  160. package/src/charts/dashboard/dashboard-section.svelte +58 -0
  161. package/src/charts/dashboard/data-panel.svelte +81 -0
  162. package/src/charts/dashboard/data-sort-head.svelte +102 -0
  163. package/src/charts/dashboard/data-table.svelte +278 -0
  164. package/src/charts/dashboard/data-table.ts +26 -0
  165. package/src/charts/dashboard/index.ts +25 -0
  166. package/src/charts/dashboard/panel-grid.svelte +43 -0
  167. package/src/charts/dashboard/period-filter.svelte +53 -0
  168. package/src/charts/dashboard/rating-chip.svelte +35 -0
  169. package/src/charts/dashboard/stat-band.svelte +47 -0
  170. package/src/charts/dashboard/stat-tile.svelte +176 -0
  171. package/src/charts/dashboard/windows.ts +8 -0
  172. package/src/charts/donut-chart.css +56 -0
  173. package/src/charts/donut-chart.svelte +158 -0
  174. package/src/charts/edge-fade.svelte +43 -0
  175. package/src/charts/edge-fade.ts +16 -0
  176. package/src/charts/figure.css +100 -0
  177. package/src/charts/figure.svelte +122 -0
  178. package/src/charts/gauge-chart.css +94 -0
  179. package/src/charts/gauge-chart.svelte +302 -0
  180. package/src/charts/hover-band.test.ts +30 -0
  181. package/src/charts/hover-band.ts +21 -0
  182. package/src/charts/hover-focus.svelte +85 -0
  183. package/src/charts/index.ts +31 -0
  184. package/src/charts/legend.css +136 -0
  185. package/src/charts/legend.svelte +112 -0
  186. package/src/charts/line-chart.css +26 -0
  187. package/src/charts/line-chart.svelte +256 -0
  188. package/src/charts/motion.ts +19 -0
  189. package/src/charts/number-chart.css +179 -0
  190. package/src/charts/number-chart.svelte +187 -0
  191. package/src/charts/primitives/chart-container.svelte +57 -0
  192. package/src/charts/primitives/chart-style.svelte +36 -0
  193. package/src/charts/primitives/chart-tooltip.svelte +161 -0
  194. package/src/charts/primitives/chart-utils.ts +54 -0
  195. package/src/charts/primitives/index.ts +11 -0
  196. package/src/charts/reference-line.css +18 -0
  197. package/src/charts/reference-line.svelte +31 -0
  198. package/src/charts/reveal.svelte +61 -0
  199. package/src/charts/ring-chart.css +104 -0
  200. package/src/charts/ring-chart.svelte +166 -0
  201. package/src/charts/sparkline.css +45 -0
  202. package/src/charts/sparkline.svelte +141 -0
  203. package/src/charts/stat-card.css +105 -0
  204. package/src/charts/stat-card.svelte +267 -0
  205. package/src/charts/time-series-chart.css +28 -0
  206. package/src/charts/time-series-chart.svelte +179 -0
  207. package/src/charts/trend-pill.css +38 -0
  208. package/src/charts/trend-pill.svelte +48 -0
  209. package/src/components/copy/copy-button.svelte +1 -1
  210. package/src/components/docs/doc-page.svelte +1 -1
  211. package/src/components/docs/on-this-page.svelte +2 -2
  212. package/src/components/list-page/empty-state.svelte +1 -1
  213. package/src/components/list-page/filter-popover.svelte +1 -1
  214. package/src/components/list-page/section-label.svelte +1 -1
  215. package/src/components/segmented/segment.svelte +4 -4
  216. package/src/components/theme/theme-provider.svelte +6 -4
  217. package/src/components/toolbar/index.ts +5 -0
  218. package/src/components/toolbar/toolbar.svelte +168 -0
  219. package/src/components/toolbar/toolbar.test.ts +66 -0
  220. package/src/editor/README.md +58 -0
  221. package/src/editor/ambient.d.ts +1 -0
  222. package/src/editor/block-id.ts +77 -0
  223. package/src/editor/block-ops.test.ts +237 -0
  224. package/src/editor/block-ops.ts +157 -0
  225. package/src/editor/content.css +418 -0
  226. package/src/editor/editor-toolbar.svelte +303 -0
  227. package/src/editor/editor.svelte +462 -0
  228. package/src/editor/extensions.ts +145 -0
  229. package/src/editor/image-toolbar.svelte +123 -0
  230. package/src/editor/index.ts +17 -0
  231. package/src/editor/inline-editor.svelte +261 -0
  232. package/src/editor/markdown-escaping.test.ts +158 -0
  233. package/src/editor/markdown-marks.test.ts +89 -0
  234. package/src/editor/markdown-test-helpers.ts +13 -0
  235. package/src/editor/markdown-youtube.test.ts +93 -0
  236. package/src/editor/markdown.test.ts +250 -0
  237. package/src/editor/markdown.ts +511 -0
  238. package/src/editor/renderer.svelte +92 -0
  239. package/src/editor/serializer.test.ts +133 -0
  240. package/src/editor/serializer.ts +370 -0
  241. package/src/editor/slash-items.ts +115 -0
  242. package/src/editor/slash-menu.svelte +159 -0
  243. package/src/editor/youtube-url.ts +35 -0
  244. package/src/effects/interactive-hover-button/InteractiveHoverButton.svelte +13 -13
  245. package/src/lib/result.ts +19 -0
  246. package/src/onboarding/README.md +120 -0
  247. package/src/onboarding/define-tours.test.ts +50 -0
  248. package/src/onboarding/define-tours.ts +31 -0
  249. package/src/onboarding/demo.svelte +86 -0
  250. package/src/onboarding/index.ts +12 -0
  251. package/src/onboarding/onboarding-provider.svelte +120 -0
  252. package/src/onboarding/onboarding.svelte.ts +24 -0
  253. package/src/onboarding/pick.test.ts +79 -0
  254. package/src/onboarding/pick.ts +36 -0
  255. package/src/onboarding/stores.test.ts +103 -0
  256. package/src/onboarding/stores.ts +58 -0
  257. package/src/onboarding/tour-card.svelte +70 -0
  258. package/src/onboarding/tour-runner.svelte +197 -0
  259. package/src/onboarding/types.ts +34 -0
  260. package/src/onboarding/wait.ts +29 -0
  261. package/src/shadcn/command/command-input.svelte +1 -1
  262. package/src/slides/Controls.svelte +138 -0
  263. package/src/slides/Deck.svelte +271 -0
  264. package/src/slides/Fragment.svelte +71 -0
  265. package/src/slides/Overview.svelte +113 -0
  266. package/src/slides/Presenter.svelte +292 -0
  267. package/src/slides/README.md +89 -0
  268. package/src/slides/Slide.svelte +179 -0
  269. package/src/slides/SlideList.svelte +19 -0
  270. package/src/slides/ambient.d.ts +1 -0
  271. package/src/slides/bridges.ts +69 -0
  272. package/src/slides/deck-controller.svelte.ts +292 -0
  273. package/src/slides/demo-slides.svelte +358 -0
  274. package/src/slides/demo.svelte +176 -0
  275. package/src/slides/export/capture.ts +113 -0
  276. package/src/slides/export/ffmpeg.ts +120 -0
  277. package/src/slides/export/index.ts +9 -0
  278. package/src/slides/export/launch.ts +10 -0
  279. package/src/slides/export/pdf.ts +152 -0
  280. package/src/slides/export/types.ts +48 -0
  281. package/src/slides/index.ts +19 -0
  282. package/src/slides/labels.ts +48 -0
  283. package/src/slides/layout.ts +51 -0
  284. package/src/slides/model.ts +47 -0
  285. package/src/slides/navigation.ts +100 -0
  286. package/src/slides/theme.css +205 -0
  287. package/src/slides/types.ts +86 -0
  288. package/src/tasks/README.md +312 -0
  289. package/src/tasks/ambient.d.ts +3 -0
  290. package/src/tasks/board-order.ts +137 -0
  291. package/src/tasks/dates.ts +31 -0
  292. package/src/tasks/define-statuses.ts +32 -0
  293. package/src/tasks/demo.svelte +389 -0
  294. package/src/tasks/detail/detail-demo.svelte +186 -0
  295. package/src/tasks/detail/detail-types.ts +54 -0
  296. package/src/tasks/detail/task-chip.svelte +28 -0
  297. package/src/tasks/detail/task-detail.svelte +315 -0
  298. package/src/tasks/detail/task-property.svelte +17 -0
  299. package/src/tasks/detail/task-thread.svelte +258 -0
  300. package/src/tasks/gantt/gantt-bar.svelte +243 -0
  301. package/src/tasks/gantt/gantt-grid.svelte +94 -0
  302. package/src/tasks/gantt/gantt-group-row.svelte +55 -0
  303. package/src/tasks/gantt/gantt-header.svelte +71 -0
  304. package/src/tasks/gantt/gantt-task-row.svelte +186 -0
  305. package/src/tasks/gantt/gantt.css +22 -0
  306. package/src/tasks/gantt/gantt.test.ts +77 -0
  307. package/src/tasks/gantt/logic.test.ts +163 -0
  308. package/src/tasks/gantt/logic.ts +248 -0
  309. package/src/tasks/inbox/inbox-card.svelte +283 -0
  310. package/src/tasks/inbox/inbox-draft.svelte +84 -0
  311. package/src/tasks/inbox/inbox-logic.ts +55 -0
  312. package/src/tasks/inbox/inbox-statuses.ts +48 -0
  313. package/src/tasks/inbox/inbox-trace.svelte +45 -0
  314. package/src/tasks/inbox/inbox-types.ts +108 -0
  315. package/src/tasks/inbox/inbox.svelte +203 -0
  316. package/src/tasks/inbox/index.ts +23 -0
  317. package/src/tasks/index.ts +85 -0
  318. package/src/tasks/kanban-board.svelte +561 -0
  319. package/src/tasks/kanban-column.svelte +117 -0
  320. package/src/tasks/kanban-drag.ts +38 -0
  321. package/src/tasks/kanban-types.ts +65 -0
  322. package/src/tasks/presets/sales.ts +16 -0
  323. package/src/tasks/record/demo-data.ts +286 -0
  324. package/src/tasks/record/field-section.svelte +53 -0
  325. package/src/tasks/record/fields.ts +142 -0
  326. package/src/tasks/record/format.ts +87 -0
  327. package/src/tasks/record/gutter.ts +2 -0
  328. package/src/tasks/record/index.ts +54 -0
  329. package/src/tasks/record/info-list.svelte +316 -0
  330. package/src/tasks/record/kinds.ts +42 -0
  331. package/src/tasks/record/logic.ts +324 -0
  332. package/src/tasks/record/next-up.svelte +70 -0
  333. package/src/tasks/record/project-list.svelte +133 -0
  334. package/src/tasks/record/record-demo.svelte +324 -0
  335. package/src/tasks/record/record-header.svelte +70 -0
  336. package/src/tasks/record/record-shell.svelte +138 -0
  337. package/src/tasks/record/record-stats.svelte +48 -0
  338. package/src/tasks/record/record-timeline.svelte +231 -0
  339. package/src/tasks/record/record.test.ts +350 -0
  340. package/src/tasks/record/steps-rail.svelte +474 -0
  341. package/src/tasks/record/types.ts +280 -0
  342. package/src/tasks/record/where-we-are.svelte +121 -0
  343. package/src/tasks/reorder.ts +19 -0
  344. package/src/tasks/side-panel-types.ts +16 -0
  345. package/src/tasks/side-panel.svelte +193 -0
  346. package/src/tasks/status-badge.svelte +63 -0
  347. package/src/tasks/status-dot.svelte +228 -0
  348. package/src/tasks/status-icon.svelte +30 -0
  349. package/src/tasks/status-types.ts +40 -0
  350. package/src/tasks/task-gantt.svelte +230 -0
  351. package/src/tasks/task-list.svelte +470 -0
  352. package/src/tasks/task-types.ts +33 -0
  353. package/src/tasks/time/duration.ts +69 -0
  354. package/src/tasks/time/time-chip.svelte +62 -0
  355. package/src/tasks/time/time-dot.svelte +58 -0
  356. package/src/tasks/time/time-ring.svelte +74 -0
  357. package/src/tasks/time/time-tracker.svelte +565 -0
  358. package/src/tasks/time/time-types.ts +74 -0
  359. package/src/tasks/use-kanban-dnd.svelte.ts +173 -0
  360. package/src/tokens/base.css +5 -5
  361. package/src/tokens/palette.css +115 -120
@@ -0,0 +1,176 @@
1
+ <script lang="ts">
2
+ import Deck from './Deck.svelte';
3
+ import type { DeckController } from './deck-controller.svelte.js';
4
+ import DemoSlides, { demoStyle } from './demo-slides.svelte';
5
+ import type { DeckTheme, DeckTransition } from './types.js';
6
+
7
+ interface Props {
8
+ /** Where the presenter view is mounted. Leave out to hide the button. */
9
+ presenter?: string;
10
+ };
11
+
12
+ let { presenter }: Props = $props();
13
+
14
+ const themes: DeckTheme[] = ['ink', 'paper', 'auto'];
15
+ const transitions: DeckTransition[] = ['fade', 'slide', 'zoom', 'morph'];
16
+
17
+ let theme = $state<DeckTheme>('ink');
18
+ let transition = $state<DeckTransition>('fade');
19
+ let deck = $state<DeckController>();
20
+ </script>
21
+
22
+ <div class="demo">
23
+ <div class="demo__bar">
24
+ <div class="demo__group" role="radiogroup" aria-label="Theme">
25
+ {#each themes as option (option)}
26
+ <button
27
+ type="button"
28
+ role="radio"
29
+ aria-checked={theme === option}
30
+ class="demo__option"
31
+ onclick={() => (theme = option)}>{option}</button
32
+ >
33
+ {/each}
34
+ </div>
35
+ <label class="demo__field">
36
+ <span>Transition</span>
37
+ <select bind:value={transition}>
38
+ {#each transitions as option (option)}
39
+ <option value={option}>{option}</option>
40
+ {/each}
41
+ </select>
42
+ </label>
43
+ <div class="demo__actions">
44
+ <button type="button" class="demo__action" onclick={() => deck?.toggleOverview()}>Overview</button>
45
+ {#if presenter}
46
+ <a class="demo__action" href={presenter} target="heyramzi-slides-demo-presenter">Presenter</a>
47
+ {/if}
48
+ <button type="button" class="demo__action" onclick={() => window.print()}>Print</button>
49
+ </div>
50
+ </div>
51
+
52
+ <div class="demo__stage">
53
+ <Deck
54
+ bind:controller={deck}
55
+ id="slides-demo"
56
+ title="Slides demo"
57
+ sync="slides-demo"
58
+ urlSync={false}
59
+ {theme}
60
+ {transition}
61
+ {presenter}
62
+ style={demoStyle}
63
+ >
64
+ <DemoSlides />
65
+ </Deck>
66
+ </div>
67
+ </div>
68
+
69
+ <style>
70
+ .demo {
71
+ display: grid;
72
+ gap: 14px;
73
+ width: 100%;
74
+ }
75
+
76
+ .demo__bar {
77
+ display: flex;
78
+ flex-wrap: wrap;
79
+ align-items: center;
80
+ gap: 10px 16px;
81
+ font-size: 13px;
82
+ }
83
+ .demo__group {
84
+ display: inline-flex;
85
+ padding: 3px;
86
+ background: color-mix(in srgb, currentColor 7%, transparent);
87
+ border-radius: 10px;
88
+ }
89
+ .demo__option {
90
+ padding: 5px 12px;
91
+ font: inherit;
92
+ font-weight: 500;
93
+ color: inherit;
94
+ text-transform: capitalize;
95
+ background: transparent;
96
+ border: 0;
97
+ border-radius: 7px;
98
+ opacity: 0.65;
99
+ cursor: pointer;
100
+ transition:
101
+ background-color var(--duration-quick, 150ms) var(--ease-smooth-out, ease-out),
102
+ opacity var(--duration-quick, 150ms) var(--ease-smooth-out, ease-out);
103
+ }
104
+ @media (hover: hover) and (pointer: fine) {
105
+ .demo__option:hover {
106
+ opacity: 1;
107
+ }
108
+ }
109
+ .demo__option[aria-checked='true'] {
110
+ background: color-mix(in srgb, currentColor 14%, transparent);
111
+ opacity: 1;
112
+ }
113
+ .demo__field {
114
+ display: inline-flex;
115
+ align-items: center;
116
+ gap: 8px;
117
+ opacity: 0.85;
118
+ }
119
+ .demo__field select {
120
+ padding: 5px 8px;
121
+ font: inherit;
122
+ color: inherit;
123
+ text-transform: capitalize;
124
+ background: color-mix(in srgb, currentColor 7%, transparent);
125
+ border: 0;
126
+ border-radius: 8px;
127
+ }
128
+ .demo__actions {
129
+ display: inline-flex;
130
+ gap: 6px;
131
+ margin-left: auto;
132
+ }
133
+ .demo__action {
134
+ padding: 6px 12px;
135
+ font: inherit;
136
+ font-weight: 500;
137
+ color: inherit;
138
+ text-decoration: none;
139
+ background: transparent;
140
+ border: 1px solid color-mix(in srgb, currentColor 22%, transparent);
141
+ border-radius: 8px;
142
+ cursor: pointer;
143
+ transition: background-color var(--duration-quick, 150ms) var(--ease-smooth-out, ease-out);
144
+ }
145
+ @media (hover: hover) and (pointer: fine) {
146
+ .demo__action:hover {
147
+ background: color-mix(in srgb, currentColor 8%, transparent);
148
+ }
149
+ }
150
+ .demo__option:focus-visible,
151
+ .demo__action:focus-visible,
152
+ .demo__field select:focus-visible {
153
+ outline: 2px solid currentColor;
154
+ outline-offset: 2px;
155
+ }
156
+
157
+ .demo__stage {
158
+ overflow: hidden;
159
+ border-radius: 16px;
160
+ box-shadow: 0 0 0 1px color-mix(in srgb, currentColor 14%, transparent);
161
+ }
162
+
163
+ @media print {
164
+ .demo {
165
+ display: block;
166
+ }
167
+ .demo__bar {
168
+ display: none;
169
+ }
170
+ .demo__stage {
171
+ overflow: visible;
172
+ border-radius: 0;
173
+ box-shadow: none;
174
+ }
175
+ }
176
+ </style>
@@ -0,0 +1,113 @@
1
+ // Capture a page, or one element of it, to PNG at a canvas size and device scale.
2
+
3
+ import { mkdir, writeFile } from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import type { BrowserLike, LocatorLike, PageLike, Size, WaitUntil } from './types.js';
6
+
7
+ /**
8
+ * `networkidle` makes a screenshot land on a finished page and never fires on a
9
+ * page that keeps polling: an analytics beacon, a chat widget, a buffering hero
10
+ * video. So the first attempt asks for idle and a retry falls back to `load`,
11
+ * which always fires; the caller's `wait` settles whatever is left.
12
+ */
13
+ export const DEFAULT_WAITS: readonly WaitUntil[] = ['networkidle', 'load'];
14
+
15
+ export interface GotoOptions {
16
+ /** Tried in order; the first that resolves wins and the last failure is thrown. */
17
+ waits?: readonly WaitUntil[];
18
+ timeout?: number;
19
+ }
20
+
21
+ /** Navigates, retrying with the next wait condition when one times out. */
22
+ export async function gotoSettled(page: PageLike, url: string, options: GotoOptions = {}) {
23
+ const waits = options.waits ?? DEFAULT_WAITS;
24
+ const timeout = options.timeout ?? 45_000;
25
+ let failure: unknown;
26
+ for (const waitUntil of waits) {
27
+ try {
28
+ return await page.goto(url, { waitUntil, timeout });
29
+ } catch (error) {
30
+ failure = error;
31
+ }
32
+ }
33
+ throw failure ?? new Error('gotoSettled needs at least one wait condition.');
34
+ }
35
+
36
+ export type CaptureSource = { url: string } | { html: string };
37
+
38
+ export interface CaptureOptions {
39
+ /** The viewport the page lays out in. */
40
+ size: Size;
41
+ /** Device scale factor; 2 doubles the pixels. Default 1. */
42
+ scale?: number;
43
+ /** Capture just the first element matching this selector, once it is visible. */
44
+ selector?: string;
45
+ /** Capture the whole scrollable page. Ignored when a selector is given. */
46
+ fullPage?: boolean;
47
+ /** CSS added after load, for hiding overlays. */
48
+ style?: string;
49
+ /** Settle time in ms after load, for animations and virtualised lists. */
50
+ wait?: number;
51
+ goto?: GotoOptions;
52
+ /** Also write the PNG here. */
53
+ path?: string;
54
+ }
55
+
56
+ /** Opens a page at the canvas size, captures it to PNG and closes the page. */
57
+ export async function capturePage(
58
+ browser: BrowserLike,
59
+ source: CaptureSource,
60
+ options: CaptureOptions,
61
+ ): Promise<Uint8Array> {
62
+ const page = await browser.newPage({
63
+ viewport: options.size,
64
+ deviceScaleFactor: options.scale ?? 1,
65
+ });
66
+ try {
67
+ if ('url' in source) await gotoSettled(page, source.url, options.goto);
68
+ else await page.setContent(source.html, { waitUntil: 'load' });
69
+ if (options.style) await page.addStyleTag({ content: options.style });
70
+ if (options.wait) await page.waitForTimeout(options.wait);
71
+ const { selector } = options;
72
+ let bytes: Uint8Array;
73
+ if (selector) {
74
+ const target = page.locator(selector).first();
75
+ await target.waitFor({ state: 'visible' });
76
+ bytes = await target.screenshot({ type: 'png' });
77
+ } else {
78
+ bytes = await page.screenshot({ type: 'png', fullPage: options.fullPage });
79
+ }
80
+ if (options.path) {
81
+ await mkdir(path.dirname(options.path), { recursive: true });
82
+ await writeFile(options.path, bytes);
83
+ }
84
+ return bytes;
85
+ } finally {
86
+ await page.close();
87
+ }
88
+ }
89
+
90
+ /** `01.png`, `02.png` ...: the file name for the nth capture, one-based, zero-padded. */
91
+ export function frameName(index: number, digits = 2, extension = 'png'): string {
92
+ return `${String(index + 1).padStart(digits, '0')}.${extension}`;
93
+ }
94
+
95
+ /**
96
+ * Screenshots every element a locator matches into `dir`, one PNG each, and
97
+ * returns the paths in order. The elements are the pages of a deck.
98
+ */
99
+ export async function captureEach(
100
+ elements: LocatorLike,
101
+ dir: string,
102
+ options: { digits?: number } = {},
103
+ ): Promise<string[]> {
104
+ await mkdir(dir, { recursive: true });
105
+ const count = await elements.count();
106
+ const files: string[] = [];
107
+ for (let index = 0; index < count; index++) {
108
+ const file = path.join(dir, frameName(index, options.digits));
109
+ await elements.nth(index).screenshot({ path: file });
110
+ files.push(file);
111
+ }
112
+ return files;
113
+ }
@@ -0,0 +1,120 @@
1
+ // Encode frames to an mp4 with the ffmpeg on the machine. The package ships no
2
+ // binary; pass `ffmpeg` to name one that is not on PATH.
3
+
4
+ import { spawn } from 'node:child_process';
5
+ import { mkdir, rm } from 'node:fs/promises';
6
+ import path from 'node:path';
7
+
8
+ /** Runs ffmpeg and resolves when it exits 0; the error carries the tail of its stderr. */
9
+ export function runFfmpeg(args: string[], ffmpeg = 'ffmpeg'): Promise<void> {
10
+ return new Promise((resolve, reject) => {
11
+ const child = spawn(ffmpeg, args, { stdio: ['ignore', 'ignore', 'pipe'] });
12
+ let stderr = '';
13
+ child.stderr.on('data', (chunk) => {
14
+ stderr += chunk;
15
+ });
16
+ child.on('error', (error) =>
17
+ reject(new Error(`${ffmpeg} could not be started (${error.message}). Video export needs ffmpeg on PATH, or pass its path.`)),
18
+ );
19
+ child.on('close', (code) =>
20
+ code === 0 ? resolve() : reject(new Error(`${ffmpeg} exited ${code}:\n${stderr.slice(-1500)}`)),
21
+ );
22
+ });
23
+ }
24
+
25
+ export interface EncodeOptions {
26
+ fps: number;
27
+ /** x264 constant rate factor; lower is bigger and cleaner. Default 23. */
28
+ crf?: number;
29
+ /** x264 speed preset. Default `slow`. */
30
+ preset?: string;
31
+ /** The printf pattern the frames are numbered with. Default `%04d.png`. */
32
+ pattern?: string;
33
+ ffmpeg?: string;
34
+ }
35
+
36
+ /** The ffmpeg arguments for a silent yuv420p H.264 mp4 from a numbered image sequence. */
37
+ export function encodeArgs(dir: string, outFile: string, options: EncodeOptions): string[] {
38
+ return [
39
+ '-y',
40
+ '-hide_banner',
41
+ '-loglevel',
42
+ 'error',
43
+ '-framerate',
44
+ String(options.fps),
45
+ '-i',
46
+ path.join(dir, options.pattern ?? '%04d.png'),
47
+ '-an',
48
+ '-vf',
49
+ 'format=yuv420p',
50
+ '-c:v',
51
+ 'libx264',
52
+ '-preset',
53
+ options.preset ?? 'slow',
54
+ '-crf',
55
+ String(options.crf ?? 23),
56
+ '-movflags',
57
+ '+faststart',
58
+ outFile,
59
+ ];
60
+ }
61
+
62
+ /** Encodes a folder of numbered frames. There is no audio stream at all. */
63
+ export async function encodeFrames(dir: string, outFile: string, options: EncodeOptions): Promise<void> {
64
+ await mkdir(path.dirname(outFile), { recursive: true });
65
+ await runFfmpeg(encodeArgs(dir, outFile, options), options.ffmpeg);
66
+ }
67
+
68
+ export interface LoopFrame {
69
+ /** Zero-based frame number. */
70
+ index: number;
71
+ /** Position in the cycle, in [0, 1). */
72
+ t: number;
73
+ /** Where this frame must be written, as a PNG. */
74
+ file: string;
75
+ }
76
+
77
+ export interface LoopOptions extends Omit<EncodeOptions, 'fps' | 'pattern'> {
78
+ fps?: number;
79
+ seconds?: number;
80
+ /** Photograph the frame at `t`; called once per frame, in order. */
81
+ drawFrame(frame: LoopFrame): Promise<void>;
82
+ }
83
+
84
+ /** Frames for a loop of this length: whole frames only, never fewer than one. */
85
+ export function loopFrameCount(seconds: number, fps: number): number {
86
+ return Math.max(1, Math.round(seconds * fps));
87
+ }
88
+
89
+ /**
90
+ * Renders a seamless loop: asks `drawFrame` for t = 0, 1/n, ... (n-1)/n and
91
+ * encodes the result.
92
+ *
93
+ * `t` never reaches 1, so the last frame is one step short of the first and
94
+ * the cut back to frame zero is the same step as every other. Driving all
95
+ * motion off one t is what makes the seam exact by construction. A captured
96
+ * CSS animation loops only when its last frame happens to land on its first.
97
+ */
98
+ export async function renderLoop(
99
+ outFile: string,
100
+ options: LoopOptions,
101
+ ): Promise<{ file: string; frames: number; seconds: number }> {
102
+ const fps = options.fps ?? 30;
103
+ const seconds = options.seconds ?? 2.5;
104
+ const frames = loopFrameCount(seconds, fps);
105
+ const work = `${outFile}.frames`;
106
+ await mkdir(work, { recursive: true });
107
+ try {
108
+ for (let index = 0; index < frames; index++) {
109
+ await options.drawFrame({
110
+ index,
111
+ t: index / frames,
112
+ file: path.join(work, `${String(index).padStart(4, '0')}.png`),
113
+ });
114
+ }
115
+ await encodeFrames(work, outFile, { ...options, fps });
116
+ } finally {
117
+ await rm(work, { recursive: true, force: true });
118
+ }
119
+ return { file: outFile, frames, seconds };
120
+ }
@@ -0,0 +1,9 @@
1
+ // @heyramzi/ui/slides/export: Node-only. Capture, print and encode slides without
2
+ // depending on Playwright or bundling ffmpeg. Hand it the Playwright pages and
3
+ // browsers you already have.
4
+
5
+ export * from './capture.js';
6
+ export * from './ffmpeg.js';
7
+ export * from './launch.js';
8
+ export * from './pdf.js';
9
+ export type * from './types.js';
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Launches Chromium, falling back to the installed Chrome. The bundled
3
+ * Chromium goes missing after a Playwright bump, and Chrome prints the same.
4
+ * Pass `chromium` from Playwright.
5
+ */
6
+ export async function launchChromium<B>(chromium: {
7
+ launch(options?: { headless?: boolean; channel?: string }): Promise<B>;
8
+ }): Promise<B> {
9
+ return chromium.launch({ headless: true }).catch(() => chromium.launch({ headless: true, channel: 'chrome' }));
10
+ }
@@ -0,0 +1,152 @@
1
+ // Print a URL or HTML to PDF at a page size, and assemble captured PNGs into one.
2
+
3
+ import { readFile } from 'node:fs/promises';
4
+ import type { PageLike, ResponseLike, Size, WaitUntil } from './types.js';
5
+
6
+ /** Chromium drops backgrounds in its print pipeline without the color-adjust line. */
7
+ export const RESET_CSS = `*{margin:0;padding:0;box-sizing:border-box;}
8
+ html{-webkit-print-color-adjust:exact;print-color-adjust:exact;}`;
9
+
10
+ /** The `@page` rule for a page of this size in CSS pixels, with no margin. */
11
+ export function pageRule(size: Size): string {
12
+ return `@page{size:${size.width}px ${size.height}px;margin:0;}`;
13
+ }
14
+
15
+ /** The reset, the page size and one full-bleed `.page` box per image. */
16
+ export function imagePagesCss(size: Size, background?: string): string {
17
+ return `${RESET_CSS}
18
+ ${background ? `body{background:${background};}\n` : ''}${pageRule(size)}
19
+ .page{display:block;width:${size.width}px;height:${size.height}px;break-after:page;}
20
+ .page:last-child{break-after:auto;}`;
21
+ }
22
+
23
+ /** One `<img class="page">` per data URL, laid out so each prints on its own page. */
24
+ export function imagePagesHtml(dataUrls: string[], size: Size, background?: string): string {
25
+ const pages = dataUrls.map((dataUrl) => `<img class="page" src="${dataUrl}" alt="">`).join('');
26
+ return `<!doctype html><html><head><meta charset="utf-8"><style>
27
+ ${imagePagesCss(size, background)}
28
+ </style></head><body>${pages}</body></html>`;
29
+ }
30
+
31
+ /** The page count of a PDF, read off its page objects. */
32
+ export function countPdfPages(pdf: Uint8Array): number {
33
+ return (Buffer.from(pdf).toString('latin1').match(/\/Type\s*\/Page[^s]/g) ?? []).length;
34
+ }
35
+
36
+ export interface PrintOptions {
37
+ /**
38
+ * The page size in CSS pixels. Leave it out to print at the size the
39
+ * document's own `@page` declares, which a fixed-size deck writes.
40
+ */
41
+ size?: Size;
42
+ /** A selector to wait for, or a function that resolves when the page is ready to print. */
43
+ ready?: string | ((page: PageLike) => Promise<void>);
44
+ /** How long `ready` may take, in ms. Default 60000. */
45
+ readyTimeout?: number;
46
+ /** A final pause in ms once ready, for paint that has no signal. */
47
+ settle?: number;
48
+ /** For example `1-12`. */
49
+ pageRanges?: string;
50
+ /** Write the PDF here as well as returning it. */
51
+ path?: string;
52
+ }
53
+
54
+ export interface PrintResult {
55
+ pdf: Uint8Array;
56
+ pages: number;
57
+ /** The `src` of every image that had not loaded when the page was printed. */
58
+ brokenImages: string[];
59
+ }
60
+
61
+ /**
62
+ * Waits until fonts and images have settled and the caller's own signal has
63
+ * fired, then prints the page as it stands. Returns the PDF with its page count
64
+ * and the images that never loaded, so a caller can refuse a half-painted sheet.
65
+ */
66
+ export async function printPage(page: PageLike, options: PrintOptions = {}): Promise<PrintResult> {
67
+ const { ready } = options;
68
+ if (typeof ready === 'string') {
69
+ await page.waitForSelector(ready, { timeout: options.readyTimeout ?? 60_000 });
70
+ } else if (ready) {
71
+ await ready(page);
72
+ }
73
+ await page.evaluate(() => document.fonts.ready.then(() => undefined));
74
+ if (options.settle) await page.waitForTimeout(options.settle);
75
+ const brokenImages = await page.evaluate(() =>
76
+ [...document.images].filter((img) => !img.complete || img.naturalWidth === 0).map((img) => img.src),
77
+ );
78
+ const { size } = options;
79
+ const pdf = await page.pdf({
80
+ path: options.path,
81
+ printBackground: true,
82
+ ...(size
83
+ ? { width: `${size.width}px`, height: `${size.height}px` }
84
+ : { preferCSSPageSize: true }),
85
+ pageRanges: options.pageRanges,
86
+ });
87
+ return { pdf, pages: countPdfPages(pdf), brokenImages };
88
+ }
89
+
90
+ export interface PrintNavigation extends PrintOptions {
91
+ /** The load condition for the navigation. Default `load`. */
92
+ waitUntil?: WaitUntil;
93
+ /** Called with the navigation response; throw to refuse to print an error page. */
94
+ check?: (response: ResponseLike | null, page: PageLike) => void;
95
+ }
96
+
97
+ /** Opens a URL in the page and prints it. The caller owns the page and any routing on it. */
98
+ export async function printUrl(
99
+ page: PageLike,
100
+ url: string,
101
+ options: PrintNavigation = {},
102
+ ): Promise<PrintResult> {
103
+ const response = await page.goto(url, { waitUntil: options.waitUntil ?? 'load' });
104
+ options.check?.(response, page);
105
+ return printPage(page, options);
106
+ }
107
+
108
+ /** Loads HTML into the page and prints it. */
109
+ export async function printHtml(
110
+ page: PageLike,
111
+ html: string,
112
+ options: PrintNavigation = {},
113
+ ): Promise<PrintResult> {
114
+ await page.setContent(html, { waitUntil: options.waitUntil ?? 'load' });
115
+ return printPage(page, options);
116
+ }
117
+
118
+ export interface ImagesToPdfOptions {
119
+ /** The size of every page, in CSS pixels. Each image is drawn at exactly this size. */
120
+ size: Size;
121
+ /** The ground behind the images. */
122
+ background?: string;
123
+ }
124
+
125
+ /**
126
+ * Lays PNGs out one per page and prints that, nothing else.
127
+ *
128
+ * Printing the original document would run it through Chromium's print
129
+ * pipeline, which re-rasterises effects instead of compositing them: a blurred
130
+ * backdrop lost 47% of its chroma that way. Handing the pipeline flat bitmaps
131
+ * removes the whole class of drift, so the PDF shows what the PNG shows.
132
+ *
133
+ * Each image is a file path or the bytes themselves.
134
+ */
135
+ export async function imagesToPdf(
136
+ page: PageLike,
137
+ images: Array<string | Uint8Array>,
138
+ options: ImagesToPdfOptions,
139
+ ): Promise<Uint8Array> {
140
+ if (images.length === 0) throw new Error('imagesToPdf needs at least one image.');
141
+ const dataUrls = await Promise.all(
142
+ images.map(async (image) => {
143
+ const bytes = typeof image === 'string' ? await readFile(image) : image;
144
+ return `data:image/png;base64,${Buffer.from(bytes).toString('base64')}`;
145
+ }),
146
+ );
147
+ const { pdf } = await printHtml(page, imagePagesHtml(dataUrls, options.size, options.background), {
148
+ size: options.size,
149
+ pageRanges: `1-${images.length}`,
150
+ });
151
+ return pdf;
152
+ }
@@ -0,0 +1,48 @@
1
+ // The slice of a Playwright page the export helpers touch, declared here so the
2
+ // package takes no dependency on Playwright. Playwright's own Page, Locator and
3
+ // Browser satisfy these as they are, whichever version the caller has installed.
4
+
5
+ export interface Size {
6
+ width: number;
7
+ height: number;
8
+ }
9
+
10
+ export type WaitUntil = 'load' | 'domcontentloaded' | 'networkidle';
11
+
12
+ export interface ResponseLike {
13
+ ok(): boolean;
14
+ status(): number;
15
+ }
16
+
17
+ export interface LocatorLike {
18
+ first(): LocatorLike;
19
+ nth(index: number): LocatorLike;
20
+ count(): Promise<number>;
21
+ waitFor(options?: { state?: 'visible'; timeout?: number }): Promise<void>;
22
+ screenshot(options?: { path?: string; type?: 'png' }): Promise<Uint8Array>;
23
+ }
24
+
25
+ export interface PageLike {
26
+ goto(url: string, options?: { waitUntil?: WaitUntil; timeout?: number }): Promise<ResponseLike | null>;
27
+ setContent(html: string, options?: { waitUntil?: WaitUntil; timeout?: number }): Promise<void>;
28
+ addStyleTag(options: { content: string }): Promise<unknown>;
29
+ evaluate<R>(pageFunction: () => R | Promise<R>): Promise<R>;
30
+ waitForTimeout(ms: number): Promise<void>;
31
+ waitForSelector(selector: string, options?: { timeout?: number }): Promise<unknown>;
32
+ locator(selector: string): LocatorLike;
33
+ screenshot(options?: { type?: 'png'; fullPage?: boolean }): Promise<Uint8Array>;
34
+ pdf(options: {
35
+ path?: string;
36
+ width?: string;
37
+ height?: string;
38
+ printBackground?: boolean;
39
+ preferCSSPageSize?: boolean;
40
+ pageRanges?: string;
41
+ }): Promise<Uint8Array>;
42
+ url(): string;
43
+ close(): Promise<void>;
44
+ }
45
+
46
+ export interface BrowserLike {
47
+ newPage(options?: { viewport?: Size; deviceScaleFactor?: number }): Promise<PageLike>;
48
+ }
@@ -0,0 +1,19 @@
1
+ export { default as Deck } from './Deck.svelte';
2
+ export { default as Slide } from './Slide.svelte';
3
+ export { default as Fragment } from './Fragment.svelte';
4
+ export { default as Presenter } from './Presenter.svelte';
5
+ export { DeckController } from './deck-controller.svelte.js';
6
+ export type { SlideEntry, Source } from './deck-controller.svelte.js';
7
+ export { defaultLabels } from './labels.js';
8
+ export type { DeckLabels } from './labels.js';
9
+ export type { Position } from './navigation.js';
10
+ export type {
11
+ BridgeOptions,
12
+ Canvas,
13
+ DeckTheme,
14
+ DeckTransition,
15
+ SlideAlign,
16
+ SlideNotes,
17
+ UrlSync,
18
+ } from './types.js';
19
+ export { DEFAULT_CANVAS } from './types.js';