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.
@@ -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 editor = elem.getElementsByClassName("editor")[0];
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(editor.value);
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([editor.value], { type: "text/plain" });
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
- editor?.addEventListener("code-input_load", async () => {
182
- if (id) {
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
- editor.value = result.sketch;
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 editorTextarea = editor?.querySelector("textarea");
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
- if (editorTextarea) {
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
- if (editorTextarea) {
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 editor = elem.getElementsByClassName("editor")[0];
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
- const getEditorValue = () => {
2206
- const textarea = editor?.querySelector("textarea");
2207
- if (textarea) return textarea.value;
2208
- return typeof editor?.textContent === "string" ? editor.textContent : "";
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
- editor.value = result.script;
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
- editor.addEventListener("code-input_load", restoreEditorState);
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
- const initializeEditor = async () => {
1324
- if (this.editorInitialized) return;
1325
- this.editorInitialized = true;
1326
- await this.restoreState();
1327
- this.uiManager.updateTabs();
1328
- this.uiManager.updateBinaryFilesList();
1329
- this.rerender();
1330
-
1331
- // Create debounced rerender for input events
1332
- const debouncedRerender = debounce(() => this.rerender(), CONFIG.DEBOUNCE_DELAY);
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
- // Edit mode - wait for code-input to load (or initialize immediately if already ready)
1341
- this.editor.addEventListener('code-input_load', initializeEditor);
1342
- if (this.editor.querySelector('textarea')) {
1343
- await initializeEditor();
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
- const textarea = this.editor.querySelector('textarea');
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
- const normalizedValue = value ?? '';
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
- /** @type {HTMLTextAreaElement | null} */
122
- const editorHTML = elem.querySelector(".editor.html");
123
- /** @type {HTMLTextAreaElement | null} */
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
- editorHTML?.classList.add("active");
168
- editorCSS?.classList.remove("active");
169
- editorJS?.classList.remove("active");
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
- editorHTML?.classList.remove("active");
178
- editorCSS?.classList.add("active");
179
- editorJS?.classList.remove("active");
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
- editorHTML?.classList.remove("active");
188
- editorCSS?.classList.remove("active");
189
- editorJS?.classList.add("active");
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: editorHTML?.value,
210
- css: editorCSS?.value,
211
- js: editorJS?.value,
204
+ html: htmlVal,
205
+ css: cssVal,
206
+ js: jsVal,
212
207
  });
213
208
  const website = template
214
- .replace("###HTML###", editorHTML?.value)
215
- .replace("###CSS###", editorCSS?.value)
216
- .replace("###JS###", editorJS?.value);
209
+ .replace("###HTML###", htmlVal)
210
+ .replace("###CSS###", cssVal)
211
+ .replace("###JS###", jsVal);
217
212
  frame.srcdoc = website;
218
213
  };
219
214
 
220
- frame.addEventListener("load", () => {
221
- title.textContent = frame.contentDocument.title;
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
- editorCSS.value = result.css;
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
- editorJS?.addEventListener("code-input_load", async () => {
251
- const result = await hyperbook.store.db.webide.get(id);
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
  }