Skip to content

Commit 1efd350

Browse files
authored
Some features and review comments
1 parent 5291829 commit 1efd350

9 files changed

Lines changed: 66 additions & 51 deletions

File tree

.github/workflows/single-file.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@ name: Create single file build
22

33
on:
44
push:
5-
branches: ["*"]
5+
branches: ["**"]
66
workflow_dispatch:
77

88
permissions:
99
contents: read
1010

1111
concurrency:
12-
group: "pages"
12+
group: "single-file-build"
1313
cancel-in-progress: false
1414

1515
jobs:
16-
deploy:
16+
build:
1717
runs-on: ubuntu-latest
1818
steps:
1919
- name: Checkout
@@ -23,10 +23,12 @@ jobs:
2323
uses: actions/setup-node@v4
2424
with:
2525
node-version: "20"
26+
cache: "npm"
27+
cache-dependency-path: "Build/package-lock.json"
2628

2729
- name: Install dependencies
2830
working-directory: Build
29-
run: npm i
31+
run: npm ci
3032

3133
- name: Build
3234
working-directory: Build

Build/package-lock.json

Lines changed: 5 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Build/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,14 @@
2121
"@babel/core": "^7.29.0",
2222
"@babel/parser": "^7.29.3",
2323
"@babel/runtime": "^7.29.2",
24+
"@codemirror/autocomplete": "^6.20.2",
2425
"@codemirror/commands": "^6.10.3",
2526
"@codemirror/lang-css": "^6.3.1",
2627
"@codemirror/lang-html": "^6.4.11",
2728
"@codemirror/lang-javascript": "^6.2.5",
2829
"@codemirror/lint": "^6.9.6",
2930
"@codemirror/state": "^6.6.0",
30-
"@codemirror/view": "^6.42.1",
31+
"@codemirror/view": "^6.43.0",
3132
"@fortawesome/fontawesome-free": "^7.2.0",
3233
"@nisoku/sairin": "^0.1.2",
3334
"@prettier/plugin-xml": "^3.4.2",

Build/src/console.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ConsoleMessage, StackInfo } from './types';
2+
import { editors } from './editor';
23

34
export const consoleOutput = document.getElementById('console') as HTMLDivElement;
45

@@ -49,8 +50,34 @@ function handleConsoleMessage(event: MessageEvent<ConsoleMessage>): void {
4950
if (event.data.data[1]?.stack) {
5051
const stack = document.createElement('div');
5152
stack.className = 'console-stack';
52-
stack.textContent = event.data.data[1].stack;
53-
stack.addEventListener('click', () => stack.classList.toggle('expanded'));
53+
const rawStack: string = event.data.data[1].stack;
54+
// Render each stack line as a clickable element when possible
55+
rawStack.split('\n').forEach((line) => {
56+
const lineEl = document.createElement('div');
57+
lineEl.className = 'console-stack-line';
58+
lineEl.textContent = line.trim();
59+
// Try to parse ":line:col" at end of line
60+
const m = line.match(/:(\d+):(\d+)\)?$/);
61+
if (m) {
62+
const lineNum = parseInt(m[1], 10);
63+
const colNum = parseInt(m[2], 10);
64+
lineEl.classList.add('clickable');
65+
lineEl.addEventListener('click', () => {
66+
// Focus JS editor if available and set cursor
67+
const ed = editors.js;
68+
if (!ed) return;
69+
const doc = ed.view.state.doc;
70+
const maxLine = doc.lines;
71+
const safeLine = Math.max(1, Math.min(lineNum, maxLine));
72+
const lineObj = doc.line(safeLine);
73+
const offset = Math.min(lineObj.to, lineObj.from + Math.max(0, colNum - 1));
74+
ed.view.dispatch({ selection: { anchor: offset } });
75+
ed.view.focus();
76+
});
77+
}
78+
stack.appendChild(lineEl);
79+
});
80+
stack.addEventListener('click', (e) => { if (e.target === stack) stack.classList.toggle('expanded'); });
5481
message.appendChild(stack);
5582
}
5683
entry.appendChild(timestamp);

