@farberg/reveal-template 1.1.30 → 1.1.32

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.
@@ -7,6 +7,18 @@
7
7
 
8
8
  <ul class="menu"><ul>
9
9
 
10
+ ---
11
+ ## Code Example
12
+
13
+ ```bash
14
+ # Create a Kafka cluster using the operator
15
+ kubectl apply -f https://raw.githubusercontent.com/strimzi/strimzi-kafka-operator/refs/heads/main/examples/kafka/kafka-ephemeral.yaml
16
+ ```
17
+
18
+ ```bash
19
+ docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t yourusername/yourcontainername:1.2.3 -t yourusername/yourcontainername:latest --push .
20
+ ```
21
+
10
22
  ---
11
23
  ## Mermaid Example
12
24
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farberg/reveal-template",
3
- "version": "1.1.30",
3
+ "version": "1.1.32",
4
4
  "homepage": "https://github.com/pfisterer/reveal-template",
5
5
  "description": "Reveal.js template for Dennis' lectures",
6
6
  "main": "index.js",
@@ -10,9 +10,9 @@ Parameters:
10
10
 
11
11
  Example
12
12
 
13
- <a data-code='bash'
14
- data-begin="# Begin indicator in the file"
15
- data-end="# End indicator in the file"
13
+ <a data-code='bash'
14
+ data-begin="# Begin indicator in the file"
15
+ data-end="# End indicator in the file"
16
16
  data-link
17
17
  href="link/to/the/file.sh">Source code</a>
18
18
  */
