hyperbook 0.84.4 → 0.84.5

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 (33) 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-excalidraw/client.js +9 -0
  9. package/dist/assets/directive-excalidraw/hyperbook-excalidraw.umd.js +1 -1
  10. package/dist/assets/directive-geogebra/client.js +16 -3
  11. package/dist/assets/directive-h5p/client.js +32 -3
  12. package/dist/assets/directive-learningmap/client.js +11 -3
  13. package/dist/assets/directive-mermaid/client.js +11 -1
  14. package/dist/assets/directive-multievent/multievent.js +2 -2
  15. package/dist/assets/directive-onlineide/client.js +7 -0
  16. package/dist/assets/directive-onlineide/include/online-ide-embedded.js +43 -43
  17. package/dist/assets/directive-p5/client.js +39 -7
  18. package/dist/assets/directive-protect/client.js +11 -3
  19. package/dist/assets/directive-pyide/client.js +20 -9
  20. package/dist/assets/directive-scratchblock/client.js +9 -0
  21. package/dist/assets/directive-slideshow/client.js +12 -4
  22. package/dist/assets/directive-sqlide/client.js +7 -0
  23. package/dist/assets/directive-sqlide/include/sql-ide-embedded.js +3 -3
  24. package/dist/assets/directive-tabs/client.js +14 -3
  25. package/dist/assets/directive-textinput/client.js +14 -3
  26. package/dist/assets/directive-webide/client.js +45 -10
  27. package/dist/assets/hyperbook.types.js +209 -0
  28. package/dist/assets/i18n.js +15 -1
  29. package/dist/assets/store.js +173 -139
  30. package/dist/assets/ui.js +279 -0
  31. package/dist/index.js +27 -18
  32. package/package.json +3 -3
  33. package/dist/assets/client.js +0 -506