Build/src/defaultContent.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ export const defaultHtml = `<!DOCTYPE html>
55
<link rel="stylesheet" href="styles.css">
66
</head>
77
<body>
8-
<script src="script.js"><\/script>
8+
<script src="script.js"></script>
99
<h1>Hello, HTMLRunner!</h1>
1010
<p>This is a demo page.</p>
1111
<button onclick="testFunction()">Click me!</button>
12-
<\/body>
13-
<\/html>`;
12+
</body>
13+
</html>`;
1414

1515
export const defaultCss = `body {
1616
font-family: Arial, sans-serif;

Build/src/editor.ts

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { Compartment, EditorState, Extension } from "@codemirror/state";
2-
import { EditorView, lineNumbers, keymap } from "@codemirror/view"; // Add standardKeymap, defaultKeymap
3-
import { defaultKeymap, standardKeymap } from "@codemirror/commands";
2+
import { EditorView, keymap, lineNumbers } from "@codemirror/view";
3+
import { defaultKeymap, standardKeymap, toggleComment } from "@codemirror/commands";
44
import { html } from "@codemirror/lang-html";
55
import { css } from "@codemirror/lang-css";
66
import { javascript } from "@codemirror/lang-javascript";
7-
import { linter, lintGutter } from "@codemirror/lint";
7+
import { autocompletion } from "@codemirror/autocomplete";
8+
import { foldGutter, foldKeymap } from "@codemirror/language";
9+
import { formatCode } from "./runner";
810
import { monokai } from "@uiw/codemirror-theme-monokai";
911
import { bbedit } from "@uiw/codemirror-theme-bbedit";
1012
import { CodeMirrorEditor, Editors } from "./types";
@@ -13,7 +15,6 @@ import { toggleSearch } from "./main";
1315
import { debounce } from "./utils";
1416
import { autoRunState, cssState, darkModeState, htmlState, jsState } from "./appState";
1517
import { search } from "@codemirror/search";
16-
import { toggleComment } from "@codemirror/commands"; // Ensure toggleComment is imported
1718

1819
export const editors: Editors = {
1920
html: null as unknown as CodeMirrorEditor,
@@ -34,10 +35,17 @@ export function setDarkMode(value: boolean): void {
3435

3536
export function setAutoRun(value: boolean): void {
3637
autoRunState.set(value);
38+
Object.values(editors).forEach((editor) => {
39+
editor.view.dispatch({
40+
effects: editor.autoRunCompartment.reconfigure(
41+
value ? autoRunListener : []
42+
),
43+
});
44+
});
3745
}
3846

3947
// Shared debounced runner used by auto-run listeners so debounce state is preserved across keystrokes
40-
const debouncedRun = debounce(runCode, 1000);
48+
const debouncedRun = debounce(runCode, 250);
4149
export const autoRunListener = EditorView.updateListener.of((update) => {
4250
if (update.docChanged) debouncedRun();
4351
});
@@ -56,6 +64,7 @@ function createEditorConfig(
5664
doc: content,
5765
extensions: [
5866
lineNumbers(),
67+
foldGutter(),
5968
language,
6069
themeCompartment.of(darkModeState.get() ? monokai : bbedit),
6170
EditorView.lineWrapping,
@@ -66,16 +75,9 @@ function createEditorConfig(
6675
".cm-content": { minHeight: "100%" }
6776
}),
6877
search(),
69-
linter(
70-
(view) => {
71-
return [];
72-
},
73-
{ delay: 100 }
74-
),
75-
lintGutter(),
7678
keymap.of([
7779
...defaultKeymap,
78-
...standardKeymap,
80+
...foldKeymap,
7981
{ key: "Mod-/", run: toggleComment },
8082
]),
8183
autoRunCompartment.of(autoRunState.get() ? autoRunListener : []),

Build/src/main.ts

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
import { initializeEditors, setDarkMode, setAutoRun } from "./editor";
22
import "@fortawesome/fontawesome-free/css/all.min.css";
3-
import { EditorView, keymap } from "@codemirror/view";
4-
import { EditorState, StateEffect } from "@codemirror/state";
53
import {
64
search,
75
openSearchPanel,
86
searchPanelOpen,
97
closeSearchPanel,
108
} from "@codemirror/search";
11-
import { toggleComment } from "@codemirror/commands";
129
import Split from "split.js";
1310
import { copyToClipboard } from "./utils";
1411
import { editors } from "./editor";
@@ -258,19 +255,6 @@ function toggleAutoRun(): void {
258255
const newAutoRun = !autoRunState.get();
259256
setAutoRun(newAutoRun);
260257
updateAutoRunStatus();
261-
262-
Object.values(editors).forEach((editor) => {
263-
const listener = newAutoRun
264-
? EditorView.updateListener.of((update) => {
265-
if (update.docChanged) {
266-
debounce(runCode, 1000)();
267-
}
268-
})
269-
: [];
270-
editor.view.dispatch({
271-
effects: editor.autoRunCompartment.reconfigure(listener),
272-
});
273-
});
274258
}
275259

276260
function updateAutoRunStatus(): void {

Build/src/runner.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export function runCode(): void {
5757
}
5858

5959
let docContent;
60-
const isFullHtml = /<html[\s>]|<!doctype html/i.test(html);
60+
const isFullHtml = /<html[\s>/]|<!doctype html/i.test(html);
6161
if (isFullHtml) {
6262
const parser = new DOMParser();
6363
const doc = parser.parseFromString(html, "text/html");
@@ -88,13 +88,13 @@ export function runCode(): void {
8888
"</style>",
8989
"<script>",
9090
consoleInterceptor,
91-
"<\/script>",
91+
"</script>",
9292
"</head><body>",
9393
html,
94-
"<\/body>",
94+
"</body>",
9595
"<script>",
9696
js,
97-
"<\/script><\/html>",
97+
"</script></html>",
9898
].join("");
9999
}
100100

@@ -105,7 +105,7 @@ export function runCode(): void {
105105
throw new Error("Preview element not found or is not an iframe");
106106
}
107107
preview.src = url;
108-
preview.addEventListener("load", () => URL.revokeObjectURL(url));
108+
preview.addEventListener("load", () => URL.revokeObjectURL(url), { once: true });
109109
switchOutput("preview");
110110
} catch (error: any) {
111111
showError(`Error running code: ${error.message}`);

Build/src/ui.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,7 @@ export function updateAutoRunStatus(): void {
111111

112112
export function toggleDarkMode(): void {
113113
const newDarkMode = !darkModeState.get();
114-
setDarkMode(newDarkMode);
115-
document.body.classList.toggle("dark-mode", newDarkMode);
116-
updateThemeIcon();
114+
setPageDarkMode(newDarkMode);
117115
}
118116

119117
export function updateThemeIcon(): void {

0 commit comments

Comments
 (0)