@@ -26,6 +26,9 @@ function showError(el, err) {
26
26
  function showCode(el, language, code, link, outdent) {
27
27
  var newEl = document.createElement('pre');
28
28
  newEl.setAttribute('class', `language-${language}`);
29
+ // Keep the exact, un-escaped source so the copy button can reproduce it
30
+ // verbatim regardless of how we later restructure the DOM for display.
31
+ newEl.__rawCode = code
29
32
  //Replace special chars
30
33
  code = code.replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;")
31
34
 
@@ -82,6 +85,49 @@ function injectStyles() {
82
85
  pre.with-copy-btn {
83
86
  position: relative;
84
87
  }
88
+ /* Soft-wrap long lines like VS Code's word wrap. white-space:pre-wrap is
89
+ a purely visual wrap: it does NOT insert real newlines into the text.
90
+ overflow-wrap:break-word prefers breaking at whitespace and only splits a
91
+ word when that single token is itself too long to fit (long URLs / base64
92
+ / paths). Unlike "anywhere", it does not snap mid-word when a whitespace
93
+ break is available. Copy fidelity is handled separately (see
94
+ addCopyButton / wrapCodeLines), so wrapping never leaks into copied text. */
95
+ .reveal pre.with-copy-btn,
96
+ .reveal pre.with-copy-btn code {
97
+ white-space: pre-wrap;
98
+ overflow-wrap: break-word;
99
+ word-break: normal;
100
+ }
101
+ /* Each logical source line becomes its own block so wrapped continuation
102
+ rows can be hang-indented (text-indent pulls the first row back to the
103
+ margin; padding-left indents everything else). padding-right reserves
104
+ room for the end-of-row wrap marker (see decorateWrappedLines): text
105
+ wraps before that zone, so the absolutely-positioned marker lands inside
106
+ the box instead of overflowing and triggering a horizontal scrollbar. */
107
+ .reveal pre.with-copy-btn code .cl {
108
+ display: block;
109
+ position: relative;
110
+ padding-left: 2.5ch;
111
+ padding-right: 1.5ch;
112
+ text-indent: -2.5ch;
113
+ }
114
+ /* Preserve the height of blank lines. Generated content is excluded from
115
+ selection/copy, so the zero-width space never reaches the clipboard. */
116
+ .reveal pre.with-copy-btn code .cl:empty::before {
117
+ content: "\\200b";
118
+ }
119
+ /* Continuation marker placed at the end of each row that soft-wraps (see
120
+ decorateWrappedLines). The horizontal/vertical offset is set inline per
121
+ row; user-select:none keeps it out of native select+copy. */
122
+ .reveal pre.with-copy-btn code .wrap-marker {
123
+ position: absolute;
124
+ text-indent: 0;
125
+ font-style: normal;
126
+ opacity: 0.4;
127
+ pointer-events: none;
128
+ user-select: none;
129
+ -webkit-user-select: none;
130
+ }
85
131
  .copy-code-btn {
86
132
  position: absolute;
87
133
  top: 0.4em;
@@ -114,6 +160,89 @@ function injectStyles() {
114
160
  document.head.appendChild(style);
115
161
  }
116
162
 
163
+ // Wrap each logical line of a (already highlighted) <code> element in its own
164
+ // <span class="cl"> block. hljs emits a single run of HTML with "\n"
165
+ // separators and token <span>s that may straddle newlines, so we tokenize the
166
+ // markup and re-open any spans still active when a line ends.
167
+ function wrapCodeLines(codeEl) {
168
+ const html = codeEl.innerHTML
169
+ const tokenRe = /(<\/?[^>]+>)|([^<]+)/g
170
+ let m
171
+ let open = [] // stack of currently-open tag strings (hljs uses <span>)
172
+ let buf = ''
173
+ let lines = []
174
+ const closers = () => open.map(() => '</span>').join('')
175
+ const reopen = () => open.join('')
176
+
177
+ while ((m = tokenRe.exec(html)) !== null) {
178
+ if (m[1]) {
179
+ const tag = m[1]
180
+ if (tag[1] === '/') { open.pop(); buf += tag }
181
+ else if (tag.endsWith('/>')) { buf += tag } // self-closing (rare)
182
+ else { open.push(tag); buf += tag }
183
+ } else {
184
+ const parts = m[2].split('\n')
185
+ for (let i = 0; i < parts.length; i++) {
186
+ if (i > 0) {
187
+ buf += closers() // close spans before the line break
188
+ lines.push(buf)
189
+ buf = reopen() // re-open them on the next line
190
+ }
191
+ buf += parts[i]
192
+ }
193
+ }
194
+ }
195
+ buf += closers()
196
+ lines.push(buf)
197
+
198
+ codeEl.innerHTML = lines.map(l => `<span class="cl">${l}</span>`).join('')
199
+ }
200
+
201
+ // Place a "↴" marker at the end of every row that soft-wraps (i.e. every visual
202
+ // row except the last). CSS has no selector for soft-wrap rows, so we measure
203
+ // them with a Range. The deck is rendered at a fixed layout width and merely
204
+ // CSS-scaled, so wrap positions are stable; we divide measured offsets by the
205
+ // current scale to get layout pixels.
206
+ function decorateWrappedLines(root, scale) {
207
+ if (!root) return
208
+ const s = scale || 1
209
+ for (const line of root.querySelectorAll('pre.with-copy-btn code .cl')) {
210
+ // Idempotent: clear any decoration from a previous pass before measuring.
211
+ for (const old of line.querySelectorAll('.wrap-marker')) old.remove()
212
+
213
+ const range = document.createRange()
214
+ range.selectNodeContents(line)
215
+ const rects = range.getClientRects()
216
+ if (!rects.length) continue
217
+
218
+ const base = line.getBoundingClientRect()
219
+ // Group the rects (one per inline fragment) into visual rows keyed by
220
+ // their top offset, tracking the rightmost text edge of each row — that
221
+ // edge is where the row visually ends, i.e. the wrap point.
222
+ const rows = []
223
+ for (const r of rects) {
224
+ if (r.width === 0 && r.height === 0) continue
225
+ const top = Math.round(r.top - base.top)
226
+ const right = r.right - base.left
227
+ const row = rows.find(x => Math.abs(x.top - top) <= 2)
228
+ if (row) row.right = Math.max(row.right, right)
229
+ else rows.push({ top, right })
230
+ }
231
+ if (rows.length <= 1) continue // line did not wrap
232
+
233
+ // Mark the end of every row except the last — those are the wrap points.
234
+ for (let i = 0; i < rows.length - 1; i++) {
235
+ const marker = document.createElement('span')
236
+ marker.className = 'wrap-marker'
237
+ marker.textContent = '↴'
238
+ marker.setAttribute('aria-hidden', 'true')
239
+ marker.style.top = (rows[i].top / s) + 'px'
240
+ marker.style.left = (rows[i].right / s) + 'px'
241
+ line.appendChild(marker)
242
+ }
243
+ }
244
+ }
245
+
117
246
  function writeToClipboard(text) {
118
247
  if (navigator.clipboard && navigator.clipboard.writeText)
119
248
  return navigator.clipboard.writeText(text);
@@ -140,7 +269,13 @@ function addCopyButton(preEl) {
140
269
  btn.textContent = 'copy';
141
270
  btn.addEventListener('click', () => {
142
271
  const code = preEl.querySelector('code');
143
- const text = code ? code.innerText : preEl.innerText;
272
+ // Prefer the stashed raw source: it is immune to the per-line <span>
273
+ // wrapping and wrap markers used for display. textContent is the
274
+ // fallback (block-per-line collapses to no newlines under textContent,
275
+ // so raw is what we really want here).
276
+ const text = preEl.__rawCode != null
277
+ ? preEl.__rawCode
278
+ : (code ? code.textContent : preEl.textContent);
144
279
  writeToClipboard(text).then(() => {
145
280
  btn.textContent = '✓ copied';
146
281
  btn.classList.add('copied');
@@ -162,6 +297,17 @@ export default () => {
162
297
  init: (deck) => {
163
298
  injectStyles();
164
299
 
300
+ // Re-measure wrap points after fonts settle and on every layout change.
301
+ // Only the visible slide can be measured (hidden slides have no layout),
302
+ // so slidechanged re-runs decoration as each slide is shown.
303
+ const decorate = (root) => {
304
+ const run = () => decorateWrappedLines(root, deck.getScale());
305
+ if (document.fonts && document.fonts.ready)
306
+ document.fonts.ready.then(() => requestAnimationFrame(run));
307
+ else
308
+ requestAnimationFrame(run);
309
+ };
310
+
165
311
  deck.on('ready', async () => {
166
312
  const highlightPlugin = deck.getPlugin("highlight")
167
313
 
@@ -178,8 +324,12 @@ export default () => {
178
324
  // code text and overwrites the rendered diagram.
179
325
  for (let el of deck.getRevealElement().querySelectorAll("pre code[class]")) {
180
326
  if (!el.classList.contains("mermaid")) {
327
+ const pre = el.closest('pre')
328
+ // Capture raw source before highlighting/line-wrapping touches the DOM.
329
+ if (pre) pre.__rawCode = el.textContent
181
330
  highlightPlugin.hljs.highlightElement(el);
182
- addCopyButton(el.closest('pre'));
331
+ wrapCodeLines(el);
332
+ addCopyButton(pre);
183
333
  }
184
334
  }
185
335
 
@@ -225,7 +375,9 @@ export default () => {
225
375
 
226
376
  const newEl = showCode(el, language, code, showLink ? url : null, outdent)
227
377
  if (language !== 'mermaid') {
228
- highlightPlugin.hljs.highlightElement(newEl.querySelector('code') ?? newEl)
378
+ const codeEl = newEl.querySelector('code') ?? newEl
379
+ highlightPlugin.hljs.highlightElement(codeEl)
380
+ wrapCodeLines(codeEl)
229
381
  addCopyButton(newEl)
230
382
  }
231
383
  } catch (err) {
@@ -234,10 +386,15 @@ export default () => {
234
386
  }
235
387
  }
236
388
 
237
-
389
+ // First pass for the slide that is visible at startup.
390
+ decorate(deck.getRevealElement())
238
391
  })
239
392
 
240
-
393
+ // Slides other than the current one have no layout while hidden, so we
394
+ // (re)place wrap markers each time a slide becomes visible, and after
395
+ // any resize that changes the scale.
396
+ deck.on('slidechanged', (e) => decorate(e.currentSlide))
397
+ deck.on('resize', () => decorate(deck.getRevealElement()))
241
398
  }
242
399
  }
243
- }
400
+ }