rikiki-deck 0.6.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/.claude/skills/rikiki-debug/SKILL.md +17 -7
  2. package/.claude/skills/rikiki-deck/SKILL.md +362 -77
  3. package/.claude/skills/rikiki-theme/SKILL.md +1 -1
  4. package/README.md +69 -50
  5. package/bin/lib/assemble.mjs +154 -0
  6. package/bin/lib/box-geometry.mjs +66 -0
  7. package/bin/lib/browser.mjs +302 -0
  8. package/bin/lib/check-api.d.ts +28 -0
  9. package/bin/lib/check-api.mjs +6 -0
  10. package/bin/lib/check-plugins.mjs +228 -0
  11. package/bin/lib/check.mjs +1347 -0
  12. package/bin/lib/cli-error.mjs +26 -0
  13. package/bin/lib/component-deps.mjs +69 -0
  14. package/bin/lib/diff.mjs +275 -0
  15. package/bin/lib/export-pdf.mjs +65 -0
  16. package/bin/lib/graph-hit.mjs +86 -0
  17. package/bin/lib/inline.mjs +137 -39
  18. package/bin/lib/narrative.mjs +77 -0
  19. package/bin/lib/prune-icons.mjs +104 -0
  20. package/bin/lib/render.mjs +195 -0
  21. package/bin/lib/scan-external.mjs +126 -0
  22. package/bin/lib/starter.mjs +27 -14
  23. package/bin/lib/visual.mjs +120 -0
  24. package/bin/rikiki.mjs +420 -35
  25. package/dist/annotation-marks.d.ts +60 -0
  26. package/dist/annotation-marks.js +1 -0
  27. package/dist/bar-segments.d.ts +28 -0
  28. package/dist/bar-segments.js +1 -0
  29. package/dist/browser-location.d.ts +3 -0
  30. package/dist/browser-location.js +1 -0
  31. package/dist/cards-syntax.d.ts +31 -0
  32. package/dist/cards-syntax.js +6 -0
  33. package/dist/{plugins/click-stages.d.ts → click-stages.d.ts} +1 -1
  34. package/dist/deck-agenda.d.ts +25 -0
  35. package/dist/deck-agenda.js +6 -0
  36. package/dist/deck-annotate.d.ts +108 -0
  37. package/dist/deck-annotate.js +18 -0
  38. package/dist/deck-bar.d.ts +32 -0
  39. package/dist/deck-bar.js +19 -0
  40. package/dist/deck-bento.d.ts +38 -0
  41. package/dist/deck-bento.js +4 -0
  42. package/dist/{molecules/deck-callout.d.ts → deck-callout.d.ts} +2 -0
  43. package/dist/deck-callout.js +1 -1
  44. package/dist/deck-cell.d.ts +19 -0
  45. package/dist/deck-cell.js +1 -0
  46. package/dist/deck-checklist.d.ts +20 -0
  47. package/dist/deck-checklist.js +1 -0
  48. package/dist/{layouts/deck-cover.d.ts → deck-cover.d.ts} +8 -0
  49. package/dist/deck-cover.js +9 -6
  50. package/dist/deck-csv.d.ts +38 -0
  51. package/dist/deck-csv.js +15 -0
  52. package/dist/deck-feature-cards.js +2 -2
  53. package/dist/deck-feature.d.ts +18 -0
  54. package/dist/deck-feature.js +2 -2
  55. package/dist/deck-figure.d.ts +26 -0
  56. package/dist/deck-figure.js +8 -0
  57. package/dist/deck-fit.d.ts +14 -0
  58. package/dist/deck-fit.js +1 -0
  59. package/dist/deck-flow.d.ts +41 -0
  60. package/dist/deck-flow.js +7 -0
  61. package/dist/deck-graph.d.ts +92 -0
  62. package/dist/deck-graph.js +25 -0
  63. package/dist/deck-grid.js +1 -1
  64. package/dist/deck-icon.d.ts +20 -0
  65. package/dist/deck-icon.js +1 -0
  66. package/dist/{atoms/deck-kicker.d.ts → deck-kicker.d.ts} +5 -0
  67. package/dist/deck-kicker.js +1 -1
  68. package/dist/deck-kpi-grid.d.ts +26 -0
  69. package/dist/deck-kpi-grid.js +4 -0
  70. package/dist/deck-link.d.ts +21 -0
  71. package/dist/deck-link.js +1 -0
  72. package/dist/{molecules/deck-md.d.ts → deck-md.d.ts} +3 -0
  73. package/dist/deck-md.js +8 -3
  74. package/dist/deck-mermaid.js +15 -3
  75. package/dist/deck-outline.d.ts +50 -0
  76. package/dist/deck-outline.js +1 -0
  77. package/dist/deck-overview.js +53 -39
  78. package/dist/deck-persona.d.ts +31 -0
  79. package/dist/deck-persona.js +6 -0
  80. package/dist/deck-photo.js +1 -1
  81. package/dist/deck-point.d.ts +22 -0
  82. package/dist/deck-point.js +1 -0
  83. package/dist/deck-presenter.js +120 -48
  84. package/dist/deck-pull.d.ts +13 -0
  85. package/dist/deck-pull.js +1 -0
  86. package/dist/{atoms/deck-punch.d.ts → deck-punch.d.ts} +6 -0
  87. package/dist/deck-punch.js +1 -1
  88. package/dist/deck-quote.d.ts +28 -0
  89. package/dist/deck-quote.js +6 -0
  90. package/dist/{runtime/deck-root.d.ts → deck-root.d.ts} +88 -8
  91. package/dist/deck-root.js +17 -13
  92. package/dist/deck-section.js +2 -2
  93. package/dist/deck-source.d.ts +12 -0
  94. package/dist/deck-source.js +2 -0
  95. package/dist/deck-split.d.ts +30 -0
  96. package/dist/deck-split.js +5 -3
  97. package/dist/{molecules/deck-stat.d.ts → deck-stat.d.ts} +2 -0
  98. package/dist/deck-stat.js +2 -2
  99. package/dist/{molecules/deck-step-list.d.ts → deck-step-list.d.ts} +8 -0
  100. package/dist/deck-step-list.js +4 -2
  101. package/dist/deck-table.d.ts +26 -0
  102. package/dist/deck-table.js +1 -0
  103. package/dist/deck-takeaway.d.ts +18 -0
  104. package/dist/deck-takeaway.js +2 -2
  105. package/dist/deck-timeline.d.ts +37 -0
  106. package/dist/deck-timeline.js +5 -0
  107. package/dist/deck-transition.js +3 -3
  108. package/dist/deck-versus.d.ts +18 -0
  109. package/dist/deck-versus.js +9 -0
  110. package/dist/deep-link.d.ts +29 -0
  111. package/dist/deep-link.js +1 -0
  112. package/dist/escape-html.d.ts +3 -0
  113. package/dist/escape-html.js +1 -0
  114. package/dist/fit-controller.d.ts +27 -0
  115. package/dist/fit-controller.js +1 -0
  116. package/dist/graph-layout.d.ts +35 -0
  117. package/dist/graph-layout.js +1 -0
  118. package/dist/grid-tracks.d.ts +17 -0
  119. package/dist/grid-tracks.js +1 -0
  120. package/dist/icon-set.d.ts +6 -0
  121. package/dist/icon-set.js +1 -0
  122. package/dist/index.d.ts +37 -31
  123. package/dist/index.js +95 -49
  124. package/dist/keymap.d.ts +40 -0
  125. package/dist/keymap.js +1 -0
  126. package/dist/mouse-nav.d.ts +12 -0
  127. package/dist/mouse-nav.js +1 -0
  128. package/dist/navigation.d.ts +25 -0
  129. package/dist/navigation.js +1 -0
  130. package/dist/parse-csv.d.ts +9 -0
  131. package/dist/parse-csv.js +3 -0
  132. package/dist/shared-styles.js +1 -1
  133. package/dist/shiki.d.ts +8 -0
  134. package/dist/signature.d.ts +2 -0
  135. package/dist/signature.js +1 -0
  136. package/dist/slide-fill.d.ts +8 -0
  137. package/dist/slide-fill.js +1 -0
  138. package/dist/standalone.js +301 -169
  139. package/dist/vendor/THIRD-PARTY-NOTICES.txt +4347 -0
  140. package/dist/vendor/inventory.json +3029 -0
  141. package/dist/vendor/lit.js +62 -2
  142. package/dist/vendor/mermaid.min.js +95 -95
  143. package/dist/vendor/shiki.js +1 -57
  144. package/dist/viewport.d.ts +42 -0
  145. package/dist/viewport.js +1 -0
  146. package/docs/llms/rikiki-reference.md +955 -64
  147. package/docs/llms/rikiki-workflow.md +536 -0
  148. package/llms.txt +39 -12
  149. package/package.json +33 -12
  150. package/themes/rikiki.css +173 -47
  151. package/themes/siliceum.css +171 -51
  152. package/dist/layouts/deck-feature.d.ts +0 -11
  153. package/dist/layouts/deck-split.d.ts +0 -18
  154. package/dist/layouts/deck-takeaway.d.ts +0 -11
  155. package/dist/plugins/shiki.d.ts +0 -8
  156. /package/dist/{runtime/color.d.ts → color.d.ts} +0 -0
  157. /package/dist/{atoms/deck-badge.d.ts → deck-badge.d.ts} +0 -0
  158. /package/dist/{molecules/deck-card.d.ts → deck-card.d.ts} +0 -0
  159. /package/dist/{atoms/deck-code-highlighter.d.ts → deck-code-highlighter.d.ts} +0 -0
  160. /package/dist/{atoms/deck-code.d.ts → deck-code.d.ts} +0 -0
  161. /package/dist/{layouts/deck-feature-cards.d.ts → deck-feature-cards.d.ts} +0 -0
  162. /package/dist/{molecules/deck-grid.d.ts → deck-grid.d.ts} +0 -0
  163. /package/dist/{runtime/deck-help.d.ts → deck-help.d.ts} +0 -0
  164. /package/dist/{molecules/deck-mermaid.d.ts → deck-mermaid.d.ts} +0 -0
  165. /package/dist/{molecules/deck-metric.d.ts → deck-metric.d.ts} +0 -0
  166. /package/dist/{runtime/deck-notes.d.ts → deck-notes.d.ts} +0 -0
  167. /package/dist/{runtime/deck-overview.d.ts → deck-overview.d.ts} +0 -0
  168. /package/dist/{layouts/deck-photo.d.ts → deck-photo.d.ts} +0 -0
  169. /package/dist/{runtime/deck-presenter.d.ts → deck-presenter.d.ts} +0 -0
  170. /package/dist/{layouts/deck-section.d.ts → deck-section.d.ts} +0 -0
  171. /package/dist/{molecules/deck-shortcut.d.ts → deck-shortcut.d.ts} +0 -0
  172. /package/dist/{molecules/deck-stack.d.ts → deck-stack.d.ts} +0 -0
  173. /package/dist/{molecules/deck-tier-list.d.ts → deck-tier-list.d.ts} +0 -0
  174. /package/dist/{runtime/deck-transition.d.ts → deck-transition.d.ts} +0 -0