@@ -0,0 +1,248 @@
1
+ /// <reference path="./hyperbook.types.js" />
2
+ window.hyperbook = window.hyperbook || {};
3
+
4
+ /**
5
+ * Bootstrap script: initializes collapsibles, search, bookmarks,
6
+ * standalone mode, and section filtering on DOMContentLoaded.
7
+ * @type {HyperbookBootstrap}
8
+ * @memberof hyperbook
9
+ * @see hyperbook.store
10
+ */
11
+ hyperbook.bootstrap = (function () {
12
+ /**
13
+ * Initialize collapsible elements within the given root.
14
+ * @param {HTMLElement} root
15
+ */
16
+ const initCollapsibles = (root) => {
17
+ const detailsEls = root.querySelectorAll("details.section, details.directive-collapsible");
18
+ for (let details of detailsEls) {
19
+ const id = details.getAttribute("data-id");
20
+
21
+ // Prevent link clicks from toggling the details element in navigation
22
+ if (id && id.startsWith("_nav:") && !details.classList.contains("empty")) {
23
+ const link = details.querySelector("summary a");
24
+ link?.addEventListener("click", (event) => {
25
+ event.stopPropagation();
26
+ });
27
+ }
28
+
29
+ // Listen for toggle events to persist state and sync with other elements
30
+ details.addEventListener("toggle", () => {
31
+ if (id) {
32
+ if (details.open) {
33
+ hyperbook.store.collapsibles.put({ id });
34
+ } else {
35
+ hyperbook.store.collapsibles.delete(id);
36
+ }
37
+
38
+ // Sync all elements with the same ID
39
+ const allWithSameId = document.querySelectorAll(`[data-id="${id}"]`);
40
+ for (let el of allWithSameId) {
41
+ if (el !== details && el.tagName === "DETAILS") {
42
+ el.open = details.open;
43
+ }
44
+ }
45
+ }
46
+
47
+ setTimeout(() => {
48
+ window.dispatchEvent(new Event("resize"));
49
+ }, 100);
50
+ });
51
+ }
52
+ updateCollapsibles(root);
53
+ };
54
+
55
+ /**
56
+ * Restore collapsible open/close state from the store.
57
+ * @param {HTMLElement} root
58
+ */
59
+ const updateCollapsibles = (root) => {
60
+ hyperbook.store.collapsibles.toArray().then((collapsibles) => {
61
+ const detailsEls = root.querySelectorAll("details.section, details.directive-collapsible");
62
+ for (let details of detailsEls) {
63
+ const id = details.getAttribute("data-id");
64
+ if (id) {
65
+ const shouldBeOpen = collapsibles.some((c) => c.id === id);
66
+ if (!id.startsWith("_nav:") && details.open !== shouldBeOpen) {
67
+ details.open = shouldBeOpen;
68
+ }
69
+ }
70
+ }
71
+ });
72
+ };
73
+
74
+ /**
75
+ * Initialize search input Enter key handling.
76
+ * @param {HTMLElement} root
77
+ */
78
+ const initSearch = (root) => {
79
+ const searchInputEl = root.querySelector("#search-input");
80
+ if (searchInputEl) {
81
+ searchInputEl.addEventListener("keypress", (event) => {
82
+ if (event.key === "Enter") {
83
+ event.preventDefault();
84
+ hyperbook.ui.search();
85
+ }
86
+ });
87
+ }
88
+ };
89
+
90
+ /**
91
+ * Initialize bookmark active states within the given root.
92
+ * @param {HTMLElement} [root=document]
93
+ */
94
+ function initBookmarks(root = document) {
95
+ const bookmarkEls = root.getElementsByClassName("bookmark");
96
+ for (let bookmarkEl of bookmarkEls) {
97
+ const key = bookmarkEl.getAttribute("data-key");
98
+ hyperbook.store.bookmarks.get(key).then((bookmark) => {
99
+ if (bookmark) {
100
+ bookmarkEl.classList.add("active");
101
+ }
102
+ });
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Initialize all hyperbook elements within a root.
108
+ * @param {HTMLElement} root
109
+ */
110
+ function init(root) {
111
+ initCollapsibles(root);
112
+ initSearch(root);
113
+ initBookmarks(root);
114
+ }
115
+
116
+ /**
117
+ * Hide TOC toggle button when sections are filtered.
118
+ */
119
+ function hideTocWhenFiltered() {
120
+ const tocToggle = document.getElementById('toc-toggle');
121
+ if (tocToggle) {
122
+ tocToggle.style.display = 'none';
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Filter sections based on the `sections` query parameter.
128
+ */
129
+ function filterSections() {
130
+ const urlParams = new URLSearchParams(window.location.search);
131
+ const sectionsParam = urlParams.get('sections');
132
+
133
+ if (!sectionsParam) {
134
+ return;
135
+ }
136
+
137
+ const sectionIds = sectionsParam.split(',').map(id => id.trim()).filter(id => id);
138
+
139
+ if (sectionIds.length === 0) {
140
+ return;
141
+ }
142
+
143
+ const allHeadings = document.querySelectorAll('h1, h2, h3, h4, h5, h6');
144
+ const headingsArray = Array.from(allHeadings);
145
+
146
+ const visibleElements = new Set();
147
+
148
+ sectionIds.forEach(sectionId => {
149
+ const heading = document.getElementById(sectionId);
150
+ if (!heading) {
151
+ return;
152
+ }
153
+
154
+ const headingLevel = parseInt(heading.tagName.substring(1));
155
+
156
+ visibleElements.add(heading);
157
+
158
+ const headingIndex = headingsArray.indexOf(heading);
159
+ if (headingIndex === -1) {
160
+ return;
161
+ }
162
+
163
+ let currentElement = heading.nextElementSibling;
164
+
165
+ while (currentElement) {
166
+ const isHeading = /^H[1-6]$/.test(currentElement.tagName);
167
+
168
+ if (isHeading) {
169
+ const currentLevel = parseInt(currentElement.tagName.substring(1));
170
+
171
+ if (currentLevel <= headingLevel) {
172
+ break;
173
+ }
174
+
175
+ visibleElements.add(currentElement);
176
+ } else {
177
+ visibleElements.add(currentElement);
178
+ }
179
+
180
+ currentElement = currentElement.nextElementSibling;
181
+ }
182
+ });
183
+
184
+ const markdownDiv = document.querySelector('main article .hyperbook-markdown');
185
+ if (markdownDiv) {
186
+ Array.from(markdownDiv.children).forEach(element => {
187
+ const isUIElement = element.id === 'floating-buttons-container' ||
188
+ element.tagName === 'SIDE-DRAWER';
189
+
190
+ if (!visibleElements.has(element) && !isUIElement) {
191
+ element.style.display = 'none';
192
+ }
193
+ });
194
+
195
+ hideTocWhenFiltered();
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Check for standalone layout URL parameter or iframe context.
201
+ */
202
+ function checkStandaloneMode() {
203
+ const urlParams = new URLSearchParams(window.location.search);
204
+ const standaloneParam = urlParams.get('standalone') === 'true';
205
+
206
+ const isVSCodeWebview = typeof acquireVsCodeApi !== 'undefined';
207
+ const isInIframe = window.self !== window.top && !isVSCodeWebview;
208
+
209
+ if (standaloneParam || isInIframe) {
210
+ const mainGrid = document.querySelector('.main-grid');
211
+ if (mainGrid && !mainGrid.classList.contains('layout-standalone')) {
212
+ mainGrid.classList.add('layout-standalone');
213
+ }
214
+
215
+ const tocToggle = document.getElementById('toc-toggle');
216
+ if (tocToggle) {
217
+ tocToggle.style.display = 'none';
218
+ }
219
+
220
+ const qrcodeOpen = document.getElementById('qrcode-open');
221
+ if (qrcodeOpen) {
222
+ qrcodeOpen.style.display = 'none';
223
+ }
224
+ }
225
+ }
226
+
227
+ // Initialize on DOMContentLoaded
228
+ document.addEventListener("DOMContentLoaded", () => {
229
+ init(document);
230
+ checkStandaloneMode();
231
+ filterSections();
232
+ });
233
+
234
+ // Observe for new elements added to the DOM
235
+ const observer = new MutationObserver((mutations) => {
236
+ mutations.forEach((mutation) => {
237
+ mutation.addedNodes.forEach((node) => {
238
+ if (node.nodeType === 1) {
239
+ init(node);
240
+ }
241
+ });
242
+ });
243
+ });
244
+
245
+ observer.observe(document.body, { childList: true, subtree: true });
246
+
247
+ return { init };
248
+ })();
@@ -1,4 +1,15 @@
1
- window.hyperbook.cloud = (function () {
1
+ /// <reference path="./hyperbook.types.js" />
2
+ window.hyperbook = window.hyperbook || {};
3
+
4
+ /**
5
+ * Cloud sync integration for hyperbook store data.
6
+ * Handles authentication, event-based sync, and snapshot sync.
7
+ * @type {HyperbookCloud}
8
+ * @memberof hyperbook
9
+ * @see hyperbook.store
10
+ * @see hyperbook.i18n
11
+ */
12
+ hyperbook.cloud = (function () {
2
13
  // ===== Cloud Integration =====
3
14
  const AUTH_TOKEN_KEY = "hyperbook_auth_token";
4
15
  const AUTH_USER_KEY = "hyperbook_auth_user";
@@ -188,8 +199,8 @@ window.hyperbook.cloud = (function () {
188
199
  }
189
200
 
190
201
  async sendSnapshot() {
191
- const hyperbookExport = await store.export({ prettyJson: false });
192
- const exportData = JSON.parse(await hyperbookExport.text());
202
+ const storeExport = await hyperbook.store.db.export({ prettyJson: false });
203
+ const exportData = JSON.parse(await storeExport.text());
193
204
 
194
205
  const data = await apiRequest(
195
206
  `/api/store/${HYPERBOOK_CLOUD.id}/snapshot`,
@@ -532,7 +543,7 @@ window.hyperbook.cloud = (function () {
532
543
  const blob = new Blob([JSON.stringify(hyperbook)], {
533
544
  type: "application/json",
534
545
  });
535
- await store.import(blob, { clearTablesBeforeImport: true });
546
+ await hyperbook.store.db.import(blob, { clearTablesBeforeImport: true });
536
547
  }
537
548
 
538
549
  // Track the server's lastEventId
@@ -589,7 +600,7 @@ window.hyperbook.cloud = (function () {
589
600
  });
590
601
 
591
602
  // Hook Dexie tables to capture granular events (skip currentState — ephemeral UI data)
592
- store.tables.forEach((table) => {
603
+ hyperbook.store.tables.forEach((table) => {
593
604
  if (table.name === "currentState") return;
594
605
 
595
606
  table.hook("creating", function (primKey, obj) {
@@ -656,29 +667,29 @@ window.hyperbook.cloud = (function () {
656
667
  statusEl.className = status;
657
668
 
658
669
  if (status === "unsaved") {
659
- statusEl.textContent = i18n.get("user-unsaved", {}, "Unsaved changes");
670
+ statusEl.textContent = hyperbook.i18n.get("user-unsaved", {}, "Unsaved changes");
660
671
  updateUserIconState("logged-in");
661
672
  } else if (status === "saving") {
662
- statusEl.textContent = i18n.get("user-saving", {}, "Saving...");
673
+ statusEl.textContent = hyperbook.i18n.get("user-saving", {}, "Saving...");
663
674
  updateUserIconState("syncing");
664
675
  } else if (status === "saved") {
665
- statusEl.textContent = i18n.get("user-saved", {}, "Saved");
676
+ statusEl.textContent = hyperbook.i18n.get("user-saved", {}, "Saved");
666
677
  updateUserIconState("synced");
667
678
  } else if (status === "error") {
668
- statusEl.textContent = i18n.get("user-save-error", {}, "Save Error");
679
+ statusEl.textContent = hyperbook.i18n.get("user-save-error", {}, "Save Error");
669
680
  updateUserIconState("unsynced");
670
681
  } else if (status === "offline") {
671
- statusEl.textContent = i18n.get("user-offline", {}, "Offline");
682
+ statusEl.textContent = hyperbook.i18n.get("user-offline", {}, "Offline");
672
683
  updateUserIconState("unsynced");
673
684
  } else if (status === "offline-queued") {
674
- statusEl.textContent = i18n.get(
685
+ statusEl.textContent = hyperbook.i18n.get(
675
686
  "user-offline-queued",
676
687
  {},
677
688
  "Saved locally",
678
689
  );
679
690
  updateUserIconState("logged-in");
680
691
  } else if (status === "readonly") {
681
- statusEl.textContent = i18n.get("user-readonly", {}, "Read-Only Mode");
692
+ statusEl.textContent = hyperbook.i18n.get("user-readonly", {}, "Read-Only Mode");
682
693
  statusEl.className = "readonly";
683
694
  updateUserIconState("synced");
684
695
  }
@@ -707,7 +718,7 @@ window.hyperbook.cloud = (function () {
707
718
  const errorEl = document.getElementById("user-login-error");
708
719
 
709
720
  if (!username || !password) {
710
- errorEl.textContent = i18n.get(
721
+ errorEl.textContent = hyperbook.i18n.get(
711
722
  "user-login-required",
712
723
  {},
713
724
  "Username and password required",
@@ -721,14 +732,14 @@ window.hyperbook.cloud = (function () {
721
732
  errorEl.textContent = "";
722
733
  } catch (error) {
723
734
  errorEl.textContent =
724
- error.message || i18n.get("user-login-failed", {}, "Login failed");
735
+ error.message || hyperbook.i18n.get("user-login-failed", {}, "Login failed");
725
736
  }
726
737
  };
727
738
 
728
739
  const logout = () => {
729
740
  if (
730
741
  confirm(
731
- i18n.get("user-logout-confirm", {}, "Are you sure you want to logout?"),
742
+ hyperbook.i18n.get("user-logout-confirm", {}, "Are you sure you want to logout?"),
732
743
  )
733
744
  ) {
734
745
  hyperbookLogout();
@@ -761,8 +772,8 @@ window.hyperbook.cloud = (function () {
761
772
  const banner = document.createElement("div");
762
773
  banner.id = "impersonation-banner";
763
774
  banner.innerHTML = `
764
- <span>${i18n.get("user-impersonating", {}, "Impersonating")}: <strong>${user ? user.username : ""}</strong> — ${i18n.get("user-readonly", {}, "Read-Only Mode")}</span>
765
- <a href="#" id="exit-impersonation">${i18n.get("user-exit-impersonation", {}, "Exit Impersonation")}</a>
775
+ <span>${hyperbook.i18n.get("user-impersonating", {}, "Impersonating")}: <strong>${user ? user.username : ""}</strong> — ${hyperbook.i18n.get("user-readonly", {}, "Read-Only Mode")}</span>
776
+ <a href="#" id="exit-impersonation">${hyperbook.i18n.get("user-exit-impersonation", {}, "Exit Impersonation")}</a>
766
777
  `;
767
778
  document.body.prepend(banner);
768
779
 
@@ -1,3 +1,11 @@
1
+ /// <reference path="../hyperbook.types.js" />
2
+
3
+ /**
4
+ * ABC music notation rendering and editing.
5
+ * @type {HyperbookAbc}
6
+ * @memberof hyperbook
7
+ * @see hyperbook.store
8
+ */
1
9
  hyperbook.abc = (function () {
2
10
  window.codeInput?.registerTemplate(
3
11
  "abc-highlighted",
@@ -33,7 +41,7 @@ hyperbook.abc = (function () {
33
41
  });
34
42
 
35
43
  resetEl?.addEventListener("click", () => {
36
- store.abcMusic.delete(id);
44
+ hyperbook.store.abcMusic.delete(id);
37
45
  window.location.reload();
38
46
  });
39
47
 
@@ -46,7 +54,7 @@ hyperbook.abc = (function () {
46
54
  });
47
55
 
48
56
  editorEl.addEventListener("code-input_load", async () => {
49
- const storeResult = await store.abcMusic
57
+ const storeResult = await hyperbook.store.abcMusic
50
58
  .where("id")
51
59
  .equals(editorEl.id)
52
60
  .first();
@@ -70,7 +78,7 @@ hyperbook.abc = (function () {
70
78
  });
71
79
 
72
80
  editorEl.addEventListener("change", () => {
73
- store.abcMusic.put({
81
+ hyperbook.store.abcMusic.put({
74
82
  id: editorEl.id,
75
83
  tune: editorEl.value,
76
84
  });
@@ -1,4 +1,12 @@
1
- hyperbook.download = (function () {
1
+ /// <reference path="../hyperbook.types.js" />
2
+
3
+ /**
4
+ * Archive download management.
5
+ * @type {HyperbookArchive}
6
+ * @memberof hyperbook
7
+ * @see hyperbook.i18n
8
+ */
9
+ hyperbook.archive = (function () {
2
10
  function initElement(el) {
3
11
  const labelEl = el.getElementsByClassName("label")[0];
4
12
  const src = el.href;
@@ -7,10 +15,10 @@ hyperbook.download = (function () {
7
15
  const isOnline = r.ok;
8
16
  if (isOnline) {
9
17
  labelEl.classList.remove("offline");
10
- labelEl.innerHTML = labelEl.innerHTML.replace(`(${i18n.get("archive-offline")})`, "");
18
+ labelEl.innerHTML = labelEl.innerHTML.replace(`(${hyperbook.i18n.get("archive-offline")})`, "");
11
19
  } else {
12
20
  labelEl.classList.add("offline");
13
- labelEl.innerHTML += ` (${i18n.get("archive-offline")})`;
21
+ labelEl.innerHTML += ` (${hyperbook.i18n.get("archive-offline")})`;
14
22
  }
15
23
  });
16
24
  }
@@ -36,5 +44,10 @@ hyperbook.download = (function () {
36
44
 
37
45
  observer.observe(document.body, { childList: true, subtree: true });
38
46
 
39
- init();
47
+ // Initialize existing elements on document load
48
+ document.addEventListener("DOMContentLoaded", () => {
49
+ init();
50
+ });
51
+
52
+ return { init };
40
53
  })();
@@ -1,3 +1,11 @@
1
+ /// <reference path="../hyperbook.types.js" />
2
+
3
+ /**
4
+ * Audio player with playback controls and state persistence.
5
+ * @type {HyperbookAudio}
6
+ * @memberof hyperbook
7
+ * @see hyperbook.store
8
+ */
1
9
  hyperbook.audio = (function () {
2
10
  /**
3
11
  * @param {number} seconds
@@ -23,37 +31,68 @@ hyperbook.audio = (function () {
23
31
  */
24
32
  const wavesurferInstances = {};
25
33
 
26
- function init() {
27
- const els = document.getElementsByClassName("wave");
34
+ function initElement(el) {
35
+ const src = el.getAttribute("data-src");
36
+ const id = el.id;
37
+
38
+ if (wavesurferInstances[id]) return;
39
+
40
+ const wavesurfer = window.WaveSurfer.create({
41
+ container: el,
42
+ cursorWidth: 4,
43
+ barWidth: 4,
44
+ barGap: 5,
45
+ barRadius: 2,
46
+ height: 64,
47
+ url: src,
48
+ });
49
+ wavesurfer.on("ready", () => update(id));
50
+ wavesurfer.on("audioprocess", () => update(id));
51
+ wavesurfer.on("pause", () => update(id));
52
+ wavesurfer.on("finish", () => update(id));
53
+ wavesurfer.on("play", () => update(id));
54
+ wavesurferInstances[id] = wavesurfer;
55
+
56
+ hyperbook.store.audio.get(id).then((result) => {
57
+ if (result) {
58
+ wavesurfer.setTime(result.time);
59
+ }
60
+ });
61
+ }
62
+
63
+ function init(root) {
64
+ const els = root.getElementsByClassName("wave");
28
65
  for (let el of els) {
29
- const src = el.getAttribute("data-src");
30
- const id = el.id;
31
-
32
- const wavesurfer = window.WaveSurfer.create({
33
- container: el,
34
- cursorWidth: 4,
35
- barWidth: 4,
36
- barGap: 5,
37
- barRadius: 2,
38
- height: 64,
39
- url: src,
40
- });
41
- wavesurfer.on("ready", () => update(id));
42
- wavesurfer.on("audioprocess", () => update(id));
43
- wavesurfer.on("pause", () => update(id));
44
- wavesurfer.on("finish", () => update(id));
45
- wavesurfer.on("play", () => update(id));
46
- wavesurferInstances[id] = wavesurfer;
47
-
48
- store.audio.get(id).then((result) => {
49
- if (result) {
50
- wavesurfer.setTime(result.time);
51
- }
52
- });
66
+ initElement(el);
53
67
  }
54
68
  }
55
69
 
56
- init();
70
+ // Initialize existing elements on document load
71
+ document.addEventListener("DOMContentLoaded", () => {
72
+ init(document);
73
+ });
74
+
75
+ // Observe for new elements added to the DOM
76
+ const observer = new MutationObserver((mutations) => {
77
+ mutations.forEach((mutation) => {
78
+ mutation.addedNodes.forEach((node) => {
79
+ if (node.nodeType === 1) {
80
+ if (node.classList.contains("wave")) {
81
+ initElement(node);
82
+ } else {
83
+ const waves = node.getElementsByClassName?.("wave");
84
+ if (waves) {
85
+ for (let el of waves) {
86
+ initElement(el);
87
+ }
88
+ }
89
+ }
90
+ }
91
+ });
92
+ });
93
+ });
94
+
95
+ observer.observe(document.body, { childList: true, subtree: true });
57
96
 
58
97
  /**
59
98
  * @param {string} id
@@ -78,7 +117,7 @@ hyperbook.audio = (function () {
78
117
  playEl.classList.remove("playing");
79
118
  }
80
119
 
81
- store.audio.put({ id, time });
120
+ hyperbook.store.audio.put({ id, time });
82
121
 
83
122
  durationEl.innerHTML = ` ${secondsToTimestamp(time)}/${secondsToTimestamp(duration)}`;
84
123
  }
@@ -1,6 +1,14 @@
1
+ /// <reference path="../hyperbook.types.js" />
2
+
3
+ /**
4
+ * Bookmark management and navigation.
5
+ * @type {HyperbookBookmarks}
6
+ * @memberof hyperbook
7
+ * @see hyperbook.store
8
+ */
1
9
  hyperbook.bookmarks = (function () {
2
10
  function update(root = document) {
3
- store.bookmarks
11
+ hyperbook.store.bookmarks
4
12
  .toArray()
5
13
  .then((bookmarks) => {
6
14
  return bookmarks.map((bookmark) => {
@@ -1,3 +1,11 @@
1
+ /// <reference path="../hyperbook.types.js" />
2
+
3
+ /**
4
+ * File download management.
5
+ * @type {HyperbookDownload}
6
+ * @memberof hyperbook
7
+ * @see hyperbook.i18n
8
+ */
1
9
  hyperbook.download = (function () {
2
10
  const init = (root) => {
3
11
  const els = root.getElementsByClassName("directive-download");
@@ -10,10 +18,10 @@ hyperbook.download = (function () {
10
18
  const isOnline = r.ok;
11
19
  if (isOnline) {
12
20
  labelEl.classList.remove("offline");
13
- labelEl.innerHTML.replace(`(${i18n.get("download-offline")})`, "");
21
+ labelEl.innerHTML.replace(`(${hyperbook.i18n.get("download-offline")})`, "");
14
22
  } else {
15
23
  labelEl.classList.add("offline");
16
- labelEl.innerHTML += ` (${i18n.get("download-offline")})`;
24
+ labelEl.innerHTML += ` (${hyperbook.i18n.get("download-offline")})`;
17
25
  }
18
26
  });
19
27
  }
@@ -1,6 +1,15 @@
1
+ /// <reference path="../hyperbook.types.js" />
2
+
1
3
  window.EXCALIDRAW_ASSET_PATH =
2
4
  window.HYPERBOOK_ASSETS + "directive-excalidraw/";
3
5
 
6
+ /**
7
+ * Excalidraw whiteboard integration.
8
+ * @type {HyperbookExcalidraw}
9
+ * @memberof hyperbook
10
+ */
4
11
  hyperbook.excalidraw = (function () {
5
12
  const elems = document.getElementsByClassName("directive-excalidraw");
13
+
14
+ return {};
6
15
  })();