@hyperbook/markdown 0.59.3 → 0.59.4
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.
- package/dist/assets/bootstrap.js +4 -4
- package/dist/assets/cloud.js +76 -28
- package/dist/assets/directive-abc-music/client.js +3 -3
- package/dist/assets/directive-audio/client.js +2 -2
- package/dist/assets/directive-bookmarks/client.js +1 -1
- package/dist/assets/directive-embed/client.js +2 -2
- package/dist/assets/directive-geogebra/client.js +2 -2
- package/dist/assets/directive-h5p/client.js +2 -2
- package/dist/assets/directive-learningmap/client.js +3 -3
- package/dist/assets/directive-multievent/multievent.js +2 -2
- package/dist/assets/directive-p5/client.js +3 -3
- package/dist/assets/directive-protect/client.js +3 -3
- package/dist/assets/directive-pyide/client.js +3 -3
- package/dist/assets/directive-slideshow/client.js +4 -4
- package/dist/assets/directive-struktolab/client.js +2 -2
- package/dist/assets/directive-tabs/client.js +2 -2
- package/dist/assets/directive-textinput/client.js +2 -2
- package/dist/assets/directive-webide/client.js +6 -6
- package/dist/assets/directive-youtube/client.js +2 -2
- package/dist/assets/hyperbook.types.js +0 -22
- package/dist/assets/store.js +2 -3
- package/dist/assets/ui.js +3 -3
- package/package.json +1 -1
package/dist/assets/bootstrap.js
CHANGED
|
@@ -30,9 +30,9 @@ hyperbook.bootstrap = (function () {
|
|
|
30
30
|
details.addEventListener("toggle", () => {
|
|
31
31
|
if (id) {
|
|
32
32
|
if (details.open) {
|
|
33
|
-
hyperbook.store.collapsibles.put({ id });
|
|
33
|
+
hyperbook.store.db.collapsibles.put({ id });
|
|
34
34
|
} else {
|
|
35
|
-
hyperbook.store.collapsibles.delete(id);
|
|
35
|
+
hyperbook.store.db.collapsibles.delete(id);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
// Sync all elements with the same ID
|
|
@@ -57,7 +57,7 @@ hyperbook.bootstrap = (function () {
|
|
|
57
57
|
* @param {HTMLElement} root
|
|
58
58
|
*/
|
|
59
59
|
const updateCollapsibles = (root) => {
|
|
60
|
-
hyperbook.store.collapsibles.toArray().then((collapsibles) => {
|
|
60
|
+
hyperbook.store.db.collapsibles.toArray().then((collapsibles) => {
|
|
61
61
|
const detailsEls = root.querySelectorAll("details.section, details.directive-collapsible");
|
|
62
62
|
for (let details of detailsEls) {
|
|
63
63
|
const id = details.getAttribute("data-id");
|
|
@@ -95,7 +95,7 @@ hyperbook.bootstrap = (function () {
|
|
|
95
95
|
const bookmarkEls = root.getElementsByClassName("bookmark");
|
|
96
96
|
for (let bookmarkEl of bookmarkEls) {
|
|
97
97
|
const key = bookmarkEl.getAttribute("data-key");
|
|
98
|
-
hyperbook.store.bookmarks.get(key).then((bookmark) => {
|
|
98
|
+
hyperbook.store.db.bookmarks.get(key).then((bookmark) => {
|
|
99
99
|
if (bookmark) {
|
|
100
100
|
bookmarkEl.classList.add("active");
|
|
101
101
|
}
|
package/dist/assets/cloud.js
CHANGED
|
@@ -15,6 +15,7 @@ hyperbook.cloud = (function () {
|
|
|
15
15
|
const AUTH_USER_KEY = "hyperbook_auth_user";
|
|
16
16
|
const LAST_EVENT_ID_KEY = "hyperbook_last_event_id";
|
|
17
17
|
const EVENT_BATCH_MAX_SIZE = 512 * 1024; // 512KB
|
|
18
|
+
const OFFLINE_QUEUE_MAX_SIZE = 100; // FIX: cap offline queue to avoid unbounded memory growth
|
|
18
19
|
let isLoadingFromCloud = false;
|
|
19
20
|
let syncManager = null;
|
|
20
21
|
|
|
@@ -122,17 +123,27 @@ hyperbook.cloud = (function () {
|
|
|
122
123
|
this.updateUI("saving");
|
|
123
124
|
|
|
124
125
|
try {
|
|
125
|
-
//
|
|
126
|
+
// Snapshot exactly which events we're attempting to send,
|
|
127
|
+
// so that any events added during the async save are not lost.
|
|
128
|
+
// FIX: was `this.pendingEvents = []` after save, which would silently
|
|
129
|
+
// discard events that arrived between the slice() and the clear.
|
|
126
130
|
const eventsToSend = this.pendingEvents.slice();
|
|
127
131
|
const serialized = JSON.stringify(eventsToSend);
|
|
128
132
|
|
|
129
133
|
if (!this.isOnline) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
timestamp: Date.now()
|
|
134
|
-
}
|
|
135
|
-
|
|
134
|
+
// FIX: enforce offline queue size cap to prevent unbounded memory growth
|
|
135
|
+
if (this.offlineQueue.length >= OFFLINE_QUEUE_MAX_SIZE) {
|
|
136
|
+
console.warn("Offline queue full — falling back to snapshot on reconnect");
|
|
137
|
+
this.offlineQueue = [{ snapshot: true, timestamp: Date.now() }];
|
|
138
|
+
} else {
|
|
139
|
+
this.offlineQueue.push({
|
|
140
|
+
events: eventsToSend,
|
|
141
|
+
afterEventId: this.lastEventId,
|
|
142
|
+
timestamp: Date.now(),
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
// FIX: only clear the events we snapshotted
|
|
146
|
+
this.pendingEvents = this.pendingEvents.slice(eventsToSend.length);
|
|
136
147
|
this.updateUI("offline-queued");
|
|
137
148
|
return;
|
|
138
149
|
}
|
|
@@ -151,12 +162,16 @@ hyperbook.cloud = (function () {
|
|
|
151
162
|
// 409 — stale state, re-fetch
|
|
152
163
|
console.log("⚠ Stale state detected, re-fetching from cloud...");
|
|
153
164
|
await loadFromCloud();
|
|
154
|
-
|
|
165
|
+
// FIX: only discard the events we tried to send, not any that
|
|
166
|
+
// arrived concurrently during the async round-trip
|
|
167
|
+
this.pendingEvents = this.pendingEvents.slice(eventsToSend.length);
|
|
155
168
|
window.location.reload();
|
|
156
169
|
return;
|
|
157
170
|
}
|
|
158
171
|
|
|
159
|
-
|
|
172
|
+
// FIX: only discard the events we snapshotted, preserving any
|
|
173
|
+
// events that were added while the network request was in flight
|
|
174
|
+
this.pendingEvents = this.pendingEvents.slice(eventsToSend.length);
|
|
160
175
|
this.lastEventId = result.lastEventId;
|
|
161
176
|
localStorage.setItem(
|
|
162
177
|
LAST_EVENT_ID_KEY,
|
|
@@ -176,8 +191,13 @@ hyperbook.cloud = (function () {
|
|
|
176
191
|
});
|
|
177
192
|
}
|
|
178
193
|
|
|
179
|
-
|
|
180
|
-
|
|
194
|
+
// FIX: removed the redundant `afterEventId` parameter. It was never passed
|
|
195
|
+
// explicitly by `performSave`, so the default always took effect, making the
|
|
196
|
+
// parameter misleading. `processOfflineQueue` now passes it via options instead.
|
|
197
|
+
async sendEvents(events, options = {}) {
|
|
198
|
+
const afterEventId = options.afterEventId !== undefined
|
|
199
|
+
? options.afterEventId
|
|
200
|
+
: this.lastEventId;
|
|
181
201
|
try {
|
|
182
202
|
const data = await apiRequest(
|
|
183
203
|
`/api/store/${HYPERBOOK_CLOUD.id}/events`,
|
|
@@ -185,7 +205,7 @@ hyperbook.cloud = (function () {
|
|
|
185
205
|
method: "POST",
|
|
186
206
|
body: JSON.stringify({
|
|
187
207
|
events: events,
|
|
188
|
-
afterEventId:
|
|
208
|
+
afterEventId: afterEventId,
|
|
189
209
|
}),
|
|
190
210
|
},
|
|
191
211
|
);
|
|
@@ -264,11 +284,28 @@ hyperbook.cloud = (function () {
|
|
|
264
284
|
|
|
265
285
|
console.log(`Processing ${this.offlineQueue.length} queued saves...`);
|
|
266
286
|
|
|
287
|
+
// FIX: if the queue was compacted to a snapshot sentinel, send a full
|
|
288
|
+
// snapshot instead of trying to replay individual events
|
|
289
|
+
if (this.offlineQueue.length === 1 && this.offlineQueue[0].snapshot) {
|
|
290
|
+
this.offlineQueue = [];
|
|
291
|
+
try {
|
|
292
|
+
const result = await this.sendSnapshot();
|
|
293
|
+
this.lastEventId = result.lastEventId;
|
|
294
|
+
localStorage.setItem(LAST_EVENT_ID_KEY, String(this.lastEventId));
|
|
295
|
+
this.lastSaveTime = Date.now();
|
|
296
|
+
console.log("✓ Offline snapshot flushed");
|
|
297
|
+
} catch (error) {
|
|
298
|
+
console.error("Failed to flush offline snapshot:", error);
|
|
299
|
+
}
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
|
|
267
303
|
// Send queued events in order
|
|
268
304
|
for (let i = 0; i < this.offlineQueue.length; i++) {
|
|
269
305
|
const queued = this.offlineQueue[i];
|
|
270
306
|
try {
|
|
271
|
-
|
|
307
|
+
// FIX: use named options object to match the updated sendEvents signature
|
|
308
|
+
const result = await this.sendEvents(queued.events, { afterEventId: queued.afterEventId });
|
|
272
309
|
|
|
273
310
|
if (result.conflict) {
|
|
274
311
|
// Conflict — discard remaining queue, re-fetch
|
|
@@ -317,24 +354,27 @@ hyperbook.cloud = (function () {
|
|
|
317
354
|
});
|
|
318
355
|
}
|
|
319
356
|
|
|
357
|
+
// FIX: manualSave's no-pending-events path now runs inside saveMutex,
|
|
358
|
+
// preventing a concurrent performSave from racing with sendSnapshot
|
|
320
359
|
async manualSave() {
|
|
321
360
|
if (this.pendingEvents.length === 0) {
|
|
322
361
|
// No pending events — send full snapshot
|
|
323
362
|
this.clearTimers();
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
363
|
+
return this.saveMutex.runExclusive(async () => {
|
|
364
|
+
try {
|
|
365
|
+
this.updateUI("saving");
|
|
366
|
+
const result = await this.sendSnapshot();
|
|
367
|
+
this.lastEventId = result.lastEventId;
|
|
368
|
+
localStorage.setItem(
|
|
369
|
+
LAST_EVENT_ID_KEY,
|
|
370
|
+
String(this.lastEventId),
|
|
371
|
+
);
|
|
372
|
+
this.updateUI("saved");
|
|
373
|
+
} catch (error) {
|
|
374
|
+
console.error("Manual save failed:", error);
|
|
375
|
+
this.updateUI("error");
|
|
376
|
+
}
|
|
377
|
+
});
|
|
338
378
|
}
|
|
339
379
|
this.clearTimers();
|
|
340
380
|
await this.performSave("manual");
|
|
@@ -599,8 +639,12 @@ hyperbook.cloud = (function () {
|
|
|
599
639
|
minSaveInterval: 1000,
|
|
600
640
|
});
|
|
601
641
|
|
|
642
|
+
// Suppress events from directive initialization (e.g., restoring collapsible
|
|
643
|
+
// state triggers Dexie writes via toggle events after hooks are registered).
|
|
644
|
+
isLoadingFromCloud = true;
|
|
645
|
+
|
|
602
646
|
// Hook Dexie tables to capture granular events (skip currentState — ephemeral UI data)
|
|
603
|
-
hyperbook.store.tables.forEach((table) => {
|
|
647
|
+
hyperbook.store.db.tables.forEach((table) => {
|
|
604
648
|
if (table.name === "currentState") return;
|
|
605
649
|
|
|
606
650
|
table.hook("creating", function (primKey, obj) {
|
|
@@ -630,6 +674,10 @@ hyperbook.cloud = (function () {
|
|
|
630
674
|
});
|
|
631
675
|
});
|
|
632
676
|
});
|
|
677
|
+
|
|
678
|
+
// Allow directive initialization to settle before accepting sync events
|
|
679
|
+
await new Promise((r) => requestAnimationFrame(() => setTimeout(r, 0)));
|
|
680
|
+
isLoadingFromCloud = false;
|
|
633
681
|
}
|
|
634
682
|
}
|
|
635
683
|
|
|
@@ -41,7 +41,7 @@ hyperbook.abc = (function () {
|
|
|
41
41
|
});
|
|
42
42
|
|
|
43
43
|
resetEl?.addEventListener("click", () => {
|
|
44
|
-
hyperbook.store.abcMusic.delete(id);
|
|
44
|
+
hyperbook.store.db.abcMusic.delete(id);
|
|
45
45
|
window.location.reload();
|
|
46
46
|
});
|
|
47
47
|
|
|
@@ -54,7 +54,7 @@ hyperbook.abc = (function () {
|
|
|
54
54
|
});
|
|
55
55
|
|
|
56
56
|
editorEl.addEventListener("code-input_load", async () => {
|
|
57
|
-
const storeResult = await hyperbook.store.abcMusic
|
|
57
|
+
const storeResult = await hyperbook.store.db.abcMusic
|
|
58
58
|
.where("id")
|
|
59
59
|
.equals(editorEl.id)
|
|
60
60
|
.first();
|
|
@@ -78,7 +78,7 @@ hyperbook.abc = (function () {
|
|
|
78
78
|
});
|
|
79
79
|
|
|
80
80
|
editorEl.addEventListener("change", () => {
|
|
81
|
-
hyperbook.store.abcMusic.put({
|
|
81
|
+
hyperbook.store.db.abcMusic.put({
|
|
82
82
|
id: editorEl.id,
|
|
83
83
|
tune: editorEl.value,
|
|
84
84
|
});
|
|
@@ -53,7 +53,7 @@ hyperbook.audio = (function () {
|
|
|
53
53
|
wavesurfer.on("play", () => update(id));
|
|
54
54
|
wavesurferInstances[id] = wavesurfer;
|
|
55
55
|
|
|
56
|
-
hyperbook.store.audio.get(id).then((result) => {
|
|
56
|
+
hyperbook.store.db.audio.get(id).then((result) => {
|
|
57
57
|
if (result) {
|
|
58
58
|
wavesurfer.setTime(result.time);
|
|
59
59
|
}
|
|
@@ -117,7 +117,7 @@ hyperbook.audio = (function () {
|
|
|
117
117
|
playEl.classList.remove("playing");
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
hyperbook.store.audio.put({ id, time });
|
|
120
|
+
hyperbook.store.db.audio.put({ id, time });
|
|
121
121
|
|
|
122
122
|
durationEl.innerHTML = ` ${secondsToTimestamp(time)}/${secondsToTimestamp(duration)}`;
|
|
123
123
|
}
|
|
@@ -14,7 +14,7 @@ hyperbook.embed.consent = (function () {
|
|
|
14
14
|
|
|
15
15
|
async function isAllowed(consentId) {
|
|
16
16
|
try {
|
|
17
|
-
var entry = await hyperbook.store.consent.get(consentId);
|
|
17
|
+
var entry = await hyperbook.store.db.consent.get(consentId);
|
|
18
18
|
return entry?.allowed === true;
|
|
19
19
|
} catch (e) {
|
|
20
20
|
return false;
|
|
@@ -22,7 +22,7 @@ hyperbook.embed.consent = (function () {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
async function allow(consentId) {
|
|
25
|
-
await hyperbook.store.consent.put({ id: consentId, allowed: true });
|
|
25
|
+
await hyperbook.store.db.consent.put({ id: consentId, allowed: true });
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
function loadContent(wrapper) {
|
|
@@ -12,14 +12,14 @@ hyperbook.geogebra = (function () {
|
|
|
12
12
|
for (const el of els) {
|
|
13
13
|
const id = el.getAttribute("data-id");
|
|
14
14
|
|
|
15
|
-
const result = await hyperbook.store.geogebra.get(id);
|
|
15
|
+
const result = await hyperbook.store.db.geogebra.get(id);
|
|
16
16
|
if (result && result.state) {
|
|
17
17
|
el.setBase64(result.state);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
el.registerUpdateListener(() => {
|
|
21
21
|
el.getBase64((b) => {
|
|
22
|
-
hyperbook.store.geogebra.put({ id, state: b });
|
|
22
|
+
hyperbook.store.db.geogebra.put({ id, state: b });
|
|
23
23
|
});
|
|
24
24
|
});
|
|
25
25
|
}
|
|
@@ -15,7 +15,7 @@ hyperbook.h5p = (function () {
|
|
|
15
15
|
const save = (id) =>
|
|
16
16
|
H5P.getUserData(id, "state", (error, userData) => {
|
|
17
17
|
if (!error) {
|
|
18
|
-
hyperbook.store.h5p.put({ id, userData });
|
|
18
|
+
hyperbook.store.db.h5p.put({ id, userData });
|
|
19
19
|
}
|
|
20
20
|
});
|
|
21
21
|
|
|
@@ -41,7 +41,7 @@ hyperbook.h5p = (function () {
|
|
|
41
41
|
const src = el.getAttribute("data-src");
|
|
42
42
|
const id = el.getAttribute("data-id");
|
|
43
43
|
if (h5pFrame && src) {
|
|
44
|
-
const result = await hyperbook.store.h5p.get(id);
|
|
44
|
+
const result = await hyperbook.store.db.h5p.get(id);
|
|
45
45
|
const h5pOptions = {
|
|
46
46
|
...h5pBaseOptions,
|
|
47
47
|
id,
|
|
@@ -13,12 +13,12 @@ hyperbook.learningmap = (function () {
|
|
|
13
13
|
for (let elem of elems) {
|
|
14
14
|
const map = elem.getElementsByTagName("hyperbook-learningmap")[0];
|
|
15
15
|
if (map) {
|
|
16
|
-
const result = await hyperbook.store.learningmap.get(elem.id);
|
|
16
|
+
const result = await hyperbook.store.db.learningmap.get(elem.id);
|
|
17
17
|
if (result) {
|
|
18
18
|
map.initialState = result;
|
|
19
19
|
}
|
|
20
20
|
map.addEventListener("change", function (event) {
|
|
21
|
-
hyperbook.store.learningmap
|
|
21
|
+
hyperbook.store.db.learningmap
|
|
22
22
|
.update(elem.id, {
|
|
23
23
|
id: elem.id,
|
|
24
24
|
nodes: event.detail.nodes,
|
|
@@ -28,7 +28,7 @@ hyperbook.learningmap = (function () {
|
|
|
28
28
|
})
|
|
29
29
|
.then((updated) => {
|
|
30
30
|
if (updated == 0) {
|
|
31
|
-
hyperbook.store.learningmap.put({
|
|
31
|
+
hyperbook.store.db.learningmap.put({
|
|
32
32
|
id: elem.id,
|
|
33
33
|
nodes: event.detail.nodes,
|
|
34
34
|
x: event.detail.x,
|
|
@@ -204,14 +204,14 @@ var multievent = {
|
|
|
204
204
|
});
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
-
await hyperbook.store.multievent.put({
|
|
207
|
+
await hyperbook.store.db.multievent.put({
|
|
208
208
|
id: "multievent_" + clNr + "_" + window.location.pathname,
|
|
209
209
|
state: JSON.stringify(state)
|
|
210
210
|
});
|
|
211
211
|
},
|
|
212
212
|
loadState: async function (clNr) {
|
|
213
213
|
try {
|
|
214
|
-
var record = await hyperbook.store.multievent.get("multievent_" + clNr + "_" + window.location.pathname);
|
|
214
|
+
var record = await hyperbook.store.db.multievent.get("multievent_" + clNr + "_" + window.location.pathname);
|
|
215
215
|
if (!record) return false;
|
|
216
216
|
|
|
217
217
|
var state = JSON.parse(record.state);
|
|
@@ -54,7 +54,7 @@ hyperbook.p5 = (function () {
|
|
|
54
54
|
});
|
|
55
55
|
|
|
56
56
|
resetEl?.addEventListener("click", () => {
|
|
57
|
-
hyperbook.store.p5.delete(id);
|
|
57
|
+
hyperbook.store.db.p5.delete(id);
|
|
58
58
|
window.location.reload();
|
|
59
59
|
});
|
|
60
60
|
|
|
@@ -68,7 +68,7 @@ hyperbook.p5 = (function () {
|
|
|
68
68
|
|
|
69
69
|
editor?.addEventListener("code-input_load", async () => {
|
|
70
70
|
if (id) {
|
|
71
|
-
const result = await hyperbook.store.p5.get(id);
|
|
71
|
+
const result = await hyperbook.store.db.p5.get(id);
|
|
72
72
|
if (result) {
|
|
73
73
|
editor.value = result.sketch;
|
|
74
74
|
const code = result.sketch;
|
|
@@ -79,7 +79,7 @@ hyperbook.p5 = (function () {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
editor.addEventListener("input", () => {
|
|
82
|
-
hyperbook.store.p5.put({ id, sketch: editor.value });
|
|
82
|
+
hyperbook.store.db.p5.put({ id, sketch: editor.value });
|
|
83
83
|
});
|
|
84
84
|
}
|
|
85
85
|
|
|
@@ -15,7 +15,7 @@ hyperbook.protect = (function () {
|
|
|
15
15
|
const value = inputEl.value;
|
|
16
16
|
const id = el.getAttribute("data-id");
|
|
17
17
|
|
|
18
|
-
hyperbook.store.protect.put({ id, passwordHash: btoa(value) }).then(() => {
|
|
18
|
+
hyperbook.store.db.protect.put({ id, passwordHash: btoa(value) }).then(() => {
|
|
19
19
|
const els = document.querySelectorAll(
|
|
20
20
|
`input[data-id="${el.getAttribute("data-id")}"]`
|
|
21
21
|
);
|
|
@@ -30,7 +30,7 @@ hyperbook.protect = (function () {
|
|
|
30
30
|
*/
|
|
31
31
|
function onUpdateToast(inputEl, el, hiddenEl) {
|
|
32
32
|
const id = el.getAttribute("data-id");
|
|
33
|
-
hyperbook.store.protect.get(id).then((result) => {
|
|
33
|
+
hyperbook.store.db.protect.get(id).then((result) => {
|
|
34
34
|
if (result) {
|
|
35
35
|
const value = atob(result.passwordHash);
|
|
36
36
|
inputEl.value = value;
|
|
@@ -58,7 +58,7 @@ hyperbook.protect = (function () {
|
|
|
58
58
|
const id = el.getAttribute("data-id");
|
|
59
59
|
|
|
60
60
|
if (!inputEl) continue;
|
|
61
|
-
hyperbook.store.protect.get(id).then((result) => {
|
|
61
|
+
hyperbook.store.db.protect.get(id).then((result) => {
|
|
62
62
|
if (result) {
|
|
63
63
|
inputEl.value = atob(result.passwordHash);
|
|
64
64
|
} else {
|
|
@@ -170,7 +170,7 @@ hyperbook.python = (function () {
|
|
|
170
170
|
});
|
|
171
171
|
|
|
172
172
|
resetEl?.addEventListener("click", () => {
|
|
173
|
-
hyperbook.store.pyide.delete(id);
|
|
173
|
+
hyperbook.store.db.pyide.delete(id);
|
|
174
174
|
window.location.reload();
|
|
175
175
|
});
|
|
176
176
|
|
|
@@ -203,14 +203,14 @@ hyperbook.python = (function () {
|
|
|
203
203
|
inputBtn?.addEventListener("click", showInput);
|
|
204
204
|
|
|
205
205
|
editor.addEventListener("code-input_load", async () => {
|
|
206
|
-
const result = await hyperbook.store.pyide.get(id);
|
|
206
|
+
const result = await hyperbook.store.db.pyide.get(id);
|
|
207
207
|
if (result) {
|
|
208
208
|
editor.value = result.script;
|
|
209
209
|
}
|
|
210
210
|
});
|
|
211
211
|
|
|
212
212
|
editor.addEventListener("input", () => {
|
|
213
|
-
hyperbook.store.pyide.put({ id, script: editor.value });
|
|
213
|
+
hyperbook.store.db.pyide.put({ id, script: editor.value });
|
|
214
214
|
});
|
|
215
215
|
|
|
216
216
|
test?.addEventListener("click", async () => {
|
|
@@ -11,7 +11,7 @@ hyperbook.slideshow = (function () {
|
|
|
11
11
|
* @param {string} id
|
|
12
12
|
*/
|
|
13
13
|
const update = (id) => {
|
|
14
|
-
hyperbook.store.slideshow.get(id).then((result) => {
|
|
14
|
+
hyperbook.store.db.slideshow.get(id).then((result) => {
|
|
15
15
|
const active = result?.active ?? 0;
|
|
16
16
|
const dots = document.querySelectorAll(`.dot[data-id="${id}"]`);
|
|
17
17
|
const images = document.querySelectorAll(
|
|
@@ -38,11 +38,11 @@ hyperbook.slideshow = (function () {
|
|
|
38
38
|
*/
|
|
39
39
|
const moveBy = (id, steps) => {
|
|
40
40
|
const images = document.querySelectorAll(`.dot[data-id="${id}"]`);
|
|
41
|
-
hyperbook.store.slideshow.get(id).then((result) => {
|
|
41
|
+
hyperbook.store.db.slideshow.get(id).then((result) => {
|
|
42
42
|
let active = result?.active ?? 0;
|
|
43
43
|
active += steps;
|
|
44
44
|
active = ((active % images.length) + images.length) % images.length;
|
|
45
|
-
hyperbook.store.slideshow.put({ id, active }).then(() => {
|
|
45
|
+
hyperbook.store.db.slideshow.put({ id, active }).then(() => {
|
|
46
46
|
window.hyperbook.slideshow.update(id);
|
|
47
47
|
});
|
|
48
48
|
});
|
|
@@ -55,7 +55,7 @@ hyperbook.slideshow = (function () {
|
|
|
55
55
|
const setActive = (id, index) => {
|
|
56
56
|
const images = document.querySelectorAll(`.dot[data-id="${id}"]`);
|
|
57
57
|
if (index >= 0 && index < images.length) {
|
|
58
|
-
hyperbook.store.slideshow.put({ id, active: index }).then(() => {
|
|
58
|
+
hyperbook.store.db.slideshow.put({ id, active: index }).then(() => {
|
|
59
59
|
window.hyperbook.slideshow.update(id);
|
|
60
60
|
});
|
|
61
61
|
}
|
|
@@ -13,7 +13,7 @@ hyperbook.struktolab = (function () {
|
|
|
13
13
|
const editorId = editor.getAttribute("data-id");
|
|
14
14
|
|
|
15
15
|
// Load saved content for this editor
|
|
16
|
-
hyperbook.store.struktolab.get(editorId).then((result) => {
|
|
16
|
+
hyperbook.store.db.struktolab.get(editorId).then((result) => {
|
|
17
17
|
if (result) {
|
|
18
18
|
editor.loadJSON(JSON.parse(result.tree));
|
|
19
19
|
}
|
|
@@ -21,7 +21,7 @@ hyperbook.struktolab = (function () {
|
|
|
21
21
|
|
|
22
22
|
// Listen for changes in the editor
|
|
23
23
|
editor.addEventListener("change", (e) => {
|
|
24
|
-
hyperbook.store.struktolab.put({
|
|
24
|
+
hyperbook.store.db.struktolab.put({
|
|
25
25
|
id: editorId,
|
|
26
26
|
tree: JSON.stringify(e.detail.tree),
|
|
27
27
|
});
|
|
@@ -17,7 +17,7 @@ hyperbook.tabs = (function () {
|
|
|
17
17
|
// Listen for changes on radio inputs
|
|
18
18
|
input.addEventListener("change", () => {
|
|
19
19
|
if (input.checked) {
|
|
20
|
-
hyperbook.store.tabs.put({
|
|
20
|
+
hyperbook.store.db.tabs.put({
|
|
21
21
|
id: tabsId,
|
|
22
22
|
active: tabId,
|
|
23
23
|
});
|
|
@@ -29,7 +29,7 @@ hyperbook.tabs = (function () {
|
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
// Restore saved tab selections
|
|
32
|
-
hyperbook.store.tabs.each((result) => {
|
|
32
|
+
hyperbook.store.db.tabs.each((result) => {
|
|
33
33
|
selectTab(result.id, result.active);
|
|
34
34
|
});
|
|
35
35
|
};
|
|
@@ -27,7 +27,7 @@ hyperbook.textinput = (function () {
|
|
|
27
27
|
const id = textarea.getAttribute("data-id");
|
|
28
28
|
|
|
29
29
|
// Load saved text from store
|
|
30
|
-
hyperbook.store.textinput.get(id).then((result) => {
|
|
30
|
+
hyperbook.store.db.textinput.get(id).then((result) => {
|
|
31
31
|
if (result && result.text) {
|
|
32
32
|
textarea.value = result.text;
|
|
33
33
|
}
|
|
@@ -37,7 +37,7 @@ hyperbook.textinput = (function () {
|
|
|
37
37
|
|
|
38
38
|
// Save text to store on input with debouncing
|
|
39
39
|
const saveToStore = debounce(() => {
|
|
40
|
-
hyperbook.store.textinput.put({
|
|
40
|
+
hyperbook.store.db.textinput.put({
|
|
41
41
|
id: id,
|
|
42
42
|
text: textarea.value,
|
|
43
43
|
}).catch((error) => {
|
|
@@ -41,7 +41,7 @@ hyperbook.webide = (function () {
|
|
|
41
41
|
|
|
42
42
|
resetEl?.addEventListener("click", () => {
|
|
43
43
|
if (window.confirm(hyperbook.i18n.get("webide-reset-prompt"))) {
|
|
44
|
-
hyperbook.store.webide.delete(id);
|
|
44
|
+
hyperbook.store.db.webide.delete(id);
|
|
45
45
|
window.location.reload();
|
|
46
46
|
}
|
|
47
47
|
});
|
|
@@ -77,7 +77,7 @@ hyperbook.webide = (function () {
|
|
|
77
77
|
});
|
|
78
78
|
|
|
79
79
|
const load = async () => {
|
|
80
|
-
const result = await hyperbook.store.webide.get(id);
|
|
80
|
+
const result = await hyperbook.store.db.webide.get(id);
|
|
81
81
|
if (!result) {
|
|
82
82
|
return;
|
|
83
83
|
}
|
|
@@ -91,7 +91,7 @@ hyperbook.webide = (function () {
|
|
|
91
91
|
load();
|
|
92
92
|
|
|
93
93
|
const update = () => {
|
|
94
|
-
hyperbook.store.webide.put({
|
|
94
|
+
hyperbook.store.db.webide.put({
|
|
95
95
|
id,
|
|
96
96
|
html: editorHTML?.value,
|
|
97
97
|
css: editorCSS?.value,
|
|
@@ -109,7 +109,7 @@ hyperbook.webide = (function () {
|
|
|
109
109
|
});
|
|
110
110
|
|
|
111
111
|
editorHTML?.addEventListener("code-input_load", async () => {
|
|
112
|
-
const result = await hyperbook.store.webide.get(id);
|
|
112
|
+
const result = await hyperbook.store.db.webide.get(id);
|
|
113
113
|
if (result) {
|
|
114
114
|
editorHTML.value = result.html;
|
|
115
115
|
}
|
|
@@ -122,7 +122,7 @@ hyperbook.webide = (function () {
|
|
|
122
122
|
});
|
|
123
123
|
|
|
124
124
|
editorCSS?.addEventListener("code-input_load", async () => {
|
|
125
|
-
const result = await hyperbook.store.webide.get(id);
|
|
125
|
+
const result = await hyperbook.store.db.webide.get(id);
|
|
126
126
|
if (result) {
|
|
127
127
|
editorCSS.value = result.css;
|
|
128
128
|
}
|
|
@@ -135,7 +135,7 @@ hyperbook.webide = (function () {
|
|
|
135
135
|
});
|
|
136
136
|
|
|
137
137
|
editorJS?.addEventListener("code-input_load", async () => {
|
|
138
|
-
const result = await hyperbook.store.webide.get(id);
|
|
138
|
+
const result = await hyperbook.store.db.webide.get(id);
|
|
139
139
|
if (result) {
|
|
140
140
|
editorJS.value = result.js;
|
|
141
141
|
}
|
|
@@ -8,7 +8,7 @@ hyperbook.youtube.consent = (function () {
|
|
|
8
8
|
|
|
9
9
|
async function isAllowed() {
|
|
10
10
|
try {
|
|
11
|
-
var entry = await hyperbook.store.consent.get(CONSENT_ID);
|
|
11
|
+
var entry = await hyperbook.store.db.consent.get(CONSENT_ID);
|
|
12
12
|
return entry?.allowed === true;
|
|
13
13
|
} catch (e) {
|
|
14
14
|
return false;
|
|
@@ -16,7 +16,7 @@ hyperbook.youtube.consent = (function () {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
async function allow() {
|
|
19
|
-
await hyperbook.store.consent.put({ id: CONSENT_ID, allowed: true });
|
|
19
|
+
await hyperbook.store.db.consent.put({ id: CONSENT_ID, allowed: true });
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
function loadContent(wrapper) {
|
|
@@ -26,28 +26,6 @@
|
|
|
26
26
|
/**
|
|
27
27
|
* @typedef {Object} HyperbookStore
|
|
28
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
29
|
* @property {() => Promise<void>} export - Export all store data as a JSON download.
|
|
52
30
|
* @property {() => Promise<void>} reset - Clear all store data after user confirmation.
|
|
53
31
|
* @property {() => Promise<void>} import - Import store data from a JSON file.
|
package/dist/assets/store.js
CHANGED
|
@@ -192,11 +192,10 @@ hyperbook.store = (function () {
|
|
|
192
192
|
input.click();
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
-
|
|
196
|
-
return Object.assign(db, {
|
|
195
|
+
return {
|
|
197
196
|
db,
|
|
198
197
|
export: hyperbookExport,
|
|
199
198
|
reset: hyperbookReset,
|
|
200
199
|
import: hyperbookImport,
|
|
201
|
-
}
|
|
200
|
+
};
|
|
202
201
|
})();
|
package/dist/assets/ui.js
CHANGED
|
@@ -55,14 +55,14 @@ hyperbook.ui = (function () {
|
|
|
55
55
|
*/
|
|
56
56
|
function toggleBookmark(key, label) {
|
|
57
57
|
const el = document.querySelectorAll(`.bookmark[data-key="${key}"]`);
|
|
58
|
-
hyperbook.store.bookmarks.get(key).then((bookmark) => {
|
|
58
|
+
hyperbook.store.db.bookmarks.get(key).then((bookmark) => {
|
|
59
59
|
if (!bookmark) {
|
|
60
|
-
hyperbook.store.bookmarks.add({ path: key, label }).then(() => {
|
|
60
|
+
hyperbook.store.db.bookmarks.add({ path: key, label }).then(() => {
|
|
61
61
|
el.forEach((e) => e.classList.add("active"));
|
|
62
62
|
hyperbook.bookmarks.update();
|
|
63
63
|
});
|
|
64
64
|
} else {
|
|
65
|
-
hyperbook.store.bookmarks.delete(key).then(() => {
|
|
65
|
+
hyperbook.store.db.bookmarks.delete(key).then(() => {
|
|
66
66
|
el.forEach((e) => e.classList.remove("active"));
|
|
67
67
|
hyperbook.bookmarks.update();
|
|
68
68
|
});
|