@@ -1,23 +1,39 @@
1
- var H="rik-presenter",R=new URL("./index.js",import.meta.url).href,l=null,p=null,u=null,f=null,h=!1;async function I(){if(f)return f;let e=window.getScreenDetails;if(!e)return null;try{return f=await e.call(window),f}catch{return null}}var g=1280,y=720;function C(e){return{left:Math.round(e.availLeft+(e.availWidth-g)/2),top:Math.round(e.availTop+(e.availHeight-y)/2)}}function M(e){let{left:n,top:r}=C(e);return`popup=yes,width=${g},height=${y},left=${n},top=${r}`}function T(e,n){let{left:r,top:t}=C(n);e.resizeTo(g,y),e.moveTo(r,t)}function S(e,n){e.requestFullscreen?.({screen:n}).then(()=>{if(!u?.has(e)){document.exitFullscreen?.().catch(()=>{});return}h=!0}).catch(()=>{})}function P(){document.fullscreenElement||(h=!1)}function B(){document.removeEventListener("fullscreenchange",P),h&&document.fullscreenElement&&document.exitFullscreen?.().catch(()=>{}),h=!1}function v(e){l?.close(),l=null,p?.close(),p=null,u?.delete(e),e.presenterActive=!1,B()}function D(e){let n=Array.from(e.children).filter(i=>i.tagName.toLowerCase().startsWith("deck-")),r=n.findIndex(i=>i.hasAttribute("active")),t=n[r]??null,d=n[r+1]??null,b=(t?.querySelector("deck-notes")?.textContent??"").trim(),o=document.querySelector('link[rel="stylesheet"][href*="rikiki"], link[rel="stylesheet"][href*="tokens"], link[rel="stylesheet"][href*="theme"]')?.href??"",a=Array.from(document.querySelectorAll("style")).map(i=>i.textContent??"").join(`
2
- `),m=document.querySelector('script[type="module"][data-rikiki-bundle]')?.textContent??"";return{current:r+1,total:n.length,slideHtml:t?.outerHTML??"",nextHtml:d?.outerHTML??null,notes:b,themeHref:o,inlineStyles:a,bundleHref:R,bundleInline:m}}function E(e){p&&p.postMessage({type:"state",state:D(e)})}var W=e=>`<!doctype html>
1
+ var R={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"};function S(e){return e==null?"":String(e).replace(/[&<>"']/g,n=>R[n])}var A="rik-presenter",D=new URL("./index.js",import.meta.url).href,a=null,p=null,u=null,f=null,h=!1;async function T(){if(f)return f;let e=window.getScreenDetails;if(!e)return null;try{return f=await e.call(window),f}catch{return null}}var g=1280,y=720;function C(e){return{left:Math.round(e.availLeft+(e.availWidth-g)/2),top:Math.round(e.availTop+(e.availHeight-y)/2)}}function M(e){let{left:n,top:r}=C(e);return`popup=yes,width=${g},height=${y},left=${n},top=${r}`}function $(e,n){let{left:r,top:t}=C(n);e.resizeTo(g,y),e.moveTo(r,t)}function E(e,n){e.requestFullscreen?.({screen:n}).then(()=>{if(!u?.has(e)){document.exitFullscreen?.().catch(()=>{});return}h=!0}).catch(()=>{})}function I(){document.fullscreenElement||(h=!1)}function B(){document.removeEventListener("fullscreenchange",I),h&&document.fullscreenElement&&document.exitFullscreen?.().catch(()=>{}),h=!1}function w(e){a?.close(),a=null,p?.close(),p=null,u?.delete(e),e.presenterActive=!1,B()}function P(e){let n=Array.from(e.children).filter(s=>s.tagName.toLowerCase().startsWith("deck-")),r=n.findIndex(s=>s.hasAttribute("active")),t=n[r]??null,d=n[r+1]??null,b=(t?.querySelector("deck-notes")?.textContent??"").trim(),o=document.querySelector('link[rel="stylesheet"][href*="rikiki"], link[rel="stylesheet"][href*="tokens"], link[rel="stylesheet"][href*="theme"]')?.href??"",l=Array.from(document.querySelectorAll("style")).map(s=>s.textContent??"").join(`
2
+ `),m=document.querySelector('script[type="module"][data-rikiki-bundle]')?.textContent??"";return{current:r+1,total:n.length,step:e.step,steps:Number(t?.getAttribute("steps")||t?.getAttribute("data-steps")||0),slideHtml:t?.outerHTML??"",nextHtml:d?.outerHTML??null,notes:b,themeHref:o,previewStyles:Array.from(document.querySelectorAll('link[rel="stylesheet"], style')).map(s=>s.outerHTML).join(`
3
+ `),moduleHrefs:Array.from(document.querySelectorAll('script[type="module"][src]')).map(s=>s.src),baseHref:document.baseURI,lang:document.documentElement.lang,canvasWidth:e.width,canvasHeight:e.height,inlineStyles:l,bundleHref:D,bundleInline:m}}function H(e){p&&p.postMessage({type:"state",state:P(e)})}var N=e=>`<!doctype html>
3
4
  <html lang="en">
