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
@@ -1,3 +1,12 @@
1
+ /// <reference path="../hyperbook.types.js" />
2
+
3
+ /**
4
+ * Web IDE with HTML/CSS/JS editing.
5
+ * @type {HyperbookWebide}
6
+ * @memberof hyperbook
7
+ * @see hyperbook.store
8
+ * @see hyperbook.i18n
9
+ */
1
10
  hyperbook.webide = (function () {
2
11
  window.codeInput?.registerTemplate(
3
12
  "webide-highlighted",
@@ -7,9 +16,7 @@ hyperbook.webide = (function () {
7
16
  ]),
8
17
  );
9
18
 
10
- const elems = document.getElementsByClassName("directive-webide");
11
-
12
- for (let elem of elems) {
19
+ function initElement(elem) {
13
20
  const title = elem.getElementsByClassName("container-title")[0];
14
21
  /** @type {HTMLTextAreaElement | null} */
15
22
  const editorHTML = elem.querySelector(".editor.html");
@@ -33,8 +40,8 @@ hyperbook.webide = (function () {
33
40
  const downloadEl = elem.querySelector("button.download");
34
41
 
35
42
  resetEl?.addEventListener("click", () => {
36
- if (window.confirm(i18n.get("webide-reset-prompt"))) {
37
- store.webide.delete(id);
43
+ if (window.confirm(hyperbook.i18n.get("webide-reset-prompt"))) {
44
+ hyperbook.store.webide.delete(id);
38
45
  window.location.reload();
39
46
  }
40
47
  });
@@ -70,7 +77,7 @@ hyperbook.webide = (function () {
70
77
  });
71
78
 
72
79
  const load = async () => {
73
- const result = await store.webide.get(id);
80
+ const result = await hyperbook.store.webide.get(id);
74
81
  if (!result) {
75
82
  return;
76
83
  }
@@ -84,7 +91,7 @@ hyperbook.webide = (function () {
84
91
  load();
85
92
 
86
93
  const update = () => {
87
- store.webide.put({
94
+ hyperbook.store.webide.put({
88
95
  id,
89
96
  html: editorHTML?.value,
90
97
  css: editorCSS?.value,
@@ -102,7 +109,7 @@ hyperbook.webide = (function () {
102
109
  });
103
110
 
104
111
  editorHTML?.addEventListener("code-input_load", async () => {
105
- const result = await store.webide.get(id);
112
+ const result = await hyperbook.store.webide.get(id);
106
113
  if (result) {
107
114
  editorHTML.value = result.html;
108
115
  }
@@ -115,7 +122,7 @@ hyperbook.webide = (function () {
115
122
  });
116
123
 
117
124
  editorCSS?.addEventListener("code-input_load", async () => {
118
- const result = await store.webide.get(id);
125
+ const result = await hyperbook.store.webide.get(id);
119
126
  if (result) {
120
127
  editorCSS.value = result.css;
121
128
  }
@@ -128,7 +135,7 @@ hyperbook.webide = (function () {
128
135
  });
129
136
 
130
137
  editorJS?.addEventListener("code-input_load", async () => {
131
- const result = await store.webide.get(id);
138
+ const result = await hyperbook.store.webide.get(id);
132
139
  if (result) {
133
140
  editorJS.value = result.js;
134
141
  }
@@ -149,4 +156,32 @@ hyperbook.webide = (function () {
149
156
  a.click();
150
157
  });
151
158
  }
159
+
160
+ function init(root) {
161
+ const elems = root.querySelectorAll(".directive-webide");
162
+ elems.forEach(initElement);
163
+ }
164
+
165
+ // Initialize existing elements on document load
166
+ document.addEventListener("DOMContentLoaded", () => {
167
+ init(document);
168
+ });
169
+
170
+ // Observe for new elements added to the DOM
171
+ const observer = new MutationObserver((mutations) => {
172
+ mutations.forEach((mutation) => {
173
+ mutation.addedNodes.forEach((node) => {
174
+ if (
175
+ node.nodeType === 1 &&
176
+ node.classList.contains("directive-webide")
177
+ ) {
178
+ initElement(node);
179
+ }
180
+ });
181
+ });
182
+ });
183
+
184
+ observer.observe(document.body, { childList: true, subtree: true });
185
+
186
+ return { init };
152
187
  })();
@@ -0,0 +1,99 @@
1
+ /// <reference path="../hyperbook.types.js" />
2
+ window.hyperbook = window.hyperbook || {};
3
+
4
+ hyperbook.youtube = hyperbook.youtube || {};
5
+
6
+ hyperbook.youtube.consent = (function () {
7
+ var CONSENT_ID = "youtube";
8
+
9
+ async function isAllowed() {
10
+ try {
11
+ var entry = await hyperbook.store.consent.get(CONSENT_ID);
12
+ return entry?.allowed === true;
13
+ } catch (e) {
14
+ return false;
15
+ }
16
+ }
17
+
18
+ async function allow() {
19
+ await hyperbook.store.consent.put({ id: CONSENT_ID, allowed: true });
20
+ }
21
+
22
+ function loadContent(wrapper) {
23
+ var banner = wrapper.querySelector(".directive-youtube-consent-banner");
24
+ if (banner) {
25
+ banner.remove();
26
+ }
27
+ var iframe = wrapper.querySelector("iframe");
28
+ if (iframe) {
29
+ var src = iframe.getAttribute("data-consent-src");
30
+ if (src) {
31
+ iframe.setAttribute("src", src);
32
+ iframe.removeAttribute("data-consent-src");
33
+ iframe.style.display = "";
34
+ }
35
+ }
36
+ }
37
+
38
+ async function initWrapper(wrapper) {
39
+ if (wrapper.getAttribute("data-consent-initialized")) return;
40
+ wrapper.setAttribute("data-consent-initialized", "true");
41
+
42
+ var allowed = await isAllowed();
43
+ if (allowed) {
44
+ loadContent(wrapper);
45
+ return;
46
+ }
47
+
48
+ var btn = wrapper.querySelector(".directive-youtube-consent-accept-btn");
49
+ var checkbox = wrapper.querySelector(
50
+ ".directive-youtube-consent-always-checkbox"
51
+ );
52
+ if (btn) {
53
+ btn.addEventListener("click", async function () {
54
+ if (checkbox && checkbox.checked) {
55
+ await allow();
56
+ }
57
+ loadContent(wrapper);
58
+ if (checkbox && checkbox.checked) {
59
+ document
60
+ .querySelectorAll(".directive-youtube-consent")
61
+ .forEach(function (el) {
62
+ loadContent(el);
63
+ });
64
+ }
65
+ });
66
+ }
67
+ }
68
+
69
+ function init(root) {
70
+ var wrappers = root.querySelectorAll
71
+ ? root.querySelectorAll(".directive-youtube-consent")
72
+ : [];
73
+ wrappers.forEach(function (w) {
74
+ initWrapper(w);
75
+ });
76
+ }
77
+
78
+ document.addEventListener("DOMContentLoaded", function () {
79
+ init(document);
80
+ });
81
+
82
+ var observer = new MutationObserver(function (mutations) {
83
+ mutations.forEach(function (mutation) {
84
+ mutation.addedNodes.forEach(function (node) {
85
+ if (node.nodeType === 1) {
86
+ init(node);
87
+ }
88
+ });
89
+ });
90
+ });
91
+
92
+ observer.observe(document.body, { childList: true, subtree: true });
93
+
94
+ return {
95
+ isAllowed: isAllowed,
96
+ allow: allow,
97
+ init: init,
98
+ };
99
+ })();
@@ -14,3 +14,66 @@
14
14
  height: 100%;
15
15
  }
16
16
 
17
+ .directive-youtube-consent {
18
+ position: absolute;
19
+ top: 0;
20
+ left: 0;
21
+ width: 100%;
22
+ height: 100%;
23
+ }
24
+
25
+ .directive-youtube-consent iframe[data-consent-src] {
26
+ display: none;
27
+ }
28
+
29
+ .directive-youtube-consent-banner {
30
+ display: flex;
31
+ flex-direction: column;
32
+ align-items: center;
33
+ justify-content: center;
34
+ gap: 12px;
35
+ padding: 24px;
36
+ background-color: var(--color-background-tinted, #f5f5f5);
37
+ border: 2px solid var(--color-brand, #333);
38
+ border-radius: 8px;
39
+ text-align: center;
40
+ height: 100%;
41
+ box-sizing: border-box;
42
+ }
43
+
44
+ .directive-youtube-consent-banner-text {
45
+ font-size: 0.95em;
46
+ line-height: 1.5;
47
+ color: var(--color-text, #333);
48
+ max-width: 600px;
49
+ }
50
+
51
+ .directive-youtube-consent-always-label {
52
+ display: flex;
53
+ align-items: center;
54
+ gap: 6px;
55
+ font-size: 0.85em;
56
+ color: var(--color-text, #333);
57
+ cursor: pointer;
58
+ }
59
+
60
+ .directive-youtube-consent-always-checkbox {
61
+ cursor: pointer;
62
+ margin: 0!important;
63
+ vertical-align: middle;
64
+ }
65
+
66
+ .directive-youtube-consent-accept-btn {
67
+ padding: 8px 24px;
68
+ background-color: var(--color-brand, #333);
69
+ color: var(--color-brand-text, #fff);
70
+ border: none;
71
+ border-radius: 4px;
72
+ cursor: pointer;
73
+ font-size: 0.95em;
74
+ }
75
+
76
+ .directive-youtube-consent-accept-btn:hover {
77
+ opacity: 0.9;
78
+ }
79
+
@@ -0,0 +1,209 @@
1
+ /**
2
+ * @file Type definitions for the hyperbook global namespace.
3
+ * This file is NOT loaded in the browser. It exists solely for IDE support
4
+ * and JSDoc cross-referencing between scripts.
5
+ *
6
+ * Usage: Add the following to the top of any script that references
7
+ * other hyperbook modules:
8
+ * /// <reference path="../hyperbook.types.js" />
9
+ */
10
+
11
+ /**
12
+ * @namespace hyperbook
13
+ * @description Global namespace for all hyperbook client-side modules.
14
+ * Initialized by i18n.js (first script to load) with
15
+ * `window.hyperbook = window.hyperbook || {}`.
16
+ */
17
+
18
+ /**
19
+ * @typedef {Object} HyperbookI18n
20
+ * @property {(key: string, values?: Record<string, string>) => string} get
21
+ * Returns the translated string for the given key, substituting `{{placeholder}}`
22
+ * patterns with values from the `values` object. Falls back to the key itself
23
+ * if no translation is found.
24
+ */
25
+
26
+ /**
27
+ * @typedef {Object} HyperbookStore
28
+ * @property {import("dexie").Dexie} db - The underlying Dexie database instance.
29
+ * @property {import("dexie").Table} currentState - Ephemeral UI state (mouse, scroll, window size).
30
+ * @property {import("dexie").Table} collapsibles - Persisted open/close state for collapsible elements.
31
+ * @property {import("dexie").Table} abcMusic - ABC music editor tunes.
32
+ * @property {import("dexie").Table} audio - Audio playback positions.
33
+ * @property {import("dexie").Table} bookmarks - User bookmarks (path + label).
34
+ * @property {import("dexie").Table} p5 - p5.js sketches.
35
+ * @property {import("dexie").Table} protect - Password hashes for protected content.
36
+ * @property {import("dexie").Table} pyide - Python IDE scripts.
37
+ * @property {import("dexie").Table} slideshow - Active slide indices.
38
+ * @property {import("dexie").Table} tabs - Active tab selections.
39
+ * @property {import("dexie").Table} excalidraw - Excalidraw drawing state.
40
+ * @property {import("dexie").Table} webide - Web IDE editor contents (HTML/CSS/JS).
41
+ * @property {import("dexie").Table} h5p - H5P user data.
42
+ * @property {import("dexie").Table} geogebra - GeoGebra applet state.
43
+ * @property {import("dexie").Table} learningmap - Learning map node positions and zoom.
44
+ * @property {import("dexie").Table} textinput - Text input field values.
45
+ * @property {import("dexie").Table} custom - Custom directive payload storage.
46
+ * @property {import("dexie").Table} onlineide - Online IDE scripts.
47
+ * @property {import("dexie").Table} sqlideScripts - SQL IDE scripts.
48
+ * @property {import("dexie").Table} sqlideDatabases - SQL IDE databases.
49
+ * @property {import("dexie").Table} multievent - Multi-event state.
50
+ * @property {import("dexie").Table} typst - Typst editor code.
51
+ * @property {() => Promise<void>} export - Export all store data as a JSON download.
52
+ * @property {() => Promise<void>} reset - Clear all store data after user confirmation.
53
+ * @property {() => Promise<void>} import - Import store data from a JSON file.
54
+ */
55
+
56
+ /**
57
+ * @typedef {Object} HyperbookCloud
58
+ * @property {() => Promise<void>} save - Manually trigger a save to the cloud.
59
+ * @property {() => Promise<void>} sendSnapshot - Send a full snapshot to the cloud.
60
+ * @property {() => void} userToggle - Toggle the user login/info drawer.
61
+ * @property {() => Promise<void>} login - Log in with username and password from the form.
62
+ * @property {() => void} logout - Log out and clear auth state.
63
+ */
64
+
65
+ /**
66
+ * @typedef {Object} HyperbookAbc
67
+ * @property {(root: HTMLElement) => void} init - Initialize ABC music elements.
68
+ */
69
+
70
+ /**
71
+ * @typedef {Object} HyperbookArchive
72
+ * @property {() => void} init - Initialize archive download status indicators.
73
+ */
74
+
75
+ /**
76
+ * @typedef {Object} HyperbookAudio
77
+ * @property {(id: string) => void} togglePlayPause - Toggle play/pause for an audio element.
78
+ */
79
+
80
+ /**
81
+ * @typedef {Object} HyperbookBookmarks
82
+ * @property {(root?: Document) => void} update - Refresh the bookmarks list in the DOM.
83
+ */
84
+
85
+ /**
86
+ * @typedef {Object} HyperbookDownload
87
+ * @property {(root: HTMLElement) => void} init - Initialize download status indicators.
88
+ */
89
+
90
+ /**
91
+ * @typedef {Object} HyperbookExcalidraw
92
+ */
93
+
94
+ /**
95
+ * @typedef {Object} HyperbookGeogebra
96
+ * @property {(root: HTMLElement) => void} init - Initialize GeoGebra applets.
97
+ */
98
+
99
+ /**
100
+ * @typedef {Object} HyperbookH5p
101
+ * @property {(root: HTMLElement) => Promise<void>} init - Initialize H5P elements.
102
+ * @property {(id: string) => void} save - Save H5P user data for an element.
103
+ */
104
+
105
+ /**
106
+ * @typedef {Object} HyperbookLearningmap
107
+ * @property {(root: HTMLElement) => void} init - Initialize learning map elements.
108
+ */
109
+
110
+ /**
111
+ * @typedef {Object} HyperbookMermaid
112
+ * @property {() => void} init - Initialize mermaid diagrams.
113
+ */
114
+
115
+ /**
116
+ * @typedef {Object} HyperbookOnlineide
117
+ * @property {(el: HTMLElement) => void} openFullscreen - Open the online IDE in fullscreen.
118
+ */
119
+
120
+ /**
121
+ * @typedef {Object} HyperbookP5
122
+ */
123
+
124
+ /**
125
+ * @typedef {Object} HyperbookProtect
126
+ * @property {(root: HTMLElement) => void} init - Initialize protected content elements.
127
+ */
128
+
129
+ /**
130
+ * @typedef {Object} HyperbookPython
131
+ * @property {(root: HTMLElement) => void} init - Initialize Python IDE elements.
132
+ */
133
+
134
+ /**
135
+ * @typedef {Object} HyperbookScratchblock
136
+ * @property {() => void} init - Render all scratch blocks on the page.
137
+ */
138
+
139
+ /**
140
+ * @typedef {Object} HyperbookSlideshow
141
+ * @property {(id: string) => void} update - Update slideshow state from store.
142
+ * @property {(id: string, steps: number) => void} moveBy - Move the slideshow by a number of steps.
143
+ * @property {(id: string, index: number) => void} setActive - Set the active slide by index.
144
+ * @property {(root: HTMLElement) => void} init - Initialize slideshows within a root element.
145
+ */
146
+
147
+ /**
148
+ * @typedef {Object} HyperbookSqlide
149
+ * @property {(el: HTMLElement) => void} openFullscreen - Open the SQL IDE in fullscreen.
150
+ */
151
+
152
+ /**
153
+ * @typedef {Object} HyperbookTabs
154
+ * @property {(tabsId: string, tabId: string) => void} selectTab - Select a tab by ID.
155
+ * @property {(root: HTMLElement) => void} init - Initialize tab elements.
156
+ */
157
+
158
+ /**
159
+ * @typedef {Object} HyperbookTextinput
160
+ * @property {(root: HTMLElement) => void} init - Initialize text input elements.
161
+ */
162
+
163
+ /**
164
+ * @typedef {Object} HyperbookWebide
165
+ */
166
+
167
+ /**
168
+ * Global hyperbook namespace available on `window.hyperbook`.
169
+ *
170
+ * @type {{
171
+ * i18n: HyperbookI18n,
172
+ * store: HyperbookStore,
173
+ * cloud?: HyperbookCloud,
174
+ * toggleLightbox: (el: HTMLElement) => void,
175
+ * toggleBookmark: (key: string, label: string) => void,
176
+ * navToggle: () => void,
177
+ * tocToggle: () => void,
178
+ * searchToggle: () => void,
179
+ * search: () => void,
180
+ * qrcodeOpen: () => void,
181
+ * qrcodeClose: () => void,
182
+ * shareOpen: () => void,
183
+ * shareClose: () => void,
184
+ * shareUpdatePreview: () => void,
185
+ * shareCopyUrl: () => void,
186
+ * init: (root: HTMLElement) => void,
187
+ * abc?: HyperbookAbc,
188
+ * archive?: HyperbookArchive,
189
+ * audio?: HyperbookAudio,
190
+ * bookmarks?: HyperbookBookmarks,
191
+ * download?: HyperbookDownload,
192
+ * excalidraw?: HyperbookExcalidraw,
193
+ * geogebra?: HyperbookGeogebra,
194
+ * h5p?: HyperbookH5p,
195
+ * learningmap?: HyperbookLearningmap,
196
+ * mermaid?: HyperbookMermaid,
197
+ * onlineide?: HyperbookOnlineide,
198
+ * p5?: HyperbookP5,
199
+ * protect?: HyperbookProtect,
200
+ * python?: HyperbookPython,
201
+ * scratchblock?: HyperbookScratchblock,
202
+ * slideshow?: HyperbookSlideshow,
203
+ * sqlide?: HyperbookSqlide,
204
+ * tabs?: HyperbookTabs,
205
+ * textinput?: HyperbookTextinput,
206
+ * webide?: HyperbookWebide,
207
+ * }}
208
+ */
209
+ var hyperbook;
@@ -1,8 +1,22 @@
1
- var i18n = (function () {
1
+ /// <reference path="./hyperbook.types.js" />
2
+ window.hyperbook = window.hyperbook || {};
3
+
4
+ /**
5
+ * Internationalization module providing translation lookups.
6
+ * @type {HyperbookI18n}
7
+ * @memberof hyperbook
8
+ */
9
+ hyperbook.i18n = (function () {
2
10
  // LOCALES
3
11
  const locales = {};
4
12
  // LOCALES
5
13
 
14
+ /**
15
+ * Get a translated string by key, with optional placeholder substitution.
16
+ * @param {string} key - The translation key.
17
+ * @param {Record<string, string>} [values] - Placeholder values to substitute.
18
+ * @returns {string} The translated string, or the key itself if not found.
19
+ */
6
20
  const get = (key, values) => {
7
21
  if (!locales[key]) {
8
22
  console.warn(