hyperbook 0.93.1 → 0.95.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.
- package/dist/assets/codemirror/codemirror.bundle.js +26 -0
- package/dist/assets/directive-abc-music/client.js +42 -42
- package/dist/assets/directive-abc-music/style.css +6 -0
- package/dist/assets/directive-openscad/client.js +782 -444
- package/dist/assets/directive-openscad/style.css +71 -4
- package/dist/assets/directive-openscad/worker.js +632 -0
- package/dist/assets/directive-p5/client.js +27 -26
- package/dist/assets/directive-p5/style.css +12 -6
- package/dist/assets/directive-pyide/client.js +20 -32
- package/dist/assets/directive-pyide/style.css +12 -6
- package/dist/assets/directive-typst/client.js +20 -55
- package/dist/assets/directive-typst/style.css +12 -6
- package/dist/assets/directive-webide/client.js +45 -79
- package/dist/assets/directive-webide/style.css +12 -6
- package/dist/index.js +41 -71
- package/dist/locales/de.json +3 -0
- package/dist/locales/en.json +3 -0
- package/package.json +2 -2
- package/dist/assets/code-input/auto-close-brackets.min.js +0 -1
- package/dist/assets/code-input/code-input.min.css +0 -1
- package/dist/assets/code-input/code-input.min.js +0 -12
- package/dist/assets/code-input/indent.min.js +0 -1
- package/dist/assets/directive-openscad/STLLoader.js +0 -411
|
@@ -7,14 +7,6 @@
|
|
|
7
7
|
* @see hyperbook.store
|
|
8
8
|
*/
|
|
9
9
|
hyperbook.p5 = (function () {
|
|
10
|
-
window.codeInput?.registerTemplate(
|
|
11
|
-
"p5-highlighted",
|
|
12
|
-
codeInput.templates.prism(window.Prism, [
|
|
13
|
-
new codeInput.plugins.AutoCloseBrackets(),
|
|
14
|
-
new codeInput.plugins.Indent(true, 2),
|
|
15
|
-
]),
|
|
16
|
-
);
|
|
17
|
-
|
|
18
10
|
const wrapSketch = (sketchCode) => {
|
|
19
11
|
if (sketchCode !== "" && !sketchCode?.includes("setup")) {
|
|
20
12
|
return `
|
|
@@ -128,7 +120,7 @@ hyperbook.p5 = (function () {
|
|
|
128
120
|
const container = elem.querySelector(".container");
|
|
129
121
|
const editorContainer = elem.querySelector(".editor-container");
|
|
130
122
|
const splitter = elem.querySelector(".splitter");
|
|
131
|
-
const
|
|
123
|
+
const editorDiv = elem.getElementsByClassName("editor")[0];
|
|
132
124
|
/** @type {HTMLButtonElement} */
|
|
133
125
|
const update = elem.getElementsByClassName("update")[0];
|
|
134
126
|
const frame = elem.getElementsByTagName("iframe")[0];
|
|
@@ -139,6 +131,20 @@ hyperbook.p5 = (function () {
|
|
|
139
131
|
const downloadEl = elem.getElementsByClassName("download")[0];
|
|
140
132
|
const fullscreenEl = elem.getElementsByClassName("fullscreen")[0];
|
|
141
133
|
|
|
134
|
+
// Initialize CodeMirror editor (only in editor mode)
|
|
135
|
+
let cm = null;
|
|
136
|
+
if (editorDiv) {
|
|
137
|
+
const initialSource = editorDiv.textContent;
|
|
138
|
+
editorDiv.textContent = "";
|
|
139
|
+
cm = HyperbookCM.create(editorDiv, {
|
|
140
|
+
lang: editorDiv.dataset.lang,
|
|
141
|
+
value: initialSource,
|
|
142
|
+
onChange: (code) => {
|
|
143
|
+
if (id) hyperbook.store.db.p5.put({ id, sketch: code });
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
142
148
|
setupSplitter(elem, container, editorContainer, splitter);
|
|
143
149
|
|
|
144
150
|
fullscreenEl?.addEventListener("click", async () => {
|
|
@@ -159,7 +165,7 @@ hyperbook.p5 = (function () {
|
|
|
159
165
|
|
|
160
166
|
copyEl?.addEventListener("click", async () => {
|
|
161
167
|
try {
|
|
162
|
-
await navigator.clipboard.writeText(
|
|
168
|
+
await navigator.clipboard.writeText(cm?.getValue() ?? "");
|
|
163
169
|
} catch (error) {
|
|
164
170
|
console.error(error.message);
|
|
165
171
|
}
|
|
@@ -172,35 +178,30 @@ hyperbook.p5 = (function () {
|
|
|
172
178
|
|
|
173
179
|
downloadEl?.addEventListener("click", () => {
|
|
174
180
|
const a = document.createElement("a");
|
|
175
|
-
const blob = new Blob([
|
|
181
|
+
const blob = new Blob([cm?.getValue() ?? ""], { type: "text/plain" });
|
|
176
182
|
a.href = URL.createObjectURL(blob);
|
|
177
183
|
a.download = `sketch-${id}.js`;
|
|
178
184
|
a.click();
|
|
179
185
|
});
|
|
180
186
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
const result = await hyperbook.store.db.p5.get(id);
|
|
187
|
+
if (id && cm) {
|
|
188
|
+
hyperbook.store.db.p5.get(id).then((result) => {
|
|
184
189
|
if (result) {
|
|
185
|
-
|
|
190
|
+
cm.setValue(result.sketch);
|
|
186
191
|
const code = result.sketch;
|
|
187
192
|
frame.srcdoc = template
|
|
188
193
|
.replace("###SLOT###", wrapSketch(code))
|
|
189
194
|
.replaceAll("###ORIGIN###", window.location.origin)
|
|
190
195
|
.replace(/\u00A0/g, " ");
|
|
191
196
|
}
|
|
192
|
-
|
|
193
|
-
editor.addEventListener("input", () => {
|
|
194
|
-
hyperbook.store.db.p5.put({ id, sketch: editor.value });
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
update?.addEventListener("click", () => {
|
|
199
|
-
const code = editor.value;
|
|
200
|
-
frame.srcdoc = template
|
|
201
|
-
.replace("###SLOT###", wrapSketch(code))
|
|
202
|
-
.replaceAll("###ORIGIN###", window.location.origin);
|
|
203
197
|
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
update?.addEventListener("click", () => {
|
|
201
|
+
const code = cm?.getValue() ?? "";
|
|
202
|
+
frame.srcdoc = template
|
|
203
|
+
.replace("###SLOT###", wrapSketch(code))
|
|
204
|
+
.replaceAll("###ORIGIN###", window.location.origin);
|
|
204
205
|
});
|
|
205
206
|
}
|
|
206
207
|
|
|
@@ -11,6 +11,18 @@ code-input {
|
|
|
11
11
|
margin: 0;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
.directive-p5 .editor {
|
|
15
|
+
width: 100%;
|
|
16
|
+
border: 1px solid var(--color-spacer);
|
|
17
|
+
flex: 1;
|
|
18
|
+
min-height: 0;
|
|
19
|
+
overflow: hidden;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.directive-p5 .editor .cm-editor {
|
|
23
|
+
height: 100%;
|
|
24
|
+
}
|
|
25
|
+
|
|
14
26
|
.directive-p5 .container {
|
|
15
27
|
width: 100%;
|
|
16
28
|
min-height: 120px;
|
|
@@ -70,12 +82,6 @@ code-input {
|
|
|
70
82
|
opacity: 0.75;
|
|
71
83
|
}
|
|
72
84
|
|
|
73
|
-
.directive-p5 .editor {
|
|
74
|
-
width: 100%;
|
|
75
|
-
border: 1px solid var(--color-spacer);
|
|
76
|
-
flex: 1;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
85
|
.directive-p5 .buttons {
|
|
80
86
|
display: flex;
|
|
81
87
|
border: 1px solid var(--color-spacer);
|
|
@@ -8,14 +8,6 @@
|
|
|
8
8
|
* @see hyperbook.i18n
|
|
9
9
|
*/
|
|
10
10
|
hyperbook.python = (function () {
|
|
11
|
-
window.codeInput?.registerTemplate(
|
|
12
|
-
"pyide-highlighted",
|
|
13
|
-
codeInput.templates.prism(window.Prism, [
|
|
14
|
-
new codeInput.plugins.AutoCloseBrackets(),
|
|
15
|
-
new codeInput.plugins.Indent(true, 2),
|
|
16
|
-
])
|
|
17
|
-
);
|
|
18
|
-
|
|
19
11
|
const PYODIDE_CDN = "https://cdn.jsdelivr.net/pyodide/v0.29.4/full/pyodide.js";
|
|
20
12
|
|
|
21
13
|
const loadPyodideScript = () => {
|
|
@@ -1899,7 +1891,7 @@ if _pg:
|
|
|
1899
1891
|
const test = elem.getElementsByClassName("test")[0];
|
|
1900
1892
|
const stop = elem.getElementsByClassName("stop")[0];
|
|
1901
1893
|
const editor = elem.getElementsByClassName("editor")[0];
|
|
1902
|
-
const
|
|
1894
|
+
const editorCm = editor?._cm;
|
|
1903
1895
|
const state = getExecutionState(elem.id);
|
|
1904
1896
|
const hasRuntime = runtimes.has(elem.id);
|
|
1905
1897
|
const hasInterrupt = interruptBuffers.has(elem.id);
|
|
@@ -1917,11 +1909,8 @@ if _pg:
|
|
|
1917
1909
|
elem.classList.toggle("locked-by-other", lockedByOther);
|
|
1918
1910
|
|
|
1919
1911
|
if (state.running || lockedByOther) {
|
|
1920
|
-
editor?.setAttribute("disabled", "");
|
|
1921
1912
|
editor?.classList.add("running");
|
|
1922
|
-
|
|
1923
|
-
editorTextarea.readOnly = true;
|
|
1924
|
-
}
|
|
1913
|
+
editorCm?.setReadOnly(true);
|
|
1925
1914
|
if (state.running && state.type === "run") {
|
|
1926
1915
|
run.textContent = hyperbook.i18n.get("pyide-running");
|
|
1927
1916
|
run.disabled = true;
|
|
@@ -1969,11 +1958,8 @@ if _pg:
|
|
|
1969
1958
|
stop.classList.toggle("stopping", state.stopping);
|
|
1970
1959
|
}
|
|
1971
1960
|
} else {
|
|
1972
|
-
editor?.removeAttribute("disabled");
|
|
1973
1961
|
editor?.classList.remove("running");
|
|
1974
|
-
|
|
1975
|
-
editorTextarea.readOnly = false;
|
|
1976
|
-
}
|
|
1962
|
+
editorCm?.setReadOnly(false);
|
|
1977
1963
|
run.classList.remove("stopping");
|
|
1978
1964
|
run.classList.remove("running");
|
|
1979
1965
|
run.textContent = hyperbook.i18n.get("pyide-run");
|
|
@@ -2162,7 +2148,7 @@ if _pg:
|
|
|
2162
2148
|
if (elem.getAttribute("data-pyide-initialized") === "true") continue;
|
|
2163
2149
|
elem.setAttribute("data-pyide-initialized", "true");
|
|
2164
2150
|
|
|
2165
|
-
const
|
|
2151
|
+
const editorDiv = elem.getElementsByClassName("editor")[0];
|
|
2166
2152
|
const container = elem.getElementsByClassName("container")[0];
|
|
2167
2153
|
const editorContainer = elem.getElementsByClassName("editor-container")[0];
|
|
2168
2154
|
const splitter = elem.getElementsByClassName("splitter")[0];
|
|
@@ -2202,11 +2188,20 @@ if _pg:
|
|
|
2202
2188
|
};
|
|
2203
2189
|
let pyideState = { id };
|
|
2204
2190
|
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2191
|
+
// Initialize CodeMirror
|
|
2192
|
+
const initialSource = editorDiv ? editorDiv.textContent : "";
|
|
2193
|
+
if (editorDiv) editorDiv.textContent = "";
|
|
2194
|
+
const cm = editorDiv ? HyperbookCM.create(editorDiv, {
|
|
2195
|
+
lang: editorDiv.dataset.lang || "python",
|
|
2196
|
+
value: initialSource,
|
|
2197
|
+
onChange: (code) => {
|
|
2198
|
+
void persistPyideState({ script: code });
|
|
2199
|
+
},
|
|
2200
|
+
}) : null;
|
|
2201
|
+
// Store CM on the element so updateRunning() can toggle readOnly
|
|
2202
|
+
if (editorDiv && cm) editorDiv._cm = cm;
|
|
2203
|
+
|
|
2204
|
+
const getEditorValue = () => cm?.getValue() ?? "";
|
|
2210
2205
|
|
|
2211
2206
|
pyideState = { ...pyideState, script: getEditorValue() };
|
|
2212
2207
|
|
|
@@ -2359,7 +2354,7 @@ if _pg:
|
|
|
2359
2354
|
if (result) {
|
|
2360
2355
|
pyideState = { ...pyideState, ...result };
|
|
2361
2356
|
if (typeof result.script === "string") {
|
|
2362
|
-
|
|
2357
|
+
cm?.setValue(result.script);
|
|
2363
2358
|
}
|
|
2364
2359
|
if (
|
|
2365
2360
|
Number.isFinite(result.splitHorizontal) &&
|
|
@@ -2386,10 +2381,7 @@ if _pg:
|
|
|
2386
2381
|
}
|
|
2387
2382
|
};
|
|
2388
2383
|
|
|
2389
|
-
|
|
2390
|
-
if (editor.querySelector("textarea")) {
|
|
2391
|
-
void restoreEditorState();
|
|
2392
|
-
}
|
|
2384
|
+
void restoreEditorState();
|
|
2393
2385
|
|
|
2394
2386
|
window.addEventListener("resize", () => {
|
|
2395
2387
|
applyCanvasOutputLayout();
|
|
@@ -2397,10 +2389,6 @@ if _pg:
|
|
|
2397
2389
|
});
|
|
2398
2390
|
applyCanvasOutputLayout();
|
|
2399
2391
|
|
|
2400
|
-
editor.addEventListener("input", () => {
|
|
2401
|
-
void persistPyideState({ script: getEditorValue() });
|
|
2402
|
-
});
|
|
2403
|
-
|
|
2404
2392
|
test?.addEventListener("click", async () => {
|
|
2405
2393
|
showOutput();
|
|
2406
2394
|
const state = getExecutionState(id);
|
|
@@ -12,6 +12,18 @@
|
|
|
12
12
|
margin: 0;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
.directive-pyide .editor {
|
|
16
|
+
width: 100%;
|
|
17
|
+
border: 1px solid var(--color-spacer);
|
|
18
|
+
flex: 1;
|
|
19
|
+
min-height: 0;
|
|
20
|
+
overflow: hidden;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.directive-pyide .editor .cm-editor {
|
|
24
|
+
height: 100%;
|
|
25
|
+
}
|
|
26
|
+
|
|
15
27
|
.directive-pyide .container {
|
|
16
28
|
width: 100%;
|
|
17
29
|
overflow: hidden;
|
|
@@ -135,12 +147,6 @@
|
|
|
135
147
|
opacity: 0.75;
|
|
136
148
|
}
|
|
137
149
|
|
|
138
|
-
.directive-pyide .editor {
|
|
139
|
-
width: 100%;
|
|
140
|
-
border: 1px solid var(--color-spacer);
|
|
141
|
-
flex: 1;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
150
|
.directive-pyide .editor.running {
|
|
145
151
|
pointer-events: none;
|
|
146
152
|
opacity: 0.7;
|
|
@@ -88,21 +88,6 @@ hyperbook.typst = (function () {
|
|
|
88
88
|
// INITIALIZATION
|
|
89
89
|
// ============================================================================
|
|
90
90
|
|
|
91
|
-
/**
|
|
92
|
-
* Initialize code-input template for Typst syntax highlighting
|
|
93
|
-
*/
|
|
94
|
-
const initializeCodeInput = () => {
|
|
95
|
-
if (!window.codeInput) return;
|
|
96
|
-
|
|
97
|
-
window.codeInput.registerTemplate(
|
|
98
|
-
"typst-highlighted",
|
|
99
|
-
window.codeInput.templates.prism(window.Prism, [
|
|
100
|
-
new window.codeInput.plugins.AutoCloseBrackets(),
|
|
101
|
-
new window.codeInput.plugins.Indent(true, 2),
|
|
102
|
-
])
|
|
103
|
-
);
|
|
104
|
-
};
|
|
105
|
-
|
|
106
91
|
// ============================================================================
|
|
107
92
|
// TYPST LOADER
|
|
108
93
|
// ============================================================================
|
|
@@ -1269,6 +1254,7 @@ hyperbook.typst = (function () {
|
|
|
1269
1254
|
this.sourceTextarea = elem.querySelector('.typst-source');
|
|
1270
1255
|
this.fullscreenBtn = elem.querySelector('.fullscreen');
|
|
1271
1256
|
this.editorInitialized = false;
|
|
1257
|
+
this.cm = null;
|
|
1272
1258
|
|
|
1273
1259
|
setupSplitter(this.elem, this.previewContainer, this.editorContainer, this.splitter);
|
|
1274
1260
|
|
|
@@ -1320,28 +1306,26 @@ hyperbook.typst = (function () {
|
|
|
1320
1306
|
*/
|
|
1321
1307
|
async initialize() {
|
|
1322
1308
|
if (this.editor) {
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
this.editor.addEventListener('input', () => {
|
|
1309
|
+
if (this.editorInitialized) return;
|
|
1310
|
+
this.editorInitialized = true;
|
|
1311
|
+
|
|
1312
|
+
// Create CodeMirror editor
|
|
1313
|
+
const initialSource = this.editor.textContent;
|
|
1314
|
+
this.editor.textContent = "";
|
|
1315
|
+
const debouncedRerender = debounce(() => this.rerender(), CONFIG.DEBOUNCE_DELAY);
|
|
1316
|
+
this.cm = HyperbookCM.create(this.editor, {
|
|
1317
|
+
lang: "typst",
|
|
1318
|
+
value: initialSource,
|
|
1319
|
+
onChange: () => {
|
|
1335
1320
|
this.saveState();
|
|
1336
1321
|
debouncedRerender();
|
|
1337
|
-
}
|
|
1338
|
-
};
|
|
1322
|
+
},
|
|
1323
|
+
});
|
|
1339
1324
|
|
|
1340
|
-
|
|
1341
|
-
this.
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
}
|
|
1325
|
+
await this.restoreState();
|
|
1326
|
+
this.uiManager.updateTabs();
|
|
1327
|
+
this.uiManager.updateBinaryFilesList();
|
|
1328
|
+
this.rerender();
|
|
1345
1329
|
} else if (this.sourceTextarea) {
|
|
1346
1330
|
// Preview mode
|
|
1347
1331
|
const initialCode = this.sourceTextarea.value;
|
|
@@ -1606,28 +1590,12 @@ hyperbook.typst = (function () {
|
|
|
1606
1590
|
|
|
1607
1591
|
getEditorValue() {
|
|
1608
1592
|
if (!this.editor) return '';
|
|
1609
|
-
|
|
1610
|
-
if (textarea) return textarea.value;
|
|
1611
|
-
try {
|
|
1612
|
-
if (typeof this.editor.value === 'string') {
|
|
1613
|
-
return this.editor.value;
|
|
1614
|
-
}
|
|
1615
|
-
} catch (e) {}
|
|
1616
|
-
return this.editor.textContent || '';
|
|
1593
|
+
return this.cm?.getValue() ?? '';
|
|
1617
1594
|
}
|
|
1618
1595
|
|
|
1619
1596
|
setEditorValue(value) {
|
|
1620
1597
|
if (!this.editor) return;
|
|
1621
|
-
|
|
1622
|
-
const textarea = this.editor.querySelector('textarea');
|
|
1623
|
-
if (textarea) {
|
|
1624
|
-
textarea.value = normalizedValue;
|
|
1625
|
-
}
|
|
1626
|
-
try {
|
|
1627
|
-
this.editor.value = normalizedValue;
|
|
1628
|
-
} catch (e) {
|
|
1629
|
-
this.editor.textContent = normalizedValue;
|
|
1630
|
-
}
|
|
1598
|
+
this.cm?.setValue(value ?? '');
|
|
1631
1599
|
}
|
|
1632
1600
|
}
|
|
1633
1601
|
|
|
@@ -1635,9 +1603,6 @@ hyperbook.typst = (function () {
|
|
|
1635
1603
|
// MAIN INITIALIZATION
|
|
1636
1604
|
// ============================================================================
|
|
1637
1605
|
|
|
1638
|
-
// Initialize code-input
|
|
1639
|
-
initializeCodeInput();
|
|
1640
|
-
|
|
1641
1606
|
// Get all Typst directive elements
|
|
1642
1607
|
const elements = document.getElementsByClassName('directive-typst');
|
|
1643
1608
|
|
|
@@ -12,6 +12,18 @@ code-input {
|
|
|
12
12
|
margin: 0;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
.directive-typst .editor {
|
|
16
|
+
width: 100%;
|
|
17
|
+
border: 1px solid var(--color-spacer);
|
|
18
|
+
flex: 1;
|
|
19
|
+
min-height: 0;
|
|
20
|
+
overflow: hidden;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.directive-typst .editor .cm-editor {
|
|
24
|
+
height: 100%;
|
|
25
|
+
}
|
|
26
|
+
|
|
15
27
|
.directive-typst .preview-container {
|
|
16
28
|
width: 100%;
|
|
17
29
|
min-height: 120px;
|
|
@@ -419,12 +431,6 @@ code-input {
|
|
|
419
431
|
color: #dc2626;
|
|
420
432
|
}
|
|
421
433
|
|
|
422
|
-
.directive-typst .editor {
|
|
423
|
-
width: 100%;
|
|
424
|
-
border: 1px solid var(--color-spacer);
|
|
425
|
-
flex: 1;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
434
|
.directive-typst .editor:not(.active) {
|
|
429
435
|
display: none;
|
|
430
436
|
}
|
|
@@ -8,14 +8,6 @@
|
|
|
8
8
|
* @see hyperbook.i18n
|
|
9
9
|
*/
|
|
10
10
|
hyperbook.webide = (function () {
|
|
11
|
-
window.codeInput?.registerTemplate(
|
|
12
|
-
"webide-highlighted",
|
|
13
|
-
codeInput.templates.prism(window.Prism, [
|
|
14
|
-
new codeInput.plugins.AutoCloseBrackets(),
|
|
15
|
-
new codeInput.plugins.Indent(true, 2),
|
|
16
|
-
]),
|
|
17
|
-
);
|
|
18
|
-
|
|
19
11
|
function setupSplitter(elem, container, editorContainer, splitter) {
|
|
20
12
|
if (!container || !editorContainer || !splitter) return;
|
|
21
13
|
|
|
@@ -118,12 +110,9 @@ hyperbook.webide = (function () {
|
|
|
118
110
|
const editorContainer = elem.querySelector(".editor-container");
|
|
119
111
|
const splitter = elem.querySelector(".splitter");
|
|
120
112
|
const title = elem.getElementsByClassName("container-title")[0];
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
const editorCSS = elem.querySelector(".editor.css");
|
|
125
|
-
/** @type {HTMLTextAreaElement | null} */
|
|
126
|
-
const editorJS = elem.querySelector(".editor.js");
|
|
113
|
+
const editorHTMLDiv = elem.querySelector(".editor.html");
|
|
114
|
+
const editorCSSDiv = elem.querySelector(".editor.css");
|
|
115
|
+
const editorJSDiv = elem.querySelector(".editor.js");
|
|
127
116
|
/** @type {HTMLButtonElement | null} */
|
|
128
117
|
const btnHTML = elem.querySelector("button.html");
|
|
129
118
|
/** @type {HTMLButtonElement | null} */
|
|
@@ -131,6 +120,23 @@ hyperbook.webide = (function () {
|
|
|
131
120
|
/** @type {HTMLButtonElement | null} */
|
|
132
121
|
const btnJS = elem.querySelector("button.js");
|
|
133
122
|
|
|
123
|
+
// Initialize CodeMirror instances
|
|
124
|
+
const cmHTML = editorHTMLDiv ? (() => {
|
|
125
|
+
const src = editorHTMLDiv.textContent;
|
|
126
|
+
editorHTMLDiv.textContent = "";
|
|
127
|
+
return HyperbookCM.create(editorHTMLDiv, { lang: "html", value: src, onChange: () => update() });
|
|
128
|
+
})() : null;
|
|
129
|
+
const cmCSS = editorCSSDiv ? (() => {
|
|
130
|
+
const src = editorCSSDiv.textContent;
|
|
131
|
+
editorCSSDiv.textContent = "";
|
|
132
|
+
return HyperbookCM.create(editorCSSDiv, { lang: "css", value: src, onChange: () => update() });
|
|
133
|
+
})() : null;
|
|
134
|
+
const cmJS = editorJSDiv ? (() => {
|
|
135
|
+
const src = editorJSDiv.textContent;
|
|
136
|
+
editorJSDiv.textContent = "";
|
|
137
|
+
return HyperbookCM.create(editorJSDiv, { lang: "javascript", value: src, onChange: () => update() });
|
|
138
|
+
})() : null;
|
|
139
|
+
|
|
134
140
|
const frame = elem.getElementsByTagName("iframe")[0];
|
|
135
141
|
const template = elem.getAttribute("data-template");
|
|
136
142
|
const id = elem.getAttribute("data-id");
|
|
@@ -164,9 +170,9 @@ hyperbook.webide = (function () {
|
|
|
164
170
|
btnCSS?.classList.remove("active");
|
|
165
171
|
btnJS?.classList.remove("active");
|
|
166
172
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
173
|
+
editorHTMLDiv?.classList.add("active");
|
|
174
|
+
editorCSSDiv?.classList.remove("active");
|
|
175
|
+
editorJSDiv?.classList.remove("active");
|
|
170
176
|
});
|
|
171
177
|
|
|
172
178
|
btnCSS?.addEventListener("click", () => {
|
|
@@ -174,9 +180,9 @@ hyperbook.webide = (function () {
|
|
|
174
180
|
btnCSS?.classList.add("active");
|
|
175
181
|
btnJS?.classList.remove("active");
|
|
176
182
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
183
|
+
editorHTMLDiv?.classList.remove("active");
|
|
184
|
+
editorCSSDiv?.classList.add("active");
|
|
185
|
+
editorJSDiv?.classList.remove("active");
|
|
180
186
|
});
|
|
181
187
|
|
|
182
188
|
btnJS?.addEventListener("click", () => {
|
|
@@ -184,80 +190,40 @@ hyperbook.webide = (function () {
|
|
|
184
190
|
btnCSS?.classList.remove("active");
|
|
185
191
|
btnJS?.classList.add("active");
|
|
186
192
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
193
|
+
editorHTMLDiv?.classList.remove("active");
|
|
194
|
+
editorCSSDiv?.classList.remove("active");
|
|
195
|
+
editorJSDiv?.classList.add("active");
|
|
190
196
|
});
|
|
191
197
|
|
|
192
|
-
const load = async () => {
|
|
193
|
-
const result = await hyperbook.store.db.webide.get(id);
|
|
194
|
-
if (!result) {
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
const website = template
|
|
198
|
-
.replace("###HTML###", result.html)
|
|
199
|
-
.replace("###CSS###", result.css)
|
|
200
|
-
.replace("###JS###", result.js);
|
|
201
|
-
frame.srcdoc = website;
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
load();
|
|
205
|
-
|
|
206
198
|
const update = () => {
|
|
199
|
+
const htmlVal = cmHTML?.getValue() ?? "";
|
|
200
|
+
const cssVal = cmCSS?.getValue() ?? "";
|
|
201
|
+
const jsVal = cmJS?.getValue() ?? "";
|
|
207
202
|
hyperbook.store.db.webide.put({
|
|
208
203
|
id,
|
|
209
|
-
html:
|
|
210
|
-
css:
|
|
211
|
-
js:
|
|
204
|
+
html: htmlVal,
|
|
205
|
+
css: cssVal,
|
|
206
|
+
js: jsVal,
|
|
212
207
|
});
|
|
213
208
|
const website = template
|
|
214
|
-
.replace("###HTML###",
|
|
215
|
-
.replace("###CSS###",
|
|
216
|
-
.replace("###JS###",
|
|
209
|
+
.replace("###HTML###", htmlVal)
|
|
210
|
+
.replace("###CSS###", cssVal)
|
|
211
|
+
.replace("###JS###", jsVal);
|
|
217
212
|
frame.srcdoc = website;
|
|
218
213
|
};
|
|
219
214
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
editorHTML?.addEventListener("code-input_load", async () => {
|
|
225
|
-
const result = await hyperbook.store.db.webide.get(id);
|
|
226
|
-
if (result) {
|
|
227
|
-
editorHTML.value = result.html;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
update();
|
|
231
|
-
|
|
232
|
-
editorHTML.addEventListener("input", () => {
|
|
233
|
-
update();
|
|
234
|
-
});
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
editorCSS?.addEventListener("code-input_load", async () => {
|
|
238
|
-
const result = await hyperbook.store.db.webide.get(id);
|
|
215
|
+
// Restore saved state on init
|
|
216
|
+
hyperbook.store.db.webide.get(id).then((result) => {
|
|
239
217
|
if (result) {
|
|
240
|
-
|
|
218
|
+
if (cmHTML && result.html != null) cmHTML.setValue(result.html);
|
|
219
|
+
if (cmCSS && result.css != null) cmCSS.setValue(result.css);
|
|
220
|
+
if (cmJS && result.js != null) cmJS.setValue(result.js);
|
|
241
221
|
}
|
|
242
|
-
|
|
243
222
|
update();
|
|
244
|
-
|
|
245
|
-
editorCSS.addEventListener("input", () => {
|
|
246
|
-
update();
|
|
247
|
-
});
|
|
248
223
|
});
|
|
249
224
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
if (result) {
|
|
253
|
-
editorJS.value = result.js;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
update();
|
|
257
|
-
|
|
258
|
-
editorJS.addEventListener("input", () => {
|
|
259
|
-
update();
|
|
260
|
-
});
|
|
225
|
+
frame.addEventListener("load", () => {
|
|
226
|
+
title.textContent = frame.contentDocument.title;
|
|
261
227
|
});
|
|
262
228
|
|
|
263
229
|
downloadEl?.addEventListener("click", async () => {
|
|
@@ -12,6 +12,18 @@ code-input {
|
|
|
12
12
|
margin: 0;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
.directive-webide .editor {
|
|
16
|
+
width: 100%;
|
|
17
|
+
border: 1px solid var(--color-spacer);
|
|
18
|
+
flex: 1;
|
|
19
|
+
min-height: 0;
|
|
20
|
+
overflow: hidden;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.directive-webide .editor .cm-editor {
|
|
24
|
+
height: 100%;
|
|
25
|
+
}
|
|
26
|
+
|
|
15
27
|
.directive-webide .container {
|
|
16
28
|
width: 100%;
|
|
17
29
|
min-height: 120px;
|
|
@@ -89,12 +101,6 @@ code-input {
|
|
|
89
101
|
opacity: 0.75;
|
|
90
102
|
}
|
|
91
103
|
|
|
92
|
-
.directive-webide .editor {
|
|
93
|
-
width: 100%;
|
|
94
|
-
border: 1px solid var(--color-spacer);
|
|
95
|
-
flex: 1;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
104
|
.directive-webide .editor:not(.active) {
|
|
99
105
|
display: none;
|
|
100
106
|
}
|