4
5
  <head>
5
6
  <meta charset="utf-8">
6
7
  <title>Rikiki \xB7 presenter</title>
7
- ${e.themeHref?`<link rel="stylesheet" href="${e.themeHref}">`:""}
8
+ ${e.themeHref?`<link rel="stylesheet" href="${S(e.themeHref)}">`:""}
8
9
  <style>
9
10
  html, body { margin: 0; padding: 0; height: 100%; background: #0f1422; color: #fafafa; font-family: var(--rik-font-sans, system-ui); }
10
11
  .grid {
11
12
  display: grid;
12
13
  grid-template-columns: 2fr 1fr;
13
- grid-template-rows: 1fr auto;
14
+ /* Next is sized to its 16:9 content (top-right); notes fill the rest of the
15
+ right column; current spans the full left height. Avoids the dead bands a
16
+ full-height narrow Next pane left around a small 16:9 thumbnail. */
17
+ grid-template-rows: auto 1fr auto;
18
+ grid-template-areas:
19
+ "current next"
20
+ "current notes"
21
+ "footer footer";
14
22
  gap: clamp(8px, 1.5vw, 16px);
15
23
  padding: clamp(8px, 1.5vw, 16px);
16
24
  height: 100vh;
17
25
  box-sizing: border-box;
18
26
  }
27
+ #current { grid-area: current; }
28
+ #next { grid-area: next; }
29
+ #notes-panel { grid-area: notes; }
30
+ #footer { grid-area: footer; }
19
31
  @media (max-width: 1000px) {
20
- .grid { grid-template-columns: 1fr; grid-template-rows: 1fr 1fr auto auto; }
32
+ .grid {
33
+ grid-template-columns: 1fr;
34
+ grid-template-rows: auto auto 1fr auto;
35
+ grid-template-areas: "current" "next" "notes" "footer";
36
+ }
21
37
  }
22
38
  .panel {
23
39
  background: #1e2840;
@@ -36,17 +52,31 @@ ${e.themeHref?`<link rel="stylesheet" href="${e.themeHref}">`:""}
36
52
  background: #161c2e;
37
53
  }
38
54
  .panel .body { flex: 1; min-height: 0; padding: 16px; overflow: hidden; border-radius: 8px; }
39
- /* Preview panes center a 16:9 box so the thumbnail matches the projection
40
- geometry regardless of the pane/window shape (issue #5) \xB7 the size
55
+ /* Current centers a 16:9 box in its (tall) pane so the thumbnail matches the
56
+ projection geometry regardless of pane shape (issue #5) \xB7 the size
41
57
  container lets the iframe size against the pane in cq units. */
42
- #current .body, #next .body { display: grid; place-items: center; container-type: size; }
58
+ #current .body { display: grid; place-items: center; container-type: size; }
43
59
  .panel iframe { border: 0; background: #0f1422; display: block; }
44
- #current-frame, #next-frame {
60
+ #current-viewport {
45
61
  aspect-ratio: 16 / 9;
46
62
  width: min(100cqw, calc(100cqh * 16 / 9));
47
63
  height: auto;
48
64
  max-width: 100%;
49
65
  }
66
+ /* Next sizes its 16:9 box from the column width (its pane row is auto), so
67
+ the panel hugs the thumbnail instead of stretching full-height. */
68
+ #next-viewport {
69
+ aspect-ratio: 16 / 9;
70
+ width: 100%;
71
+ height: auto;
72
+ display: block;
73
+ }
74
+ .preview-viewport { position: relative; overflow: hidden; }
75
+ .preview-viewport iframe {
76
+ position: absolute; top: 0; left: 0;
77
+ width: ${e.canvasWidth}px; height: ${e.canvasHeight}px;
78
+ transform-origin: top left;
79
+ }
50
80
  #notes { font-size: 17px; line-height: 1.6; white-space: pre-wrap; padding: 20px; overflow: auto; color: #e8e4f0; }
51
81
  #notes:empty::before { content: 'No notes for this slide.'; color: rgba(232,228,240,0.4); font-style: italic; }
52
82
  #footer {
@@ -78,19 +108,19 @@ ${e.themeHref?`<link rel="stylesheet" href="${e.themeHref}">`:""}
78
108
  <div class="grid">
79
109
  <section class="panel" id="current">
80
110
  <header>Current</header>
81
- <div class="body"><iframe id="current-frame" srcdoc=""></iframe></div>
111
+ <div class="body"><div class="preview-viewport" id="current-viewport"><iframe id="current-frame" srcdoc=""></iframe></div></div>
82
112
  </section>
83
113
  <section class="panel" id="next">
84
114
  <header>Next</header>
85
- <div class="body"><iframe id="next-frame" srcdoc=""></iframe></div>
115
+ <div class="body"><div class="preview-viewport" id="next-viewport"><iframe id="next-frame" srcdoc=""></iframe></div></div>
86
116
  </section>
87
- <section class="panel" id="notes-panel" style="grid-column: 1 / -1;">
117
+ <section class="panel" id="notes-panel">
88
118
  <header>Speaker notes</header>
89
119
  <div id="notes" class="body"></div>
90
120
  </section>
91
121
  <div id="footer">
92
122
  <div><span id="timer">00:00</span> <button id="timer-toggle">Pause</button> <button id="timer-reset">Reset</button></div>
93
- <div id="counter">${e.current} / ${e.total}</div>
123
+ <div><span id="counter">${e.current} / ${e.total}</span> <span id="stepper"></span></div>
94
124
  <div>
95
125
  <label class="opt"><input type="checkbox" id="opt-advance" checked> Advance on click</label>
96
126
  <span class="ghost">\xB7 P to close</span>
@@ -98,9 +128,17 @@ ${e.themeHref?`<link rel="stylesheet" href="${e.themeHref}">`:""}
98
128
  </div>
99
129
  </div>
100
130
  <script>
101
- const channel = new BroadcastChannel('${H}');
131
+ const channel = new BroadcastChannel('${A}');
102
132
  const current = document.getElementById('current-frame');
103
133
  const next = document.getElementById('next-frame');
134
+
135
+
136
+ const previewResize = new ResizeObserver(entries => {
137
+ for (const entry of entries) {
138
+ entry.target.querySelector('iframe').style.transform = 'scale(' + (entry.contentRect.width / ${e.canvasWidth}) + ')';
139
+ }
140
+ });
141
+ document.querySelectorAll('.preview-viewport').forEach(el => previewResize.observe(el));
104
142
  const notes = document.getElementById('notes');
