hyperbook 0.84.4 → 0.85.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 (39) hide show
  1. package/dist/assets/bootstrap.js +248 -0
  2. package/dist/assets/cloud.js +28 -17
  3. package/dist/assets/directive-abc-music/client.js +11 -3
  4. package/dist/assets/directive-archive/client.js +17 -4
  5. package/dist/assets/directive-audio/client.js +67 -28
  6. package/dist/assets/directive-bookmarks/client.js +9 -1
  7. package/dist/assets/directive-download/client.js +10 -2
  8. package/dist/assets/directive-embed/client.js +112 -0
  9. package/dist/assets/directive-embed/style.css +70 -1
  10. package/dist/assets/directive-excalidraw/client.js +9 -0
  11. package/dist/assets/directive-excalidraw/hyperbook-excalidraw.umd.js +1 -1
  12. package/dist/assets/directive-geogebra/client.js +16 -3
  13. package/dist/assets/directive-h5p/client.js +32 -3
  14. package/dist/assets/directive-learningmap/client.js +11 -3
  15. package/dist/assets/directive-mermaid/client.js +11 -1
  16. package/dist/assets/directive-multievent/multievent.js +2 -2
  17. package/dist/assets/directive-onlineide/client.js +7 -0
  18. package/dist/assets/directive-onlineide/include/online-ide-embedded.js +43 -43
  19. package/dist/assets/directive-p5/client.js +39 -7
  20. package/dist/assets/directive-protect/client.js +11 -3
  21. package/dist/assets/directive-pyide/client.js +20 -9
  22. package/dist/assets/directive-scratchblock/client.js +9 -0
  23. package/dist/assets/directive-slideshow/client.js +12 -4
  24. package/dist/assets/directive-sqlide/client.js +7 -0
  25. package/dist/assets/directive-sqlide/include/sql-ide-embedded.js +3 -3
  26. package/dist/assets/directive-tabs/client.js +14 -3
  27. package/dist/assets/directive-textinput/client.js +14 -3
  28. package/dist/assets/directive-webide/client.js +45 -10
  29. package/dist/assets/directive-youtube/client.js +99 -0
  30. package/dist/assets/directive-youtube/style.css +63 -0
  31. package/dist/assets/hyperbook.types.js +209 -0
  32. package/dist/assets/i18n.js +15 -1
  33. package/dist/assets/store.js +174 -139
  34. package/dist/assets/ui.js +279 -0
  35. package/dist/index.js +632 -413
  36. package/dist/locales/de.json +9 -1
  37. package/dist/locales/en.json +9 -1
  38. package/package.json +4 -4
  39. package/dist/assets/client.js +0 -506
@@ -95,5 +95,13 @@
95
95
  "user-logout-confirm": "Möchten Sie sich wirklich abmelden?",
96
96
  "user-readonly": "Nur-Lesen-Modus",
97
97
  "user-impersonating": "Angemeldet als",
98
- "user-exit-impersonation": "Identitätswechsel beenden"
98
+ "user-exit-impersonation": "Identitätswechsel beenden",
99
+ "consent-youtube-text": "Bei einem Klick auf den Play-Button wird ein YouTube-Video über YouTube-NoCookie geladen. Dennoch findet der Verbindungsaufbau statt und die dafür notwendigen Daten werden übertragen.",
100
+ "consent-youtube-nocookie": "Wir verwenden YouTube-NoCookie, um Ihre Privatsphäre zu schützen.",
101
+ "consent-youtube-always": "YouTube-Videos immer zulassen",
102
+ "consent-youtube-accept": "Akzeptieren und abspielen",
103
+ "consent-embed-text": "Hier sollen Inhalte einer externen Internetseite geladen werden.",
104
+ "consent-embed-personal-data": "Die externe Internetseite kann möglicherweise personenbezogene Daten erheben.",
105
+ "consent-embed-always": "Inhalte von {{domain}} immer zulassen",
106
+ "consent-embed-accept": "Inhalte laden"
99
107
  }
