@kadal/deck 0.0.0-stage → 0.1.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 (157) hide show
  1. package/LICENSE +661 -0
  2. package/LICENSING.md +40 -0
  3. package/README.md +170 -2
  4. package/dist/_virtual/_rolldown/runtime.js +13 -0
  5. package/dist/audience/index.d.ts +14 -0
  6. package/dist/audience/index.js +680 -0
  7. package/dist/backgrounds/index.d.ts +65 -0
  8. package/dist/backgrounds/index.js +604 -0
  9. package/dist/backgrounds/shared.d.ts +7 -0
  10. package/dist/backgrounds/shared.js +89 -0
  11. package/dist/cli/bin.d.ts +1 -0
  12. package/dist/cli/bin.js +8 -0
  13. package/dist/cli/host.js +111 -0
  14. package/dist/cli/index.d.ts +4 -0
  15. package/dist/cli/index.js +140 -0
  16. package/dist/cli/login.js +56 -0
  17. package/dist/cli/publish.js +159 -0
  18. package/dist/cli/vite.js +39 -0
  19. package/dist/compiler/code-meta.d.ts +33 -0
  20. package/dist/compiler/code-meta.js +81 -0
  21. package/dist/compiler/index.d.ts +8 -0
  22. package/dist/compiler/index.js +8 -0
  23. package/dist/compiler/magic-move.d.ts +26 -0
  24. package/dist/compiler/magic-move.js +33 -0
  25. package/dist/compiler/plain-text.js +7 -0
  26. package/dist/compiler/rehype-code.d.ts +18 -0
  27. package/dist/compiler/rehype-code.js +66 -0
  28. package/dist/compiler/rehype-glossary.d.ts +35 -0
  29. package/dist/compiler/rehype-glossary.js +211 -0
  30. package/dist/compiler/slots.d.ts +3 -0
  31. package/dist/compiler/slots.js +36 -0
  32. package/dist/compiler/split.d.ts +63 -0
  33. package/dist/compiler/split.js +195 -0
  34. package/dist/components/Background.d.ts +15 -0
  35. package/dist/components/Background.js +24 -0
  36. package/dist/components/Click.d.ts +54 -0
  37. package/dist/components/Click.js +129 -0
  38. package/dist/components/Code.d.ts +27 -0
  39. package/dist/components/Code.js +124 -0
  40. package/dist/components/Motion.d.ts +25 -0
  41. package/dist/components/Motion.js +45 -0
  42. package/dist/components/Sketch.d.ts +44 -0
  43. package/dist/components/Sketch.js +141 -0
  44. package/dist/components/Term.d.ts +63 -0
  45. package/dist/components/Term.js +492 -0
  46. package/dist/components/bling.d.ts +75 -0
  47. package/dist/components/bling.js +146 -0
  48. package/dist/components/content.d.ts +232 -0
  49. package/dist/components/content.js +637 -0
  50. package/dist/components/index.d.ts +10 -0
  51. package/dist/components/index.js +88 -0
  52. package/dist/core/clicks.d.ts +45 -0
  53. package/dist/core/clicks.js +75 -0
  54. package/dist/core/hash.d.ts +33 -0
  55. package/dist/core/hash.js +49 -0
  56. package/dist/core/keyboard.d.ts +17 -0
  57. package/dist/core/keyboard.js +198 -0
  58. package/dist/core/layout.d.ts +16 -0
  59. package/dist/core/layout.js +25 -0
  60. package/dist/core/model.d.ts +259 -0
  61. package/dist/core/model.js +377 -0
  62. package/dist/core/navigation.d.ts +74 -0
  63. package/dist/core/navigation.js +244 -0
  64. package/dist/core/poster.js +30 -0
  65. package/dist/core/store.d.ts +20 -0
  66. package/dist/core/store.js +64 -0
  67. package/dist/core/touch.js +27 -0
  68. package/dist/draw/DrawingLayer.js +315 -0
  69. package/dist/draw/strokes.js +188 -0
  70. package/dist/export/index.d.ts +68 -0
  71. package/dist/export/index.js +251 -0
  72. package/dist/export/run.js +81 -0
  73. package/dist/glossary/model.d.ts +81 -0
  74. package/dist/glossary/model.js +156 -0
  75. package/dist/glossary/parse.d.ts +27 -0
  76. package/dist/glossary/parse.js +90 -0
  77. package/dist/index.d.ts +31 -0
  78. package/dist/index.js +30 -0
  79. package/dist/live/LiveProvider.d.ts +20 -0
  80. package/dist/live/LiveProvider.js +450 -0
  81. package/dist/live/analytics.d.ts +15 -0
  82. package/dist/live/analytics.js +103 -0
  83. package/dist/live/client.d.ts +51 -0
  84. package/dist/live/client.js +193 -0
  85. package/dist/live/components.d.ts +64 -0
  86. package/dist/live/components.js +471 -0
  87. package/dist/live/context.d.ts +15 -0
  88. package/dist/live/context.js +28 -0
  89. package/dist/live/index.d.ts +7 -0
  90. package/dist/live/index.js +7 -0
  91. package/dist/live/protocol.d.ts +284 -0
  92. package/dist/live/protocol.js +42 -0
  93. package/dist/live/turnstile.js +49 -0
  94. package/dist/presenter/index.d.ts +20 -0
  95. package/dist/presenter/index.js +1211 -0
  96. package/dist/react/AutoAnimate.js +92 -0
  97. package/dist/react/Backgrounds.d.ts +11 -0
  98. package/dist/react/Backgrounds.js +154 -0
  99. package/dist/react/ConfettiLayer.js +85 -0
  100. package/dist/react/Deck.d.ts +35 -0
  101. package/dist/react/Deck.js +983 -0
  102. package/dist/react/Frame.d.ts +16 -0
  103. package/dist/react/Frame.js +174 -0
  104. package/dist/react/Hints.d.ts +3 -0
  105. package/dist/react/Hints.js +201 -0
  106. package/dist/react/Lightbox.js +54 -0
  107. package/dist/react/Overview.js +93 -0
  108. package/dist/react/PrintView.js +128 -0
  109. package/dist/react/ScrollView.js +128 -0
  110. package/dist/react/SlideFrame.d.ts +51 -0
  111. package/dist/react/SlideFrame.js +323 -0
  112. package/dist/react/Stage.js +151 -0
  113. package/dist/react/ThemeMenu.d.ts +5 -0
  114. package/dist/react/ThemeMenu.js +124 -0
  115. package/dist/react/Zoom.js +23 -0
  116. package/dist/react/chrome.js +339 -0
  117. package/dist/react/context.d.ts +165 -0
  118. package/dist/react/context.js +19 -0
  119. package/dist/react/hooks.d.ts +33 -0
  120. package/dist/react/hooks.js +70 -0
  121. package/dist/react/layouts.d.ts +21 -0
  122. package/dist/react/layouts.js +104 -0
  123. package/dist/react/mdx.d.ts +4 -0
  124. package/dist/react/mdx.js +33 -0
  125. package/dist/react/terms.d.ts +6 -0
  126. package/dist/react/terms.js +66 -0
  127. package/dist/renderers/Island.d.ts +57 -0
  128. package/dist/renderers/Island.js +223 -0
  129. package/dist/renderers/contract.d.ts +173 -0
  130. package/dist/renderers/contract.js +152 -0
  131. package/dist/renderers/index.d.ts +12 -0
  132. package/dist/renderers/index.js +16 -0
  133. package/dist/renderers/react.d.ts +16 -0
  134. package/dist/renderers/react.js +32 -0
  135. package/dist/renderers/web-components.d.ts +4 -0
  136. package/dist/renderers/web-components.js +103 -0
  137. package/dist/sketch/Canvas.js +49 -0
  138. package/dist/sketch/dsl.d.ts +131 -0
  139. package/dist/sketch/dsl.js +281 -0
  140. package/dist/sketch/index.d.ts +4 -0
  141. package/dist/sketch/index.js +3 -0
  142. package/dist/sketch/palette.d.ts +21 -0
  143. package/dist/sketch/palette.js +66 -0
  144. package/dist/sketch/render.d.ts +43 -0
  145. package/dist/sketch/render.js +198 -0
  146. package/dist/themes/base.css +1704 -0
  147. package/dist/themes/bling.css +592 -0
  148. package/dist/themes/glossary.css +317 -0
  149. package/dist/themes/live.css +1557 -0
  150. package/dist/themes/minimal.css +83 -0
  151. package/dist/themes/rla.css +92 -0
  152. package/dist/themes/sketch.css +121 -0
  153. package/dist/themes/stack.css +62 -0
  154. package/dist/vite/index.d.ts +15 -0
  155. package/dist/vite/index.js +320 -0
  156. package/package.json +159 -4
  157. package/tools/verify-island.mjs +150 -0