105
143
  const counter = document.getElementById('counter');
106
144
  const timerEl = document.getElementById('timer');
@@ -128,25 +166,30 @@ ${e.themeHref?`<link rel="stylesheet" href="${e.themeHref}">`:""}
128
166
  };
129
167
  resetBtn.onclick = () => { startedAt = Date.now(); elapsed = 0; running = true; toggleBtn.textContent = 'Pause'; };
130
168
 
131
- // Presentation options \xB7 push each change to the live deck over the channel.
132
169
  document.getElementById('opt-advance').addEventListener('change', (e) => {
133
170
  channel.postMessage({ type: 'config', advanceOnClick: e.target.checked });
134
171
  });
135
172
 
136
173
  function wrapFrame(slideHtml, forward) {
137
- const themeHref = ${JSON.stringify(e.themeHref)};
138
- const inlineStyles = ${JSON.stringify(e.inlineStyles)};
174
+ const previewStyles = ${JSON.stringify(e.previewStyles).replace(/</g,"\\u003c")};
175
+ const moduleHrefs = ${JSON.stringify(e.moduleHrefs).replace(/</g,"\\u003c")};
176
+ const baseHref = ${JSON.stringify(e.baseHref).replace(/</g,"\\u003c")};
177
+ const lang = ${JSON.stringify(e.lang).replace(/</g,"\\u003c")};
139
178
  const bundleHref = ${JSON.stringify(e.bundleHref)};
140
179
  const bundleInline = ${JSON.stringify(e.bundleInline)};
141
- const themeLink = themeHref ? '<link rel="stylesheet" href="' + themeHref + '">' : '';
142
- const themeStyle = inlineStyles ? '<style>' + inlineStyles + '</style>' : '';
143
- // Bootstrap rikiki inside the iframe so its <deck-*> elements upgrade.
144
- // \xB7 single-file deck \u2192 the framework is inlined and tagged \xB7 re-inline it
145
- // so the module base is the iframe document URL (a <script src="data:">
146
- // or a non-existent ./index.js would break new URL(rel, import.meta.url)
147
- // and abort registration). Escape any script end-tag so it can't close
148
- // this block early (this comment must avoid the literal too).
149
- // \xB7 served deck \u2192 load the real bundle URL with <script src>.
180
+
181
+
182
+
183
+
184
+ const escAttr = (s) => s.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;');
185
+ const escStyle = (s) => s.replace(/<\\/(style|script)/gi, '<\\\\/$1');
186
+
187
+
188
+
189
+
190
+
191
+
192
+
150
193
  let bundleTag;
151
194
  const esc = (c) => c.replace(/<\\/script/gi, '<\\\\/script');
152
195
  if (bundleInline) {
@@ -160,12 +203,12 @@ ${e.themeHref?`<link rel="stylesheet" href="${e.themeHref}">`:""}
160
203
  } else {
161
204
  bundleTag = '<script type="module" src="' + bundleHref + '"><' + '/script>';
162
205
  }
163
- // Mark the cloned slide [active] so its real component CSS applies
164
- // (:host([active]){display:flex}) instead of forcing display via !important.
206
+
207
+
165
208
  const activeSlide = slideHtml.replace(/^(\\s*<deck-[a-z-]+)/i, '$1 active');
166
- // Only the "Current" pane is a control surface \xB7 it captures key/click/wheel
167
- // and posts them to this popup window, which relays them onto the channel so
168
- // the live deck acts on them. Coords are normalised 0..1 over the iframe.
209
+
210
+
211
+
169
212
  const forwarder = forward
170
213
  ? '<scr' + 'ipt>(function(){' +
171
214
  'var post=function(o){o.source="rikiki-presenter-input";parent.postMessage(o,"*");};' +
@@ -174,47 +217,76 @@ ${e.themeHref?`<link rel="stylesheet" href="${e.themeHref}">`:""}
174
217
  'addEventListener("wheel",function(e){if(e.ctrlKey||e.metaKey)return;post({type:"wheel",x:e.clientX/innerWidth,y:e.clientY/innerHeight,dx:e.deltaX,dy:e.deltaY});},{passive:true});' +
175
218
  '})();<' + '/scr' + 'ipt>'
176
219
  : '';
177
- // The deck always letterboxes into its logical canvas, so the slide keeps
178
- // its 16:9 proportions regardless of the pane's shape \xB7 just drop the
179
- // hint / nav-arrow chrome for a clean, correctly-shaped thumbnail.
180
- return '<!doctype html><html><head><meta charset="UTF-8">' + themeLink + themeStyle +
181
- bundleTag +
220
+
221
+
222
+
223
+ return '<!doctype html><html lang="' + escAttr(lang) + '"><head><meta charset="UTF-8"><base href="' + escAttr(baseHref) + '">' + previewStyles +
224
+ bundleTag + moduleHrefs.filter((href) => href !== bundleHref).map((href) => '<script type="module" src="' + escAttr(href) + '"><' + '/script>').join('') +
182
225
  '<style>html,body{margin:0;padding:0;height:100%;overflow:hidden;background:#0f1422}' +
183
226
  'deck-root{position:absolute;inset:0}</style>' +
184
- '</head><body><deck-root no-hint no-arrows no-counter preview>' + activeSlide + '</deck-root>' + forwarder + '</body></html>';
227
+ '</head><body><deck-root width="${e.canvasWidth}" height="${e.canvasHeight}" no-hint no-arrows no-counter preview data-overview-snapshot>' + activeSlide + '</deck-root>' + forwarder + '</body></html>';
185
228
  }
186
229
 
230
+
231
+ const frames = new Map();
232
+ function updateFrame(frame, html, step, forward) {
233
+ let state = frames.get(frame);
234
+ if (!state) {
235
+ state = { html, step, ready: false, painted: null };
236
+ frames.set(frame, state);
237
+ frame.addEventListener('load', () => {
238
+ state.ready = true;
239
+ paint();
240
+ });
241
+ frame.srcdoc = wrapFrame('', forward);
242
+ }
243
+ state.html = html;
244
+ state.step = step;
245
+ function paint() {
246
+ if (!state.ready) return;
247
+ const root = frame.contentDocument?.querySelector('deck-root');
248
+ if (!root) return;
249
+ if (state.painted !== state.html) {
250
+ root.innerHTML = state.html || '<div style="padding:3rem;color:white">End of deck</div>';
251
+ root.firstElementChild?.setAttribute('active', '');
252
+ state.painted = state.html;
253
+ }
254
+
255
+ Promise.resolve().then(async () => {
256
+ const elements = [root.firstElementChild, ...root.querySelectorAll('*')].filter(Boolean);
257
+ await Promise.all(elements.map(el => el.updateComplete));
258
+ elements.forEach(el => el.applyStep?.(state.step));
259
+ });
260
+ }
261
+ paint();
262
+ }
187
263
  channel.onmessage = (e) => {
188
264
  if (e.data?.type !== 'state') return;
189
265
  const s = e.data.state;
190
266
  counter.textContent = s.current + ' / ' + s.total;
267
+ document.getElementById('stepper').textContent = s.steps ? ' \xB7 \xC9tape ' + s.step + ' / ' + s.steps : '';
191
268
  notes.textContent = s.notes;
192
- if (s.slideHtml) current.srcdoc = wrapFrame(s.slideHtml, true);
193
- if (s.nextHtml) next.srcdoc = wrapFrame(s.nextHtml, false);
194
- else next.srcdoc = '<!doctype html><html><body style="background:#0f1422;color:rgba(232,228,240,0.4);display:flex;align-items:center;justify-content:center;font-family:system-ui">End of deck</body></html>';
269
+ updateFrame(current, s.slideHtml, s.step, true);
270
+ updateFrame(next, s.nextHtml, 0, false);
195
271
  };