@@ -95,5 +95,13 @@
95
95
  "user-logout-confirm": "Are you sure you want to logout?",
96
96
  "user-readonly": "Read-Only Mode",
97
97
  "user-impersonating": "Impersonating",
98
- "user-exit-impersonation": "Exit Impersonation"
98
+ "user-exit-impersonation": "Exit Impersonation",
99
+ "consent-youtube-text": "Clicking the play button will load a YouTube video via YouTube-NoCookie. However, a connection will still be established and the necessary data will be transmitted.",
100
+ "consent-youtube-nocookie": "We use YouTube-NoCookie to protect your privacy.",
101
+ "consent-youtube-always": "Always allow YouTube videos",
102
+ "consent-youtube-accept": "Accept and play",
103
+ "consent-embed-text": "Content from an external website is to be loaded here.",
104
+ "consent-embed-personal-data": "The external website may collect personal data.",
105
+ "consent-embed-always": "Always allow content from {{domain}}",
106
+ "consent-embed-accept": "Load content"
99
107
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperbook",
3
- "version": "0.84.4",
3
+ "version": "0.85.0",
4
4
  "author": "Mike Barkmin",
5
5
  "homepage": "https://github.com/openpatch/hyperbook#readme",
6
6
  "license": "MIT",
@@ -56,10 +56,10 @@
56
56
  "tar": "7.4.3",
57
57
  "update-check": "1.5.4",
58
58
  "ws": "^8.18.0",
59
+ "@hyperbook/types": "0.22.1",
60
+ "@hyperbook/markdown": "0.57.0",
59
61
  "@hyperbook/fs": "0.24.2",
60
- "create-hyperbook": "0.3.5",
61
- "@hyperbook/markdown": "0.55.4",
62
- "@hyperbook/types": "0.22.1"
62
+ "create-hyperbook": "0.3.5"
63
63
  },