@@ -0,0 +1,88 @@
1
+ import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
+ import { After, Click, Clicks, Fragment, Li, Ol, Ul } from "./Click.js";
3
+ import { Code, MagicMove } from "./Code.js";
4
+ import { Absolute, Anchor, Arrow, Audio, Badge, Callout, Center, Col, Cols, Confetti, Counter, Encrypted, FitText, Frame, Grid, Iframe, Image, Kbd, Link, Mark, Mermaid, Notes, Qr, Spacer, Stack, Stretch, Tex, Timer, Toc, Transform, Tweet, Typewriter, Video, Youtube } from "./content.js";
5
+ import { Motion } from "./Motion.js";
6
+ import { Blob, Chip, Divider, Glass, Glow, GradientText, Halo, Icon, Ribbon, Shimmer, Tilt } from "./bling.js";
7
+ import { Term, TermReadout, Terms } from "./Term.js";
8
+ import { Sketch } from "./Sketch.js";
9
+ import { Background } from "./Background.js";
10
+ import { Leaderboard, Pace, Poll, Presence, QrJoin, Questions, Quiz, Reactions, WordCloud } from "../live/components.js";
11
+ //#region src/components/index.ts
12
+ var components_exports = /* @__PURE__ */ __exportAll({
13
+ Absolute: () => Absolute,
14
+ After: () => After,
15
+ Anchor: () => Anchor,
16
+ Arrow: () => Arrow,
17
+ Audio: () => Audio,
18
+ Background: () => Background,
19
+ Badge: () => Badge,
20
+ Blob: () => Blob,
21
+ Callout: () => Callout,
22
+ Center: () => Center,
23
+ Chip: () => Chip,
24
+ Click: () => Click,
25
+ Clicks: () => Clicks,
26
+ Code: () => Code,
27
+ Col: () => Col,
28
+ Cols: () => Cols,
29
+ Confetti: () => Confetti,
30
+ Counter: () => Counter,
31
+ Divider: () => Divider,
32
+ Encrypted: () => Encrypted,
33
+ FitText: () => FitText,
34
+ Fragment: () => Fragment,
35
+ Frame: () => Frame,
36
+ Glass: () => Glass,
37
+ Glow: () => Glow,
38
+ GradientText: () => GradientText,
39
+ Grid: () => Grid,
40
+ Halo: () => Halo,
41
+ Icon: () => Icon,
42
+ Iframe: () => Iframe,
43
+ Image: () => Image,
44
+ Kbd: () => Kbd,
45
+ Leaderboard: () => Leaderboard,
46
+ Li: () => Li,
47
+ Link: () => Link,
48
+ MagicMove: () => MagicMove,
49
+ Mark: () => Mark,
50
+ Math: () => Tex,
51
+ Mermaid: () => Mermaid,
52
+ Motion: () => Motion,
53
+ Notes: () => Notes,
54
+ Ol: () => Ol,
55
+ Pace: () => Pace,
56
+ Poll: () => Poll,
57
+ Presence: () => Presence,
58
+ Qr: () => Qr,
59
+ QrJoin: () => QrJoin,
60
+ Questions: () => Questions,
61
+ Quiz: () => Quiz,
62
+ Reactions: () => Reactions,
63
+ Ribbon: () => Ribbon,
64
+ Shimmer: () => Shimmer,
65
+ Sketch: () => Sketch,
66
+ Spacer: () => Spacer,
67
+ Stack: () => Stack,
68
+ Stretch: () => Stretch,
69
+ Term: () => Term,
70
+ TermReadout: () => TermReadout,
71
+ Terms: () => Terms,
72
+ Tex: () => Tex,
73
+ Tilt: () => Tilt,
74
+ Timer: () => Timer,
75
+ Toc: () => Toc,
76
+ Transform: () => Transform,
77
+ Tweet: () => Tweet,
78
+ Typewriter: () => Typewriter,
79
+ Ul: () => Ul,
80
+ Video: () => Video,
81
+ WordCloud: () => WordCloud,
82
+ Youtube: () => Youtube,
83
+ li: () => Li,
84
+ ol: () => Ol,
85
+ ul: () => Ul
86
+ });
87
+ //#endregion
88
+ export { Absolute, After, Anchor, Arrow, Audio, Background, Badge, Blob, Callout, Center, Chip, Click, Clicks, Code, Col, Cols, Confetti, Counter, Divider, Encrypted, FitText, Fragment, Frame, Glass, Glow, GradientText, Grid, Halo, Icon, Iframe, Image, Kbd, Leaderboard, Li, Link, MagicMove, Mark, Tex as Math, Mermaid, Motion, Notes, Ol, Pace, Poll, Presence, Qr, QrJoin, Questions, Quiz, Reactions, Ribbon, Shimmer, Sketch, Spacer, Stack, Stretch, Term, TermReadout, Terms, Tex, Tilt, Timer, Toc, Transform, Tweet, Typewriter, Ul, Video, WordCloud, Youtube, components_exports, Li as li, Ol as ol, Ul as ul };
@@ -0,0 +1,45 @@
1
+ //#region src/core/clicks.d.ts
2
+ /**
3
+ * ─────────────────────────────────────────────────────────────────────────────
4
+ * CLICKS
5
+ * ─────────────────────────────────────────────────────────────────────────────
6
+ *
7
+ * A click is the unit of animation inside a slide: reveal.js calls the elements fragments, Slidev
8
+ * calls the steps clicks. Elements register with the slide's registry when they render, in
9
+ * document order, and get an absolute step; the slide's total is the largest step anyone asked
10
+ * for. Registration happens during render (not in an effect) so the order is the document order
11
+ * and the total is known before the slide is shown.
12
+ *
13
+ * at: undefined relative — one after the previous element's step (the common case)
14
+ * at: "+2" relative by more; "+0" shares the previous element's step
15
+ * at: 3 absolute; 0 means "always shown" and reserves nothing
16
+ * at: [2, 5] visible from step 2 through 4, hidden again at 5
17
+ * hide: true the element starts visible and hides at its step
18
+ * span: 3 reserve three consecutive steps (a code block with three highlights)
19
+ * ─────────────────────────────────────────────────────────────────────────────
20
+ */
21
+ export type ClickAt = number | string | [number, number] | undefined;
22
+ export interface ClickSpec {
23
+ at?: ClickAt;
24
+ hide?: boolean;
25
+ span?: number;
26
+ }
27
+ export interface ClickRange {
28
+ /** The step at which the element becomes (or, for `hide`, stops being) visible. */
29
+ start: number;
30
+ /** Exclusive end: the step from which it is hidden again; Infinity for "stays". */
31
+ end: number;
32
+ hide: boolean;
33
+ /** The largest step this element needs: what the slide's total must reach. */
34
+ last: number;
35
+ }
36
+ export interface ClickRegistry {
37
+ /** Allocate (or re-read) the range for an element with a stable id. */
38
+ allocate(id: string, spec: ClickSpec): ClickRange;
39
+ release(id: string): void;
40
+ /** The largest step anyone needs: the slide's click total. */
41
+ total(): number;
42
+ /** Ask to be told when the total changes. */
43
+ onChange(listener: (total: number) => void): () => void;
44
+ }
45
+ //#endregion
@@ -0,0 +1,75 @@
1
+ //#region src/core/clicks.ts
2
+ function createClickRegistry(base = 0) {
3
+ const ranges = /* @__PURE__ */ new Map();
4
+ const active = /* @__PURE__ */ new Set();
5
+ const listeners = /* @__PURE__ */ new Set();
6
+ let cursor = base;
7
+ let lastTotal = -1;
8
+ const total = () => {
9
+ let max = base;
10
+ for (const [id, r] of ranges) if (active.has(id)) max = Math.max(max, r.last);
11
+ return max;
12
+ };
13
+ const notify = () => {
14
+ const t = total();
15
+ if (t === lastTotal) return;
16
+ lastTotal = t;
17
+ for (const l of listeners) l(t);
18
+ };
19
+ return {
20
+ allocate(id, spec) {
21
+ const existing = ranges.get(id);
22
+ if (existing) {
23
+ if (!active.has(id)) {
24
+ active.add(id);
25
+ queueMicrotask(notify);
26
+ }
27
+ return existing;
28
+ }
29
+ let start;
30
+ let end = Number.POSITIVE_INFINITY;
31
+ const at = spec.at;
32
+ if (Array.isArray(at)) {
33
+ start = at[0];
34
+ end = at[1];
35
+ } else if (typeof at === "number") start = at;
36
+ else if (typeof at === "string" && /^[+-]\d+$/.test(at)) start = cursor + Number(at);
37
+ else if (typeof at === "string" && /^\d+$/.test(at)) start = Number(at);
38
+ else start = cursor + 1;
39
+ const span = Math.max(1, spec.span ?? 1);
40
+ const last = Number.isFinite(end) ? end : start + span - 1;
41
+ const range = {
42
+ start,
43
+ end,
44
+ hide: !!spec.hide,
45
+ last
46
+ };
47
+ ranges.set(id, range);
48
+ active.add(id);
49
+ cursor = Math.max(cursor, last);
50
+ queueMicrotask(notify);
51
+ return range;
52
+ },
53
+ release(id) {
54
+ if (active.delete(id)) queueMicrotask(notify);
55
+ },
56
+ total,
57
+ onChange(listener) {
58
+ listeners.add(listener);
59
+ return () => {
60
+ listeners.delete(listener);
61
+ };
62
+ }
63
+ };
64
+ }
65
+ /** Is an element with this range shown at the current click? */
66
+ function isShown(range, click) {
67
+ if (range.hide) return click < range.start;
68
+ return click >= range.start && click < range.end;
69
+ }
70
+ /** Is this the element's own step (the one just reached), for `current-fragment` styling? */
71
+ function isCurrent(range, click) {
72
+ return click === range.start;
73
+ }
74
+ //#endregion
75
+ export { createClickRegistry, isCurrent, isShown };
@@ -0,0 +1,33 @@
1
+ import { Column } from "./model.js";
2
+ import { Position } from "./navigation.js";
3
+ //#region src/core/hash.d.ts
4
+ /**
5
+ * The URL hash as reveal.js writes it — `#/h`, `#/h/v`, `#/h/v/click` — or `#/slide-id`, plus the
6
+ * query flags every deck understands: `?print-pdf`, `?view=scroll`, `?presenter`.
7
+ */
8
+ export declare function parseHash(hash: string, columns: Column[], oneBased?: boolean): Partial<Position> | null;
9
+ export declare function formatHash(p: Position, opts?: {
10
+ oneBased?: boolean;
11
+ withClick?: boolean;
12
+ id?: string | null;
13
+ }): string;
14
+ export interface Flags {
15
+ print: boolean;
16
+ view: "slide" | "scroll" | null;
17
+ presenter: boolean;
18
+ embedded: boolean;
19
+ /**
20
+ * `?at` — this link is deliberately ABOUT its slide: a share link, the phone's "Open full". A
21
+ * hash alone cannot say that, because the deck writes the hash as the viewer navigates, so the
22
+ * address of a bookmark taken mid-talk looks exactly like a link someone chose to send.
23
+ */
24
+ at: boolean;
25
+ /**
26
+ * `?projector` — a window that exists to be LOOKED at: a second screen, or the window you share
27
+ * on a call. It renders the deck exactly as the deck page does, follows the presenter and cannot
28
+ * be made to stop, and takes no navigation input of its own.
29
+ */
30
+ projector: boolean;
31
+ }
32
+ export declare function parseFlags(search: string): Flags;
33
+ //#endregion
@@ -0,0 +1,49 @@
1
+ import { findById } from "./model.js";
2
+ //#region src/core/hash.ts
3
+ /**
4
+ * The URL hash as reveal.js writes it — `#/h`, `#/h/v`, `#/h/v/click` — or `#/slide-id`, plus the
5
+ * query flags every deck understands: `?print-pdf`, `?view=scroll`, `?presenter`.
6
+ */
7
+ function parseHash(hash, columns, oneBased = false) {
8
+ const raw = hash.replace(/^#\/?/, "");
9
+ if (!raw) return null;
10
+ const parts = raw.split("/");
11
+ const offset = oneBased ? 1 : 0;
12
+ if (parts[0] && !/^\d+$/.test(parts[0])) {
13
+ const found = findById(columns, decodeURIComponent(parts[0]));
14
+ return found ? {
15
+ ...found,
16
+ click: 0
17
+ } : null;
18
+ }
19
+ const h = Number(parts[0] ?? 0) - offset;
20
+ const v = parts[1] ? Number(parts[1]) - offset : 0;
21
+ const click = parts[2] ? Number(parts[2]) : 0;
22
+ return {
23
+ h: Number.isFinite(h) ? h : 0,
24
+ v: Number.isFinite(v) ? v : 0,
25
+ click: Number.isFinite(click) ? click : 0
26
+ };
27
+ }
28
+ function formatHash(p, opts = {}) {
29
+ if (opts.id) return `#/${encodeURIComponent(opts.id)}${opts.withClick && p.click ? `/${p.click}` : ""}`;
30
+ const offset = opts.oneBased ? 1 : 0;
31
+ let out = `#/${p.h + offset}`;
32
+ if (p.v > 0 || opts.withClick && p.click > 0) out += `/${p.v + offset}`;
33
+ if (opts.withClick && p.click > 0) out += `/${p.click}`;
34
+ return out;
35
+ }
36
+ function parseFlags(search) {
37
+ const q = new URLSearchParams(search);
38
+ const view = q.get("view");
39
+ return {
40
+ print: q.has("print-pdf") || q.get("print") === "pdf",
41
+ view: view === "scroll" ? "scroll" : view === "slide" ? "slide" : null,
42
+ presenter: q.has("presenter"),
43
+ embedded: q.has("embedded"),
44
+ at: q.has("at"),
45
+ projector: q.has("projector")
46
+ };
47
+ }
48
+ //#endregion
49
+ export { formatHash, parseFlags, parseHash };
@@ -0,0 +1,17 @@
1
+ //#region src/core/keyboard.d.ts
2
+ /**
3
+ * Keyboard bindings: reveal.js's and Slidev's, merged. A deck may override any binding through
4
+ * `keyboard: { "KeyX": "action" | null }` in its config; `null` unbinds. Actions are names the
5
+ * runtime dispatches; the map is data so the help overlay can print it.
6
+ */
7
+ export type KeyAction = "next" | "prev" | "nextSlide" | "prevSlide" | "left" | "right" | "up" | "down" | "first" | "last" | "overview" | "fullscreen" | "pause" | "presenter" | "help" | "jump" | "search" | "autoSlide" | "draw" | "pointer" | "clearDrawing" | "undoDrawing" | "colorScheme" | "escape" | "zoomOut" | "hints" | "themeMenu";
8
+ export interface KeyBinding {
9
+ keys: string[];
10
+ action: KeyAction;
11
+ description: string;
12
+ /** Shown in the help overlay under this label. */
13
+ label: string;
14
+ }
15
+ /** `code` values (physical keys), with `Shift+`/`Alt+`/`Ctrl+`/`Meta+` prefixes. */
16
+ export declare const KEY_BINDINGS: KeyBinding[];
17
+ //#endregion
@@ -0,0 +1,198 @@
1
+ //#region src/core/keyboard.ts
2
+ /** `code` values (physical keys), with `Shift+`/`Alt+`/`Ctrl+`/`Meta+` prefixes. */
3
+ const KEY_BINDINGS = [
4
+ {
5
+ keys: [
6
+ "Space",
7
+ "KeyJ",
8
+ "KeyN",
9
+ "PageDown"
10
+ ],
11
+ action: "next",
12
+ label: "Space J N",
13
+ description: "Next step or slide, through vertical stacks"
14
+ },
15
+ {
16
+ keys: [
17
+ "KeyK",
18
+ "KeyP",
19
+ "PageUp",
20
+ "Shift+Space",
21
+ "Backspace"
22
+ ],
23
+ action: "prev",
24
+ label: "K P Shift Space",
25
+ description: "Previous step or slide"
26
+ },
27
+ {
28
+ keys: ["ArrowRight", "KeyL"],
29
+ action: "right",
30
+ label: "→ L",
31
+ description: "Next step, then the next column"
32
+ },
33
+ {
34
+ keys: ["ArrowLeft", "KeyH"],
35
+ action: "left",
36
+ label: "← H",
37
+ description: "Previous step, then the previous column"
38
+ },
39
+ {
40
+ keys: ["ArrowDown"],
41
+ action: "down",
42
+ label: "↓",
43
+ description: "Slide beneath"
44
+ },
45
+ {
46
+ keys: ["ArrowUp"],
47
+ action: "up",
48
+ label: "↑",
49
+ description: "Slide above"
50
+ },
51
+ {
52
+ keys: ["Shift+ArrowRight"],
53
+ action: "nextSlide",
54
+ label: "Shift →",
55
+ description: "Next slide, skipping steps"
56
+ },
57
+ {
58
+ keys: ["Shift+ArrowLeft"],
59
+ action: "prevSlide",
60
+ label: "Shift ←",
61
+ description: "Previous slide, skipping steps"
62
+ },
63
+ {
64
+ keys: ["Home", "Shift+ArrowUp"],
65
+ action: "first",
66
+ label: "gg Home",
67
+ description: "First slide"
68
+ },
69
+ {
70
+ keys: [
71
+ "End",
72
+ "Shift+ArrowDown",
73
+ "Shift+KeyG"
74
+ ],
75
+ action: "last",
76
+ label: "G End",
77
+ description: "Last slide"
78
+ },
79
+ {
80
+ keys: ["Escape", "KeyO"],
81
+ action: "overview",
82
+ label: "Esc O",
83
+ description: "Overview"
84
+ },
85
+ {
86
+ keys: ["KeyF"],
87
+ action: "hints",
88
+ label: "F",
89
+ description: "Focus: label every link, control and embedded page — type a label to use it"
90
+ },
91
+ {
92
+ keys: ["Shift+KeyF"],
93
+ action: "fullscreen",
94
+ label: "Shift F",
95
+ description: "Fullscreen"
96
+ },
97
+ {
98
+ keys: ["KeyB", "Period"],
99
+ action: "pause",
100
+ label: "B .",
101
+ description: "Pause: black out the screen"
102
+ },
103
+ {
104
+ keys: ["KeyS"],
105
+ action: "presenter",
106
+ label: "S",
107
+ description: "Open the presenter view"
108
+ },
109
+ {
110
+ keys: ["Shift+Slash"],
111
+ action: "help",
112
+ label: "?",
113
+ description: "This help"
114
+ },
115
+ {
116
+ keys: ["KeyG", "Shift+Semicolon"],
117
+ action: "jump",
118
+ label: ": g",
119
+ description: "Jump to a slide by number (:12), or gg for the first"
120
+ },
121
+ {
122
+ keys: [
123
+ "Slash",
124
+ "Ctrl+Shift+KeyF",
125
+ "Meta+Shift+KeyF"
126
+ ],
127
+ action: "search",
128
+ label: "/ Ctrl Shift F",
129
+ description: "Search the deck"
130
+ },
131
+ {
132
+ keys: ["KeyA"],
133
+ action: "autoSlide",
134
+ label: "A",
135
+ description: "Pause or resume auto-slide"
136
+ },
137
+ {
138
+ keys: ["KeyC"],
139
+ action: "draw",
140
+ label: "C",
141
+ description: "Draw on the slide"
142
+ },
143
+ {
144
+ keys: ["KeyX"],
145
+ action: "pointer",
146
+ label: "X",
147
+ description: "Laser pointer"
148
+ },
149
+ {
150
+ keys: ["Shift+KeyC"],
151
+ action: "clearDrawing",
152
+ label: "Shift C",
153
+ description: "Clear the slide's drawing"
154
+ },
155
+ {
156
+ keys: ["Ctrl+KeyZ", "Meta+KeyZ"],
157
+ action: "undoDrawing",
158
+ label: "Ctrl Z",
159
+ description: "Undo the last stroke"
160
+ },
161
+ {
162
+ keys: ["KeyD"],
163
+ action: "colorScheme",
164
+ label: "D",
165
+ description: "Dark or light"
166
+ },
167
+ {
168
+ keys: ["KeyT"],
169
+ action: "themeMenu",
170
+ label: "T",
171
+ description: "Theme and light or dark"
172
+ }
173
+ ];
174
+ function comboOf(e) {
175
+ const mods = [];
176
+ if (e.ctrlKey) mods.push("Ctrl");
177
+ if (e.shiftKey) mods.push("Shift");
178
+ if (e.altKey) mods.push("Alt");
179
+ if (e.metaKey) mods.push("Meta");
180
+ return [...mods, e.code].join("+");
181
+ }
182
+ /** Resolve a keyboard event to an action, honouring overrides. Returns null when unbound. */
183
+ function actionFor(e, overrides = {}) {
184
+ const combo = comboOf(e);
185
+ if (combo in overrides) return overrides[combo];
186
+ for (const b of KEY_BINDINGS) if (b.keys.includes(combo)) return b.action;
187
+ if (e.ctrlKey || e.altKey || e.metaKey) return null;
188
+ return null;
189
+ }
190
+ /** Typing in a field must never turn pages. */
191
+ function isEditable(target) {
192
+ if (!(target instanceof HTMLElement)) return false;
193
+ if (target.isContentEditable) return true;
194
+ const tag = target.tagName;
195
+ return tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT";
196
+ }
197
+ //#endregion
198
+ export { KEY_BINDINGS, actionFor, comboOf, isEditable };
@@ -0,0 +1,16 @@
1
+ import "./model.js";
2
+ //#region src/core/layout.d.ts
3
+ /**
4
+ * The stage is authored at `width × height` and scaled uniformly to fit the viewport with a margin
5
+ * — reveal.js's layout. `disableLayout` decks (BYOL) skip this and fill the viewport.
6
+ */
7
+ export interface Layout {
8
+ scale: number;
9
+ /** Stage size after scaling, in CSS pixels. */
10
+ width: number;
11
+ height: number;
12
+ /** Offsets that centre the stage in the viewport. */
13
+ left: number;
14
+ top: number;
15
+ }
16
+ //#endregion
@@ -0,0 +1,25 @@
1
+ //#region src/core/layout.ts
2
+ function computeLayout(config, viewport) {
3
+ const availableW = viewport.width * (1 - config.margin);
4
+ const availableH = viewport.height * (1 - config.margin);
5
+ let scale = Math.min(availableW / config.width, availableH / config.height);
6
+ scale = Math.max(config.minScale, Math.min(config.maxScale, scale));
7
+ const width = config.width * scale;
8
+ const height = config.height * scale;
9
+ return {
10
+ scale,
11
+ width,
12
+ height,
13
+ left: (viewport.width - width) / 2,
14
+ top: (viewport.height - height) / 2
15
+ };
16
+ }
17
+ /** A point in viewport pixels → stage coordinates (0..width, 0..height). */
18
+ function toStage(layout, x, y) {
19
+ return {
20
+ x: (x - layout.left) / layout.scale,
21
+ y: (y - layout.top) / layout.scale
22
+ };
23
+ }
24
+ //#endregion
25
+ export { computeLayout, toStage };