196
272
 
197
- // Initial paint from the seed state
198
- const seed = ${JSON.stringify(e)};
273
+ const seed = ${JSON.stringify(e).replace(/</g,"\\u003c")};
199
274
  channel.postMessage({ type: 'state', state: seed });
200
275
 
201
- // Forward keys back to the main window (so the speaker can drive nav from the laptop)
202
276
  window.addEventListener('keydown', (e) => {
203
277
  if (e.target.matches && e.target.matches('input,textarea,button')) return;
204
278
  channel.postMessage({ type: 'key', key: e.key, shift: e.shiftKey });
205
279
  });
206
280
 
207
- // Relay input captured inside the "Current" preview iframe (key/click/wheel)
208
- // onto the channel \xB7 the iframe is a separate browsing context so its events
209
- // never reach this window directly \xB7 it postMessages them here instead.
281
+
282
+
210
283
  window.addEventListener('message', (e) => {
211
284
  const d = e.data;
212
285
  if (!d || d.source !== 'rikiki-presenter-input') return;
213
286
  channel.postMessage({ type: d.type, key: d.key, shift: d.shift, x: d.x, y: d.y, dx: d.dx, dy: d.dy });
214
287
  });
215
288
 
216
- // Tell main window we're alive
217
289
  channel.postMessage({ type: 'hello' });
218
290
  <\/script>
219
291
  </body>
220
- </html>`;function F(e,n){let r=document.elementFromPoint(e,n);for(;r?.shadowRoot;){let t=r.shadowRoot.elementFromPoint(e,n);if(!t||t===r)break;r=t}return r}function $(e,n,r){for(let t=e;t;t=t.parentElement){let d=getComputedStyle(t);if(r!==0&&t.scrollHeight>t.clientHeight&&/auto|scroll/.test(d.overflowY)||n!==0&&t.scrollWidth>t.clientWidth&&/auto|scroll/.test(d.overflowX))return t}return null}function N(e,n,r){let t=e.getBoundingClientRect();return{x:t.left+n*t.width,y:t.top+r*t.height}}function L(e,n){let{x:r,y:t}=N(e,n.x??.5,n.y??.5);return{x:r,y:t,target:F(r,t)??e}}function A(e){let n=(e??"all").trim();return n==="none"?"none":n===""||n==="all"?"wheel arrows aux":n.split(/\s+/).filter(r=>r!=="click").join(" ")||"none"}function O(e){if(u=u??new WeakSet,u.has(e)){v(e);return}u.add(e);let n=e.mouseNav;p=new BroadcastChannel(H),e.addEventListener("slide-change",()=>E(e)),p.addEventListener("message",s=>{let o=s.data;if(o?.type==="key"&&o.key)window.dispatchEvent(new KeyboardEvent("keydown",{key:o.key,shiftKey:!!o.shift,bubbles:!0}));else if(o?.type==="click"){let{x:a,y:m,target:i}=L(e,o),c={bubbles:!0,composed:!0,cancelable:!0,clientX:a,clientY:m,view:window,shiftKey:!!o.shift};i.dispatchEvent(new PointerEvent("pointerdown",{...c,pointerId:1,isPrimary:!0})),i.dispatchEvent(new PointerEvent("pointerup",{...c,pointerId:1,isPrimary:!0})),i.dispatchEvent(new MouseEvent("click",c))}else if(o?.type==="wheel"){let{x:a,y:m,target:i}=L(e,o),c=o.dx??0,k=o.dy??0,x=$(i,c,k);x?x.scrollBy({left:c,top:k}):i.dispatchEvent(new WheelEvent("wheel",{bubbles:!0,composed:!0,cancelable:!0,clientX:a,clientY:m,deltaX:c,deltaY:k,view:window}))}else o?.type==="config"&&typeof o.advanceOnClick=="boolean"?e.mouseNav=o.advanceOnClick?n:A(n):o?.type==="hello"&&E(e)});let r=f,t=r?.screens.find(s=>s!==r.currentScreen)??null;t?S(e,t):I().then(s=>{if(!s)return;let o=s.screens.find(a=>a!==s.currentScreen);o&&S(e,o),l&&s.currentScreen&&T(l,s.currentScreen)});let d=D(e),w=r?.currentScreen?M(r.currentScreen):`popup=yes,width=${g},height=${y}`;if(l=window.open("","rikiki-presenter",w),!l){console.warn("[rikiki/presenter] popup was blocked \xB7 allow popups for this site"),v(e);return}l.document.open(),l.document.write(W(d)),l.document.close(),e.presenterActive=!0,document.addEventListener("fullscreenchange",P);let b=setInterval(()=>{l?.closed&&(clearInterval(b),v(e))},1e3)}export{O as installPresenter};
292
+ </html>`;function W(e,n){let r=document.elementFromPoint(e,n);for(;r?.shadowRoot;){let t=r.shadowRoot.elementFromPoint(e,n);if(!t||t===r)break;r=t}return r}function q(e,n,r){for(let t=e;t;t=t.parentElement){let d=getComputedStyle(t);if(r!==0&&t.scrollHeight>t.clientHeight&&/auto|scroll/.test(d.overflowY)||n!==0&&t.scrollWidth>t.clientWidth&&/auto|scroll/.test(d.overflowX))return t}return null}function F(e,n,r){let t=e.getBoundingClientRect();return{x:t.left+n*t.width,y:t.top+r*t.height}}function L(e,n){let{x:r,y:t}=F(e,n.x??.5,n.y??.5);return{x:r,y:t,target:W(r,t)??e}}function O(e){let n=(e??"all").trim();return n==="none"?"none":n===""||n==="all"?"wheel arrows aux":n.split(/\s+/).filter(r=>r!=="click").join(" ")||"none"}function j(e){if(u=u??new WeakSet,u.has(e)){w(e);return}u.add(e);let n=e.mouseNav;p=new BroadcastChannel(A),e.addEventListener("step-change",()=>H(e)),p.addEventListener("message",i=>{let o=i.data;if(o?.type==="key"&&o.key)window.dispatchEvent(new KeyboardEvent("keydown",{key:o.key,shiftKey:!!o.shift,bubbles:!0}));else if(o?.type==="click"){let{x:l,y:m,target:s}=L(e,o),c={bubbles:!0,composed:!0,cancelable:!0,clientX:l,clientY:m,view:window,shiftKey:!!o.shift};s.dispatchEvent(new PointerEvent("pointerdown",{...c,pointerId:1,isPrimary:!0})),s.dispatchEvent(new PointerEvent("pointerup",{...c,pointerId:1,isPrimary:!0})),s.dispatchEvent(new MouseEvent("click",c))}else if(o?.type==="wheel"){let{x:l,y:m,target:s}=L(e,o),c=o.dx??0,v=o.dy??0,x=q(s,c,v);x?x.scrollBy({left:c,top:v}):s.dispatchEvent(new WheelEvent("wheel",{bubbles:!0,composed:!0,cancelable:!0,clientX:l,clientY:m,deltaX:c,deltaY:v,view:window}))}else o?.type==="config"&&typeof o.advanceOnClick=="boolean"?e.mouseNav=o.advanceOnClick?n:O(n):o?.type==="hello"&&H(e)});let r=f,t=r?.screens.find(i=>i!==r.currentScreen)??null;t?E(e,t):T().then(i=>{if(!i)return;let o=i.screens.find(l=>l!==i.currentScreen);o&&E(e,o),a&&i.currentScreen&&$(a,i.currentScreen)});let d=P(e),k=r?.currentScreen?M(r.currentScreen):`popup=yes,width=${g},height=${y}`;if(a=window.open("","rikiki-presenter",k),!a){console.warn("[rikiki/presenter] popup was blocked \xB7 allow popups for this site"),w(e);return}a.document.open(),a.document.write(N(d)),a.document.close(),e.presenterActive=!0,document.addEventListener("fullscreenchange",I);let b=setInterval(()=>{a?.closed&&(clearInterval(b),w(e))},1e3)}export{j as installPresenter};
@@ -0,0 +1,13 @@
1
+ import { LitElement } from 'lit';
2
+ export type DeckPullSide = 'left' | 'right' | 'full';
3
+ export declare class DeckPull extends LitElement {
4
+ static styles: import("lit").CSSResult;
5
+ /** `full` (default), or floated to one side so the text wraps around it. */
6
+ side: DeckPullSide;
7
+ render(): import("lit-html").TemplateResult<1>;
8
+ }
9
+ declare global {
10
+ interface HTMLElementTagNameMap {
11
+ 'deck-pull': DeckPull;
12
+ }
13
+ }
@@ -0,0 +1 @@
1
+ var s=Object.defineProperty;var c=Object.getOwnPropertyDescriptor;var p=(a,t,i,l)=>{for(var e=l>1?void 0:l?c(t,i):t,d=a.length-1,o;d>=0;d--)(o=a[d])&&(e=(l?o(t,i,e):o(e))||e);return l&&e&&s(t,i,e),e};import{LitElement as n,css as f,html as g}from"./vendor/lit.js";import{customElement as k,property as v}from"./vendor/lit.js";var r=class extends n{constructor(){super(...arguments);this.side="full"}render(){return g`<slot></slot>`}};r.styles=f`:host{display:block;font-family:var(--deck-pull-font,var(--rik-font-display,var(--rik-font-sans)));font-size:var(--deck-pull-size,var(--rik-font-size-big));line-height:1.15;letter-spacing:-0.02em;font-weight:800;color:var(--deck-pull-color,var(--rik-text-default));background:var(--deck-pull-bg,transparent);padding:var(--deck-pull-padding,var(--rik-space-2) 0);border-left:var(--deck-pull-rule-width,4px) solid var(--deck-pull-rule,var(--rik-accent));padding-left:var(--rik-space-4);text-wrap:balance}:host([side='left']),:host([side='right']){width:var(--deck-pull-width,38%);margin-block:var(--rik-space-2)}:host([side='left']){float:left;margin-right:var(--rik-space-4)}:host([side='right']){float:right;margin-left:var(--rik-space-4);border-left:0;border-right:var(--deck-pull-rule-width,4px) solid var(--deck-pull-rule,var(--rik-accent));padding-left:0;padding-right:var(--rik-space-4);text-align:right}@media print{:host{-webkit-print-color-adjust:exact;print-color-adjust:exact}}`,p([v({type:String,reflect:!0})],r.prototype,"side",2),r=p([k("deck-pull")],r);export{r as DeckPull};
@@ -7,6 +7,12 @@ export declare class DeckPunch extends LitElement {
7
7
  size?: DeckPunchSize;
8
8
  weight?: string;
9
9
  align?: string;
10
+ /** Shrink to fit the box (overrides `size`/cqw fluid scaling). */
11
+ fit: boolean;
12
+ /** fit floor / ceiling in rem · default 1 / 12. */
13
+ fitMin?: number;
14
+ fitMax?: number;
15
+ private fitter;
10
16
  updated(): void;
11
17
  render(): import("lit-html").TemplateResult<1>;
12
18
  }