64
64
  "scripts": {
65
65
  "version": "pnpm build",
@@ -1,506 +0,0 @@
1
- var hyperbook = Object.assign(window.hyperbook || {}, (function () {
2
- /**
3
- * Initialize elements within the given root element.
4
- * @param {HTMLElement} root - The root element to initialize.
5
- */
6
- const initCollapsibles = (root) => {
7
- // Handle both navigation sections and directive-collapsible elements
8
- const detailsEls = root.querySelectorAll("details.section, details.directive-collapsible");
9
- for (let details of detailsEls) {
10
- const id = details.getAttribute("data-id");
11
-
12
- // Prevent link clicks from toggling the details element in navigation
13
- if (id && id.startsWith("_nav:") && !details.classList.contains("empty")) {
14
- const link = details.querySelector("summary a");
15
- link?.addEventListener("click", (event) => {
16
- event.stopPropagation();
17
- });
18
- }
19
-
20
- // Listen for toggle events to persist state and sync with other elements
21
- details.addEventListener("toggle", () => {
22
- if (id) {
23
- if (details.open) {
24
- store.collapsibles.put({ id });
25
- } else {
26
- store.collapsibles.delete(id);
27
- }
28
-
29
- // Sync all elements with the same ID
30
- const allWithSameId = document.querySelectorAll(`[data-id="${id}"]`);
31
- for (let el of allWithSameId) {
32
- if (el !== details && el.tagName === "DETAILS") {
33
- el.open = details.open;
34
- }
35
- }
36
- }
37
-
38
- setTimeout(() => {
39
- window.dispatchEvent(new Event("resize")); // geogebra needs this to resize the applet
40
- }, 100);
41
- });
42
- }
43
- updateCollapsibles(root);
44
- };
45
-
46
- /**
47
- * @param {HTMLElement} root
48
- */
49
- const updateCollapsibles = (root) => {
50
- store.collapsibles.toArray().then((collapsibles) => {
51
- const detailsEls = root.querySelectorAll("details.section, details.directive-collapsible");
52
- for (let details of detailsEls) {
53
- const id = details.getAttribute("data-id");
54
- if (id) {
55
- const shouldBeOpen = collapsibles.some((c) => c.id === id);
56
- // Only update if state differs and it's not a navigation section
57
- // (navigation sections are auto-expanded based on current page)
58
- if (!id.startsWith("_nav:") && details.open !== shouldBeOpen) {
59
- details.open = shouldBeOpen;
60
- }
61
- }
62
- }
63
- });
64
- };
65
-
66
- const initSearch = (root) => {
67
- const searchInputEl = root.querySelector("#search-input");
68
- if (searchInputEl) {
69
- searchInputEl.addEventListener("keypress", (event) => {
70
- if (event.key === "Enter") {
71
- event.preventDefault();
72
- search();
73
- }
74
- });
75
- }
76
- };
77
-
78
- /**
79
- * Toggle the table of contents drawer.
80
- */
81
- function tocToggle() {
82
- const tocDrawerEl = document.getElementById("toc-drawer");
83
- tocDrawerEl.open = !tocDrawerEl.open;
84
- }
85
-
86
- /**
87
- * Toggle the search drawer.
88
- */
89
- function searchToggle() {
90
- const searchDrawerEl = document.getElementById("search-drawer");
91
- searchDrawerEl.open = !searchDrawerEl.open;
92
- }
93
-
94
- /**
95
- * Perform a search and display the results.
96
- */
97
- function search() {
98
- const resultsEl = document.getElementById("search-results");
99
- resultsEl.innerHTML = "";
100
- const searchInputEl = document.querySelector("#search-input");
101
- const query = searchInputEl.value;
102
- const idx = window.lunr.Index.load(LUNR_INDEX);
103
- const documents = SEARCH_DOCUMENTS;
104
- const results = idx.search(query);
105
- for (let result of results) {
106
- const doc = documents[result.ref];
107
-
108
- const container = document.createElement("a");
109
- container.href = doc.href;
110
- container.classList.add("search-result");
111
- const heading = document.createElement("div");
112
- heading.textContent = doc.heading;
113
- heading.classList.add("search-result-heading");
114
- const content = document.createElement("div");
115
- content.classList.add("search-result-content");
116
- const href = document.createElement("div");
117
- href.classList.add("search-result-href");
118
- href.textContent = doc.href;
119
-
120
- let contentHTML = "";
121
- const terms = Object.keys(result.matchData.metadata);
122
- const term = terms[0];
123
- if (result?.matchData?.metadata?.[term]?.content?.position?.length > 0) {
124
- const pos = result.matchData.metadata[term].content.position[0];
125
- const start = pos[0];
126
- const len = pos[1];
127
- let cutoffBefore = start - 50;
128
- if (cutoffBefore < 0) {
129
- cutoffBefore = 0;
130
- } else {
131
- contentHTML += "...";
132
- }
133
- contentHTML += doc.content.slice(cutoffBefore, start);
134
-
135
- contentHTML += `<mark>${doc.content.slice(start, start + len)}</mark>`;
136
- let cutoffAfter = start + len + 50;
137
-
138
- contentHTML += doc.content.slice(start + len, cutoffAfter);
139
- if (cutoffAfter < doc.content.length) {
140
- contentHTML += "...";
141
- }
142
- }
143
-
144
- content.innerHTML = contentHTML;
145
-
146
- container.appendChild(heading);
147
- container.appendChild(content);
148
- container.appendChild(href);
149
- resultsEl.appendChild(container);
150
- }
151
- }
152
-
153
- /**
154
- * Open the QR code dialog.
155
- */
156
- function qrcodeOpen() {
157
- const qrCodeDialog = document.getElementById("qrcode-dialog");
158
- const qrcodeEls = qrCodeDialog.getElementsByClassName("make-qrcode");
159
- const urlEls = qrCodeDialog.getElementsByClassName("url");
160
- const qrcodeEl = qrcodeEls[0];
161
- const qrcode = new window.QRCode({
162
- content: window.location.href,
163
- padding: 0,
164
- join: true,
165
- color: "var(--color-text)",
166
- container: "svg-viewbox",
167
- background: "var(--color-background)",
168
- ecl: "M",
169
- });
170
- qrcodeEl.innerHTML = qrcode.svg();
171
- for (let urlEl of urlEls[0].children) {
172
- const href = urlEl.getAttribute("data-href");
173
- urlEl.innerHTML = `${window.location.origin}${href}`;
174
- }
175
-
176
- qrCodeDialog.showModal();
177
- }
178
-
179
- /**
180
- * Close the QR code dialog.
181
- */
182
- function qrcodeClose() {
183
- const qrCodeDialog = document.getElementById("qrcode-dialog");
184
- qrCodeDialog.close();
185
- }
186
-
187
- /**
188
- * Open the share dialog.
189
- */
190
- function shareOpen() {
191
- const shareDialog = document.getElementById("share-dialog");
192
- shareUpdatePreview();
193
- shareDialog.showModal();
194
- }
195
-
196
- /**
197
- * Close the share dialog.
198
- */
199
- function shareClose() {
200
- const shareDialog = document.getElementById("share-dialog");
201
- shareDialog.close();
202
- }
203
-
204
- /**
205
- * Update the URL preview in the share dialog.
206
- */
207
- function shareUpdatePreview() {
208
- const standaloneCheckbox = document.getElementById("share-standalone-checkbox");
209
- const sectionCheckboxes = document.querySelectorAll("#share-dialog input[data-anchor]");
210
- const previewEl = document.getElementById("share-url-preview");
211
-
212
- const baseUrl = window.location.origin + window.location.pathname;
213
- const params = new URLSearchParams();
214
-
215
- if (standaloneCheckbox && standaloneCheckbox.checked) {
216
- params.append("standalone", "true");
217
- }
218
-
219
- const selectedSections = Array.from(sectionCheckboxes)
220
- .filter(cb => cb.checked)
221
- .map(cb => cb.getAttribute("data-anchor"));
222
-
223
- if (selectedSections.length > 0) {
224
- params.append("sections", selectedSections.join(","));
225
- }
226
-
227
- const finalUrl = params.toString() ? `${baseUrl}?${params.toString()}` : baseUrl;
228
- previewEl.textContent = finalUrl;
229
- }
230
-
231
- /**
232
- * Copy the shareable URL to clipboard.
233
- */
234
- function shareCopyUrl() {
235
- const previewEl = document.getElementById("share-url-preview");
236
- const url = previewEl.textContent;
237
-
238
- navigator.clipboard.writeText(url).then(() => {
239
- const button = document.querySelector("#share-dialog .copy-button");
240
- const originalText = button.textContent;
241
- button.textContent = window.i18n.get("share-dialog-url-copied");
242
-
243
- setTimeout(() => {
244
- button.textContent = originalText;
245
- }, 2000);
246
- });
247
- }
248
-
249
- /**
250
- * Toggle the navigation drawer.
251
- */
252
- function navToggle() {
253
- const navDrawerEl = document.getElementById("nav-drawer");
254
- navDrawerEl.open = !navDrawerEl.open;
255
- }
256
-
257
- /**
258
- * Toggle a bookmark.
259
- * @param {string} key - The key of the bookmark.
260
- * @param {string} label - The label of the bookmark.
261
- */
262
- function toggleBookmark(key, label) {
263
- const el = document.querySelectorAll(`.bookmark[data-key="${key}"]`);
264
- store.bookmarks.get(key).then((bookmark) => {
265
- if (!bookmark) {
266
- store.bookmarks.add({ path: key, label }).then(() => {
267
- el.forEach((e) => e.classList.add("active"));
268
- hyperbook.bookmarks.update();
269
- });
270
- } else {
271
- store.bookmarks.delete(key).then(() => {
272
- el.forEach((e) => e.classList.remove("active"));
273
- hyperbook.bookmarks.update();
274
- });
275
- }
276
- });
277
- }
278
-
279
- /**
280
- * Initialize bookmarks within the given root element.
281
- * @param {HTMLElement} [root=document] - The root element to initialize.
282
- */
283
- function initBookmarks(root = document) {
284
- const bookmarkEls = root.getElementsByClassName("bookmark");
285
- for (let bookmarkEl of bookmarkEls) {
286
- const key = bookmarkEl.getAttribute("data-key");
287
- store.bookmarks.get(key).then((bookmark) => {
288
- if (bookmark) {
289
- bookmarkEl.classList.add("active");
290
- }
291
- });
292
- }
293
- }
294
-
295
- /**
296
- * Toggle the lightbox view of an element.
297
- * @param {HTMLElement} el - The element to toggle.
298
- */
299
- function toggleLightbox(el) {
300
- const overlay = document.createElement("div");
301
- overlay.classList.add("lightbox-overlay");
302
-
303
- const captionText =
304
- el.parentElement.querySelector("figcaption")?.textContent || "";
305
-
306
- // container for image + caption
307
- const content = document.createElement("div");
308
- content.classList.add("lightbox-content");
309
-
310
- // image container fills remaining space
311
- const imgContainer = document.createElement("div");
312
- imgContainer.classList.add("lightbox-image-container");
313
-
314
- const lightboxImg = document.createElement("img");
315
- lightboxImg.src = el.src;
316
- imgContainer.appendChild(lightboxImg);
317
-
318
- content.appendChild(imgContainer);
319
-
320
- // add caption if exists
321
- if (captionText) {
322
- const caption = document.createElement("div");
323
- caption.classList.add("caption");
324
- caption.textContent = captionText;
325
- content.appendChild(caption);
326
- }
327
-
328
- overlay.appendChild(content);
329
-
330
- overlay.addEventListener("click", () => {
331
- document.body.removeChild(overlay);
332
- });
333
-
334
- document.body.appendChild(overlay);
335
- overlay.style.display = "flex";
336
- }
337
-
338
- function init(root) {
339
- initCollapsibles(root);
340
- initSearch(root);
341
- initBookmarks(root);
342
- }
343
-
344
- /**
345
- * Hide TOC toggle button when sections are filtered
346
- */
347
- function hideTocWhenFiltered() {
348
- const tocToggle = document.getElementById('toc-toggle');
349
-
350
- if (tocToggle) {
351
- tocToggle.style.display = 'none';
352
- }
353
- }
354
-
355
- // Filter sections based on query parameter
356
- function filterSections() {
357
- const urlParams = new URLSearchParams(window.location.search);
358
- const sectionsParam = urlParams.get('sections');
359
-
360
- if (!sectionsParam) {
361
- return; // No filtering needed
362
- }
363
-
364
- // Parse sections parameter (comma-separated list of IDs)
365
- const sectionIds = sectionsParam.split(',').map(id => id.trim()).filter(id => id);
366
-
367
- if (sectionIds.length === 0) {
368
- return; // No valid IDs provided
369
- }
370
-
371
- // Get all headings in the document
372
- const allHeadings = document.querySelectorAll('h1, h2, h3, h4, h5, h6');
373
- const headingsArray = Array.from(allHeadings);
374
-
375
- // Build a map of which elements should be visible
376
- const visibleElements = new Set();
377
-
378
- sectionIds.forEach(sectionId => {
379
- // Find the heading with this ID
380
- const heading = document.getElementById(sectionId);
381
- if (!heading) {
382
- return; // ID not found
383
- }
384
-
385
- // Get the heading level (h1 = 1, h2 = 2, etc.)
386
- const headingLevel = parseInt(heading.tagName.substring(1));
387
-
388
- // Mark this heading as visible
389
- visibleElements.add(heading);
390
-
391
- // Find the index of this heading
392
- const headingIndex = headingsArray.indexOf(heading);
393
- if (headingIndex === -1) {
394
- return;
395
- }
396
-
397
- // Collect all elements until the next heading of the same or higher level
398
- let currentElement = heading.nextElementSibling;
399
-
400
- while (currentElement) {
401
- // Check if this is a heading
402
- const isHeading = /^H[1-6]$/.test(currentElement.tagName);
403
-
404
- if (isHeading) {
405
- const currentLevel = parseInt(currentElement.tagName.substring(1));
406
-
407
- // Stop if we hit a heading of the same or higher level (lower number)
408
- if (currentLevel <= headingLevel) {
409
- break;
410
- }
411
-
412
- // Include lower-level headings (subsections)
413
- visibleElements.add(currentElement);
414
- } else {
415
- // Include non-heading elements
416
- visibleElements.add(currentElement);
417
- }
418
-
419
- currentElement = currentElement.nextElementSibling;
420
- }
421
- });
422
-
423
- // Hide all elements that are not in visibleElements
424
- const markdownDiv = document.querySelector('main article .hyperbook-markdown');
425
- if (markdownDiv) {
426
- Array.from(markdownDiv.children).forEach(element => {
427
- // Don't hide UI elements (floating buttons container, side drawers)
428
- const isUIElement = element.id === 'floating-buttons-container' ||
429
- element.tagName === 'SIDE-DRAWER';
430
-
431
- if (!visibleElements.has(element) && !isUIElement) {
432
- element.style.display = 'none';
433
- }
434
- });
435
-
436
- // Hide TOC toggle when sections are filtered
437
- hideTocWhenFiltered();
438
- }
439
- }
440
-
441
- // Check for standalone layout URL parameter or iframe context
442
- function checkStandaloneMode() {
443
- // Check if explicitly requested via URL parameter
444
- const urlParams = new URLSearchParams(window.location.search);
445
- const standaloneParam = urlParams.get('standalone') === 'true';
446
-
447
- // Check if page is inside an iframe (but not VSCode webview)
448
- const isVSCodeWebview = typeof acquireVsCodeApi !== 'undefined';
449
- const isInIframe = window.self !== window.top && !isVSCodeWebview;
450
-
451
- if (standaloneParam || isInIframe) {
452
- const mainGrid = document.querySelector('.main-grid');
453
- if (mainGrid && !mainGrid.classList.contains('layout-standalone')) {
454
- mainGrid.classList.add('layout-standalone');
455
- }
456
-
457
- // Hide TOC and QR code buttons when in standalone mode
458
- const tocToggle = document.getElementById('toc-toggle');
459
- if (tocToggle) {
460
- tocToggle.style.display = 'none';
461
- }
462
-
463
- const qrcodeOpen = document.getElementById('qrcode-open');
464
- if (qrcodeOpen) {
465
- qrcodeOpen.style.display = 'none';
466
- }
467
- }
468
- }
469
-
470
- // Initialize existing elements on document load
471
- document.addEventListener("DOMContentLoaded", () => {
472
- init(document);
473
- checkStandaloneMode();
474
- filterSections();
475
- });
476
-
477
- // Observe for new elements added to the DOM
478
- const observer = new MutationObserver((mutations) => {
479
- mutations.forEach((mutation) => {
480
- mutation.addedNodes.forEach((node) => {
481
- if (node.nodeType === 1) {
482
- // Element node
483
- init(node);
484
- }
485
- });
486
- });
487
- });
488
-
489
- observer.observe(document.body, { childList: true, subtree: true });
490
-
491
- return {
492
- toggleLightbox,
493
- toggleBookmark,
494
- navToggle,
495
- tocToggle,
496
- searchToggle,
497
- search,
498
- qrcodeOpen,
499
- qrcodeClose,
500
- shareOpen,
501
- shareClose,
502
- shareUpdatePreview,
503
- shareCopyUrl,
504
- init,
505
- };
506
- })());