contour-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 (53) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +159 -2
  3. package/dist/astro/Deck.astro +61 -0
  4. package/dist/astro/host.d.ts +13 -0
  5. package/dist/astro/host.js +116 -0
  6. package/dist/astro/legacy.d.ts +22 -0
  7. package/dist/astro/legacy.js +42 -0
  8. package/dist/core/deck.d.ts +2 -0
  9. package/dist/core/deck.js +587 -0
  10. package/dist/core/dom.d.ts +8 -0
  11. package/dist/core/dom.js +22 -0
  12. package/dist/core/gestures.d.ts +6 -0
  13. package/dist/core/gestures.js +24 -0
  14. package/dist/core/index.d.ts +3 -0
  15. package/dist/core/index.js +2 -0
  16. package/dist/core/labels.d.ts +2 -0
  17. package/dist/core/labels.js +19 -0
  18. package/dist/core/model.d.ts +37 -0
  19. package/dist/core/model.js +51 -0
  20. package/dist/core/navigator.d.ts +6 -0
  21. package/dist/core/navigator.js +12 -0
  22. package/dist/core/notes.d.ts +11 -0
  23. package/dist/core/notes.js +34 -0
  24. package/dist/core/overview.d.ts +11 -0
  25. package/dist/core/overview.js +104 -0
  26. package/dist/core/rail.d.ts +8 -0
  27. package/dist/core/rail.js +26 -0
  28. package/dist/core/stops.d.ts +17 -0
  29. package/dist/core/stops.js +40 -0
  30. package/dist/core/transitions.d.ts +9 -0
  31. package/dist/core/transitions.js +37 -0
  32. package/dist/core/types.d.ts +70 -0
  33. package/dist/core/types.js +1 -0
  34. package/dist/rehype/hast.d.ts +26 -0
  35. package/dist/rehype/hast.js +48 -0
  36. package/dist/rehype/index.d.ts +12 -0
  37. package/dist/rehype/index.js +221 -0
  38. package/dist/rehype/page.d.ts +2 -0
  39. package/dist/rehype/page.js +84 -0
  40. package/dist/rehype/reading.d.ts +11 -0
  41. package/dist/rehype/reading.js +9 -0
  42. package/dist/rehype/titles.d.ts +19 -0
  43. package/dist/rehype/titles.js +63 -0
  44. package/dist/remark/index.d.ts +20 -0
  45. package/dist/remark/index.js +117 -0
  46. package/dist/shared/markers.d.ts +15 -0
  47. package/dist/shared/markers.js +15 -0
  48. package/dist/style.css +629 -0
  49. package/docs/CONTRACT.md +164 -0
  50. package/package.json +82 -4
  51. package/styles/base.css +3 -0
  52. package/styles/deck.css +151 -0
  53. package/styles/index.css +3 -0