@@ -1 +1 @@
1
- var p=Object.defineProperty;var d=Object.getOwnPropertyDescriptor;var r=(n,i,o,s)=>{for(var t=s>1?void 0:s?d(i,o):i,l=n.length-1,c;l>=0;l--)(c=n[l])&&(t=(s?c(i,o,t):c(t))||t);return s&&t&&p(i,o,t),t};import{LitElement as f,html as m,css as v}from"./vendor/lit.js";import{customElement as y,property as a}from"./vendor/lit.js";var g={warn:"var(--rik-status-warn)",danger:"var(--rik-status-danger)",ok:"var(--rik-status-success)",info:"var(--rik-accent)",muted:"var(--rik-text-default--faint)",accent:"var(--rik-accent)"},h={lead:"var(--rik-font-size-lead)",big:"var(--rik-font-size-big)",mega:"var(--rik-font-size-mega)",stat:"var(--rik-font-size-stat)",display:"clamp(2.6rem, 6cqw, 5rem)"},e=class extends f{updated(){this.tone&&g[this.tone]?this.style.setProperty("--_color",g[this.tone]):this.style.removeProperty("--_color"),this.size&&h[this.size]?this.style.setProperty("--_size",h[this.size]):this.style.removeProperty("--_size")}render(){return m`<slot></slot>`}};e.styles=v`:host{display:block;margin:0;font-family:var(--rik-font-display,var(--rik-font-sans));font-weight:900;line-height:1.1;letter-spacing:-0.02em;font-size:var(--deck-punch-size,var(--_size,var(--rik-font-size-lead)));color:var(--deck-punch-color,var(--_color,inherit))}:host([weight="700"]){font-weight:700}:host([weight="800"]){font-weight:800}:host([align="center"]){text-align:center}:host([align="right"]){text-align:right}`,r([a({type:String})],e.prototype,"tone",2),r([a({type:String})],e.prototype,"size",2),r([a({type:String,reflect:!0})],e.prototype,"weight",2),r([a({type:String,reflect:!0})],e.prototype,"align",2),e=r([y("deck-punch")],e);export{e as DeckPunch};
1
+ var p=Object.defineProperty;var d=Object.getOwnPropertyDescriptor;var n=(a,t,r,l)=>{for(var e=l>1?void 0:l?d(t,r):t,o=a.length-1,s;o>=0;o--)(s=a[o])&&(e=(l?s(t,r,e):s(e))||e);return l&&e&&p(t,r,e),e};import{LitElement as u,html as v,css as y}from"./vendor/lit.js";import{customElement as b,property as h}from"./vendor/lit.js";function g(a,t,r,l=.5){if(t<=a)return a;if(r(t))return t;let e=a,o=t;for(;o-e>l;){let s=(e+o)/2;r(s)?e=s:o=s}return e}var c=class{constructor(t,r={}){this.host=t;this.opts=r;t.addController(this)}hostConnected(){this.ro=new ResizeObserver(()=>this.refit()),this.ro.observe(this.host),this.refit(),document.fonts?.ready?.then(()=>{this.ro&&this.refit()})}hostDisconnected(){this.ro?.disconnect(),this.ro=void 0}refit(){let t=this.host;if(this.opts.enabled&&!this.opts.enabled()){t.style.removeProperty("font-size");return}if(t.clientWidth===0||t.clientHeight===0)return;let r=parseFloat(getComputedStyle(document.documentElement).fontSize)||16,l=(this.opts.minRem?.()??1)*r,e=(this.opts.maxRem?.()??12)*r,o=s=>(t.style.fontSize=`${s}px`,t.scrollWidth<=t.clientWidth&&t.scrollHeight<=t.clientHeight);t.style.fontSize=`${g(l,e,o)}px`}};var m={warn:"var(--rik-status-warn__text)",danger:"var(--rik-status-danger__text)",ok:"var(--rik-status-success__text)",info:"var(--rik-accent__text)",muted:"var(--rik-text-default--faint)",accent:"var(--rik-accent__text)"},f={lead:"var(--rik-font-size-lead)",big:"var(--rik-font-size-big)",mega:"var(--rik-font-size-mega)",stat:"var(--rik-font-size-stat)",display:"clamp(2.6rem, 6cqw, 5rem)"},i=class extends u{constructor(){super(...arguments);this.fit=!1;this.fitter=new c(this,{enabled:()=>this.fit,minRem:()=>this.fitMin??1,maxRem:()=>this.fitMax??12})}updated(){this.fitter.refit(),this.tone&&m[this.tone]?this.style.setProperty("--_color",m[this.tone]):this.style.removeProperty("--_color"),this.size&&f[this.size]?this.style.setProperty("--_size",f[this.size]):this.style.removeProperty("--_size")}render(){return v`<slot></slot>`}};i.styles=y`:host{display:block;margin:0;font-family:var(--rik-font-display,var(--rik-font-sans));font-weight:900;line-height:1.1;letter-spacing:-0.02em;font-size:var(--deck-punch-size,var(--_size,var(--rik-font-size-lead)));color:var(--deck-punch-color,var(--_color,inherit))}:host([weight="700"]){font-weight:700}:host([weight="800"]){font-weight:800}:host([align="center"]){text-align:center}:host([align="right"]){text-align:right}:host([fit]){display:block;width:100%;height:100%;overflow:hidden}`,n([h({type:String})],i.prototype,"tone",2),n([h({type:String})],i.prototype,"size",2),n([h({type:String,reflect:!0})],i.prototype,"weight",2),n([h({type:String,reflect:!0})],i.prototype,"align",2),n([h({type:Boolean})],i.prototype,"fit",2),n([h({type:Number,attribute:"fit-min"})],i.prototype,"fitMin",2),n([h({type:Number,attribute:"fit-max"})],i.prototype,"fitMax",2),i=n([b("deck-punch")],i);export{i as DeckPunch};
@@ -0,0 +1,28 @@
1
+ import { LitElement } from 'lit';
2
+ export type DeckQuoteSize = 'lead' | 'big' | 'mega';
3
+ export declare class DeckQuote extends LitElement {
4
+ static styles: import("lit").CSSResult;
5
+ /** Attributed name · omitted, the quote stands alone. */
6
+ author?: string;
7
+ /** Role or affiliation, under the name.
8
+ *
9
+ * NOT `role`: that attribute is ARIA's, and putting "CTO, Acme" in it would
10
+ * announce the figure as a landmark of that name. The property would also
11
+ * shadow HTMLElement.role. */
12
+ authorRole?: string;
13
+ /** Type size of the quoted line. */
14
+ size: DeckQuoteSize;
15
+ /** Drop the accent rule down the left edge. */
16
+ plain: boolean;
17
+ /** Use the inverse text tokens · for a quote on a dark slide. */
18
+ onDark: boolean;
19
+ /** Hide the decorative opening mark. */
20
+ noMark: boolean;
21
+ willUpdate(): void;
22
+ render(): import("lit-html").TemplateResult<1>;
23
+ }
24
+ declare global {
25
+ interface HTMLElementTagNameMap {
26
+ 'deck-quote': DeckQuote;
27
+ }
28
+ }
@@ -0,0 +1,6 @@
1
+ var p=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var r=(s,o,n,i)=>{for(var t=i>1?void 0:i?k(o,n):o,c=s.length-1,d;c>=0;c--)(d=s[c])&&(t=(i?d(o,n,t):d(t))||t);return i&&t&&p(o,n,t),t};import{LitElement as h,css as f,html as l}from"./vendor/lit.js";import{customElement as v,property as a}from"./vendor/lit.js";var u={lead:"var(--rik-font-size-lead)",big:"var(--rik-font-size-big)",mega:"var(--rik-font-size-mega)"},e=class extends h{constructor(){super(...arguments);this.size="big";this.plain=!1;this.onDark=!1;this.noMark=!1}willUpdate(){this.style.setProperty("--_size",u[this.size]??u.big)}render(){return l`<figure> ${this.noMark?"":l`<span class="mark" aria-hidden="true">&ldquo;</span>`}
2
+ <blockquote class="quote" part="quote"><slot></slot></blockquote>
3
+ ${this.author?l`<figcaption class="who" part="attribution"> <span class="author">${this.author}</span> ${this.authorRole?l`<span class="role">${this.authorRole}</span>`:""}
4
+ </figcaption>`:""}
5
+ </figure>
6
+ `}};e.styles=f`:host{display:block;font-family:var(--rik-font-sans);padding-left:var(--rik-space-4);border-left:var(--deck-quote-rule-width,3px) solid var(--deck-quote-rule,var(--rik-accent))}figure{margin:0}:host([plain]){padding-left:0;border-left:none}.mark{display:block;font-family:var(--rik-font-display,var(--rik-font-sans));font-size:var(--deck-quote-mark-size,calc(var(--_size) * 1.8));line-height:0.8;color:var(--deck-quote-mark-color,var(--rik-accent));user-select:none}.quote{display:block;font-size:var(--deck-quote-size,var(--_size));line-height:1.25;color:var(--deck-quote-color,var(--rik-text-default));max-width:var(--deck-quote-max-width,26ch);margin:var(--deck-quote-gap,var(--rik-space-2)) 0;text-wrap:balance}.who{margin-top:var(--deck-quote-gap,var(--rik-space-2));line-height:1.35}.author{display:block;font-weight:700;color:var(--deck-quote-author-color,var(--rik-text-default))}.role{display:block;font-size:var(--rik-font-size-body);color:var(--deck-quote-role-color,var(--rik-text-default--muted))}:host([on-dark]) .quote,:host([on-dark]) .author{color:var(--rik-text-inverse)}:host([on-dark]) .role{color:var(--rik-text-inverse--muted)}`,r([a({type:String})],e.prototype,"author",2),r([a({type:String,attribute:"author-role"})],e.prototype,"authorRole",2),r([a({type:String,reflect:!0})],e.prototype,"size",2),r([a({type:Boolean,reflect:!0})],e.prototype,"plain",2),r([a({type:Boolean,reflect:!0,attribute:"on-dark"})],e.prototype,"onDark",2),r([a({type:Boolean,attribute:"no-mark"})],e.prototype,"noMark",2),e=r([v("deck-quote")],e);export{e as DeckQuote};
@@ -35,6 +35,9 @@ export interface DeckPlugin {
35
35
  navigate?(to: number, ctx: DeckContext, proceed: () => void): boolean | void;
36
36
  }
37
37
  export declare class DeckRoot extends LitElement {
38
+ /** Internal overview surface: author selectors still see deck-root, but the
39
+ * outer thumbnail owns sizing and no presentation runtime is needed. */
40
+ private get _overviewSnapshot();
38
41
  static styles: import("lit").CSSResult;
39
42
  current: number;
40
43
  step: number;
@@ -153,12 +156,29 @@ export declare class DeckRoot extends LitElement {
153
156
  * the container for an embedded one). Driven by a ResizeObserver. */
154
157
  private _applyScale;
155
158
  private _resizeObserver;
159
+ /** A step the opening fragment asked for, held until the slide can hear it.
160
+ *
161
+ * The components that carry steps are opt-in modules, and a module can
162
+ * register AFTER the engine has read the fragment and applied the step. The
163
+ * step reached a slide whose children were still plain elements with no
164
+ * applyStep to call, so it landed on nothing and no second attempt was ever
165
+ * made · every link into the middle of a build opened its neutral state,
166
+ * which is the one thing such a link exists to avoid. */
167
+ private _pendingStep;
168
+ private _stepWatcher;
156
169
  /** Zoom is live only in the fixed canvas and outside overlays. */
157
170
  private _zoomEnabled;
158
171
  /** Publish zoom + pan as custom props the #stage transform reads. */
159
172
  private _applyZoom;
160
173
  /** Keep the pan within bounds so the magnified stage always covers the
161
174
  * viewport (no gaps); at fit (zoom 1) it forces re-centring. */
175
+ /** The three fields above, as the viewport model sees them. */
176
+ private get _view();
177
+ private set _view(value);
178
+ /** The logical canvas and the box it is shown in · the two measurements the
179
+ * arithmetic in src/domain/viewport.ts needs, and the only DOM it involves. */
180
+ private get _canvas();
181
+ private get _box();
162
182
  private _clampPan;
163
183
  /** Zoom by a factor, keeping the point at viewport (cx, cy) fixed. */
164
184
  private _zoomAt;
@@ -176,6 +196,16 @@ export declare class DeckRoot extends LitElement {
176
196
  * surface · removing the override lets the bands fall back to that same
177
197
  * surface, which stays seamless too. */
178
198
  private _applyLetterbox;
199
+ /** A slide can reach the engine before its own module does · an opt-in
200
+ * component loads from its own script tag, and nothing orders the two.
201
+ * Until it upgrades it has no shadow DOM and reports a TRANSPARENT
202
+ * background, so the read above finds nothing and the bands keep the page
203
+ * surface. This is the only measurement in the engine with no observer
204
+ * behind it, so without this it would never be taken again.
205
+ *
206
+ * Same shape as _stepWatcher below, which catches a step count that
207
+ * arrives late for the same reason. */
208
+ private _repaintLetterboxWhenDefined;
179
209
  /** The scaling baseline is a framework concern, not a theme one: inject it
180
210
  * globally so any theme (or none) gets it. The rem unit tracks the logical
181
211
  * canvas height · the #stage transform does the responsive scaling · and the
@@ -195,6 +225,15 @@ export declare class DeckRoot extends LitElement {
195
225
  * slide · it comes last so it wins the equal-specificity tie with the
196
226
  * `:not([fluid])` rule. The `height:100%` pair backs the 100% `:host`
197
227
  * sizing. */
228
+ /** Size the printed page from the deck's own canvas.
229
+ *
230
+ * `@page` cannot read a custom property, so the rule is written from JS every
231
+ * time the canvas changes. Without it the browser prints A4 and crops a 16:9
232
+ * slide down its right edge · the exact symptom the FAQ promised away. */
233
+ /** What a screen reader hears on a slide change · position first, because
234
+ * that is the part a listener cannot get any other way. */
235
+ private _liveLabel;
236
+ private _applyPageSize;
198
237
  private static _injectGlobals;
199
238
  connectedCallback(): void;
200
239
  disconnectedCallback(): void;
@@ -221,6 +260,9 @@ export declare class DeckRoot extends LitElement {
221
260
  private _onAuxUp;
222
261
  private _onAuxClick;
223
262
  /** Group slides into chapters bounded by <deck-section> markers. */
263
+ /** The deck's shape as the navigation model sees it · rebuilt with the DOM
264
+ * chapter list, and the only thing the decisions below read. */
265
+ private _outline;
224
266
  private _buildChapters;
225
267
  /** 2D navigation is opt-in via `nav="2d"` · it also needs the structure to
226
268
  * make sense (2+ chapters, at least one with multiple slides). Without the
@@ -230,9 +272,34 @@ export declare class DeckRoot extends LitElement {
230
272
  private _mouseEnabled;
231
273
  /** Flat index → {chapter, intra-chapter index}. */
232
274
  private _coords;
233
- private _flatFromCoords;
275
+ /** True when the deck IS the page rather than a widget inside one.
276
+ *
277
+ * A full-page deck owns the URL, the keyboard and the wheel · that is the
278
+ * whole point. An embedded deck owns none of them by default: the host put
279
+ * the anchor in the URL, the host's reader is using the arrow keys to read
280
+ * the host's page, and the wheel over a widget scrolls the page it sits in. */
281
+ private _isFullPage;
282
+ /** Does a key press belong to this deck right now? Always, when the deck is
283
+ * the page · only while focus is inside it, when it is embedded. */
284
+ private _ownsKeyboard;
234
285
  private _onHash;
286
+ /** What the deep-link use case needs to know about this deck right now. */
287
+ private _linkContext;
235
288
  private _readHash;
289
+ /** Watch for a stepping component arriving late, and replay the step at it.
290
+ *
291
+ * The trigger is the `data-steps` attribute a stepping component writes onto
292
+ * its slide when it connects: that mutation IS the moment a component
293
+ * capable of hearing a step appears. Watching it beats polling or waiting a
294
+ * fixed time, and beats asking every component to reach back into the deck.
295
+ *
296
+ * Armed only for a fragment that asks for a step, and disarmed at the first
297
+ * move the reader makes · their navigation outranks the link they opened. */
298
+ private _watchForLateSteps;
299
+ /** Replay the opening step now that something on the slide can receive it. */
300
+ private _replayPendingStep;
301
+ /** Stop waiting · the link was honoured, or the reader moved on themselves. */
302
+ private _forgetPendingStep;
236
303
  private _writeHash;
237
304
  private _onKey;
238
305
  /** Lazy-import the help module the first time the user opens it. */
@@ -243,20 +310,33 @@ export declare class DeckRoot extends LitElement {
243
310
  /** Lazy-import the overview module the first time the user opens it. */
244
311
  private _renderOverviewIfActive;
245
312
  private _maxSteps;
246
- /** The engine's own step count for the active slide · `steps`/`data-steps`
247
- * attribute, or a `deck-code[step-groups]` group count. Plugins extend this
248
- * through their `steps` hook (see _maxSteps). */
313
+ /** Step count for ANY slide · the navigation model asks about the slide it is
314
+ * moving TO. Going back into the previous slide has to know that slide's
315
+ * last step BEFORE the move, which is why this is not limited to `current`.
316
+ * This is the `StepsOf` the domain takes. */
317
+ private _maxStepsFor;
318
+ /** The engine's own step count for a slide · `steps`/`data-steps` attribute,
319
+ * or a `deck-code[step-groups]` group count. Plugins extend it through their
320
+ * `steps` hook (see _maxStepsFor). */
249
321
  private _baseMaxSteps;
322
+ /** Where the deck is, as the navigation model sees it. */
323
+ private get _position();
324
+ /** Apply a whole position decided by the domain.
325
+ *
326
+ * Slide AND step move together. The engine used to set them in two
327
+ * statements, so a plugin that DEFERS navigation (a View Transition) ran the
328
+ * second one against the old slide and the deferred move then reset the step
329
+ * to 0 · going back into a slide landed on step 0 instead of its last step.
330
+ * One value, applied once, cannot come apart that way. */
331
+ private _applyPosition;
332
+ private _moveTo;
250
333
  private _advance;
251
334
  private _back;
252
335
  private _goTo;
253
- private _goToNow;
254
336
  private _goToCoords;
255
337
  private _applyActive;
256
338
  private _applyStep;
257
- /** The bottom-right slide counter is noise in modes where it shouldn't show:
258
- * the overview grid, a black/white blanked screen, and the cover slide (the
259
- * title slide has no business carrying a page number). */
339
+ /** Keep numbering on all slide layouts; hide it only in overlay modes. */
260
340
  private _counterHidden;
261
341
  private _updateUI;
262
342
  updated(changed: PropertyValues<this>): void;