@@ -0,0 +1,116 @@
1
+ /**
2
+ * What a host page does around the core, written once: open the deck when the URL or a control asks
3
+ * for it, close it on exit, and send old reveal.js links to their new position.
4
+ * `Deck.astro` calls this; a plain HTML host can call it too.
5
+ *
6
+ * | Host markup | Meaning |
7
+ * | ---------------------------------------- | ------------------------------------------------------------ |
8
+ * | `[data-ct-host]` | wrapper of one `[data-ct-deck]` |
9
+ * | `data-ct-doc-class="prose"` | classes the wrapper carries only while the deck is closed |
10
+ * | `data-ct-paging`, `data-ct-overview` | the `paging` and `overview` options of the core |
11
+ * | `data-ct-legacy` | accept `?view=slides&slide=h.v` |
12
+ * | `[data-ct-start]`, `[data-ct-start="notes"]` | a control that opens the deck or its notes view: inside a host for that deck, elsewhere for the first deck of the page |
13
+ */
14
+ import { createDeck } from "../core/index.js";
15
+ import { legacyAt, parseLegacy, readLegacy } from "./legacy.js";
16
+ const mounted = new WeakMap();
17
+ function optionsOf(host) {
18
+ const { ctPaging, ctOverview } = host.dataset;
19
+ return {
20
+ ...(ctPaging === "free" || ctPaging === "snap" ? { paging: ctPaging } : {}),
21
+ ...(ctOverview === "3d" || ctOverview === "flat" ? { overview: ctOverview } : {}),
22
+ };
23
+ }
24
+ /**
25
+ * Rewrites `?view=slides&slide=h.v` to `?view=deck&at=b.s.p` in place and returns nothing:
26
+ * the core then reads its own parameters. An old link without a match opens the outline.
27
+ */
28
+ function adoptLegacyUrl(root) {
29
+ const url = new URL(location.href);
30
+ if (url.searchParams.get("view") !== "slides")
31
+ return;
32
+ const old = parseLegacy(url.searchParams.get("slide") ?? "0");
33
+ const at = old && legacyAt(readLegacy(root), old.h, old.v);
34
+ url.searchParams.delete("slide");
35
+ url.searchParams.set("view", "deck");
36
+ if (at)
37
+ url.searchParams.set("at", at);
38
+ history.replaceState(history.state, "", url);
39
+ }
40
+ /** Wires one `[data-ct-host]`. Mounting the same element twice returns the first host. */
41
+ export function mount(host) {
42
+ const known = mounted.get(host);
43
+ if (known)
44
+ return known;
45
+ const root = host.querySelector("[data-ct-deck]");
46
+ if (!root)
47
+ return null;
48
+ const docClasses = (host.dataset.ctDocClass ?? "").split(/\s+/).filter(Boolean);
49
+ const abort = new AbortController();
50
+ let deck = null;
51
+ let opener = null;
52
+ function close() {
53
+ if (!deck)
54
+ return;
55
+ deck.destroy();
56
+ deck = null;
57
+ host.classList.add(...docClasses);
58
+ opener?.focus({ preventScroll: true });
59
+ opener = null;
60
+ }
61
+ function open(options = {}) {
62
+ if (deck)
63
+ return;
64
+ // Document-view classes (a prose class, say) would restyle the shell and the stage, so they wait outside.
65
+ host.classList.remove(...docClasses);
66
+ try {
67
+ deck = createDeck(root, { ...optionsOf(host), ...options });
68
+ }
69
+ catch (error) {
70
+ host.classList.add(...docClasses);
71
+ throw error;
72
+ }
73
+ deck.on("exit", close);
74
+ }
75
+ // In the capture phase: a client-side router that listens on the document would otherwise
76
+ // take a start link as a navigation before this handler sees it.
77
+ document.addEventListener("click", event => {
78
+ const control = event.target.closest?.("[data-ct-start]");
79
+ if (!control || event.defaultPrevented)
80
+ return;
81
+ // A modified click on a start link opens it in a new tab or window, as any link.
82
+ if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)
83
+ return;
84
+ // A control inside a host opens that deck; one elsewhere opens the first deck of the page.
85
+ if ((control.closest("[data-ct-host]") ?? document.querySelector("[data-ct-host]")) !== host)
86
+ return;
87
+ event.preventDefault();
88
+ opener = control;
89
+ open({ view: control.dataset.ctStart === "notes" ? "notes" : "deck" });
90
+ }, { capture: true, signal: abort.signal });
91
+ if ("ctLegacy" in host.dataset)
92
+ adoptLegacyUrl(root);
93
+ const view = new URLSearchParams(location.search).get("view");
94
+ if (view === "deck" || view === "notes")
95
+ open();
96
+ const api = {
97
+ get deck() {
98
+ return deck;
99
+ },
100
+ open,
101
+ close,
102
+ unmount() {
103
+ close();
104
+ abort.abort();
105
+ mounted.delete(host);
106
+ },
107
+ };
108
+ mounted.set(host, api);
109
+ return api;
110
+ }
111
+ /** Mounts every `[data-ct-host]` in the document that is not mounted yet. */
112
+ export function mountAll() {
113
+ return Array.from(document.querySelectorAll("[data-ct-host]"))
114
+ .map(mount)
115
+ .filter((host) => host !== null);
116
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Old reveal.js links (`?view=slides&slide=h.v`) to a Contour position, read from the
3
+ * `data-legacy` and `data-legacy-v` attributes of docs/CONTRACT.md.
4
+ */
5
+ /** One slide as the old deck knew it: its horizontal index, the vertical index of its first page, its page count. */
6
+ export interface LegacySlide {
7
+ legacy: number;
8
+ legacyV: number;
9
+ pages: number;
10
+ }
11
+ /** `"11"` or `"11.2"` to the old indexes; `null` when the text is not an old slide parameter. */
12
+ export declare function parseLegacy(slide: string): {
13
+ h: number;
14
+ v: number;
15
+ } | null;
16
+ /**
17
+ * The `?at=` value (1-based `b.s.p`) of the old slide `h.v`. `blocks[b][s]` describes slide `s` of block `b`.
18
+ * A `v` past the end of the old stack lands on its last page; an `h` the deck never had gives `null`.
19
+ */
20
+ export declare function legacyAt(blocks: LegacySlide[][], h: number, v: number): string | null;
21
+ /** Reads the legacy table of a deck element. Slides without `data-legacy` never match. */
22
+ export declare function readLegacy(root: Element): LegacySlide[][];
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Old reveal.js links (`?view=slides&slide=h.v`) to a Contour position, read from the
3
+ * `data-legacy` and `data-legacy-v` attributes of docs/CONTRACT.md.
4
+ */
5
+ /** `"11"` or `"11.2"` to the old indexes; `null` when the text is not an old slide parameter. */
6
+ export function parseLegacy(slide) {
7
+ const match = /^(\d+)(?:\.(\d+))?$/.exec(slide.trim());
8
+ return match ? { h: Number(match[1]), v: Number(match[2] ?? 0) } : null;
9
+ }
10
+ /**
11
+ * The `?at=` value (1-based `b.s.p`) of the old slide `h.v`. `blocks[b][s]` describes slide `s` of block `b`.
12
+ * A `v` past the end of the old stack lands on its last page; an `h` the deck never had gives `null`.
13
+ */
14
+ export function legacyAt(blocks, h, v) {
15
+ let found;
16
+ for (const [b, slides] of blocks.entries()) {
17
+ for (const [s, slide] of slides.entries()) {
18
+ if (slide.legacy !== h)
19
+ continue;
20
+ // Slides split from one old stack share `h`; the one that holds `v` starts at or before it.
21
+ if (!found || (slide.legacyV <= v && slide.legacyV >= found.slide.legacyV))
22
+ found = { b, s, slide };
23
+ }
24
+ }
25
+ if (!found)
26
+ return null;
27
+ const page = Math.min(Math.max(v - found.slide.legacyV, 0), found.slide.pages - 1);
28
+ return `${found.b + 1}.${found.s + 1}.${page + 1}`;
29
+ }
30
+ /** Reads the legacy table of a deck element. Slides without `data-legacy` never match. */
31
+ export function readLegacy(root) {
32
+ const childrenWith = (el, attribute) => Array.from(el.children).filter(child => child.hasAttribute(attribute));
33
+ const index = (el, attribute, missing) => {
34
+ const value = el.getAttribute(attribute);
35
+ return value !== null && /^\d+$/.test(value) ? Number(value) : missing;
36
+ };
37
+ return childrenWith(root, "data-ct-block").map(block => childrenWith(block, "data-ct-slide").map(slide => ({
38
+ legacy: index(slide, "data-legacy", -1),
39
+ legacyV: index(slide, "data-legacy-v", 0),
40
+ pages: childrenWith(slide, "data-ct-page").length,
41
+ })));
42
+ }
@@ -0,0 +1,2 @@
1
+ import type { Deck, DeckOptions } from "./types.js";
2
+ export declare function createDeck(root: HTMLElement, options?: DeckOptions): Deck;