Skip to content

Commit 598c48a

Browse files
authored
Stuff
1 parent 3e741de commit 598c48a

19 files changed

Lines changed: 2314 additions & 344 deletions

Build/dev-dist/registerSW.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Build/index.html

Lines changed: 78 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,76 @@
1212
</div>
1313
<div class="error-message" id="error-message"></div>
1414
<div class="container">
15-
<div class="header">
16-
<div class="logo">HTMLRunner</div>
17-
<div class="controls">
15+
<div class="menubar">
16+
<div class="menubar-left">
17+
<span class="logo">HTMLRunner</span>
18+
<div class="menu-items">
19+
<div class="menu-item" data-menu="file">File</div>
20+
<div class="menu-item" data-menu="edit">Edit</div>
21+
<div class="menu-item" data-menu="view">View</div>
22+
<div class="menu-item" data-menu="run">Run</div>
23+
<div class="menu-item" data-menu="git-menu">Git</div>
24+
<div class="menu-item" data-menu="help">Help</div>
25+
</div>
26+
</div>
27+
<div class="menubar-right">
1828
<button class="btn btn-run"><i class="fas fa-play"></i> Run</button>
19-
<button class="btn btn-format"><i class="fas fa-code"></i> Format</button>
20-
<button class="btn btn-reset"><i class="fas fa-undo"></i> Reset</button>
21-
<button class="btn btn-clear"><i class="fas fa-trash"></i> Clear Console</button>
22-
<button class="btn btn-download"><i class="fas fa-download"></i> Export Code</button>
23-
<button class="btn btn-auto-run"><i class="fas fa-sync"></i> Auto-Run: <span id="auto-run-status">Off</span></button>
24-
<button class="theme-toggle">
29+
<button class="btn btn-format" title="Format Code"><i class="fas fa-code"></i></button>
30+
<button class="btn btn-auto-run" title="Toggle Auto-Run"><i class="fas fa-sync"></i> <span id="auto-run-status">Off</span></button>
31+
<button class="theme-toggle" title="Toggle Theme">
2532
<i class="fas fa-moon"></i>
2633
<span>Dark Mode</span>
2734
</button>
2835
</div>
2936
</div>
37+
<div class="menu-dropdown" id="menu-file">
38+
<div class="menu-dropdown-item" data-action="addFile"><i class="fas fa-plus"></i> New File</div>
39+
<div class="menu-dropdown-item" data-action="addFolder"><i class="fas fa-folder-plus"></i> New Folder</div>
40+
<div class="menu-dropdown-item" data-action="importFiles"><i class="fas fa-folder-open"></i> Import Files</div>
41+
<div class="menu-dropdown-separator"></div>
42+
<div class="menu-dropdown-item" data-action="exportAsZip"><i class="fas fa-download"></i> Export ZIP</div>
43+
<div class="menu-dropdown-separator"></div>
44+
<div class="menu-dropdown-item" data-action="resetCode"><i class="fas fa-undo"></i> Reset to Defaults</div>
45+
</div>
46+
<div class="menu-dropdown" id="menu-edit">
47+
<div class="menu-dropdown-item" data-action="undo"><i class="fas fa-undo"></i> Undo</div>
48+
<div class="menu-dropdown-item" data-action="redo"><i class="fas fa-redo"></i> Redo</div>
49+
<div class="menu-dropdown-separator"></div>
50+
<div class="menu-dropdown-item" data-action="toggleSearch"><i class="fas fa-search"></i> Find</div>
51+
<div class="menu-dropdown-item" data-action="toggleReplace"><i class="fas fa-exchange-alt"></i> Find &amp; Replace</div>
52+
<div class="menu-dropdown-separator"></div>
53+
<div class="menu-dropdown-item" data-action="formatCode"><i class="fas fa-code"></i> Format Code</div>
54+
</div>
55+
<div class="menu-dropdown" id="menu-view">
56+
<div class="menu-dropdown-item" data-action="toggleSidebar"><i class="fas fa-columns"></i> Toggle Sidebar</div>
57+
<div class="menu-dropdown-item" data-action="switchOutput-preview"><i class="fas fa-eye"></i> Show Preview</div>
58+
<div class="menu-dropdown-item" data-action="switchOutput-console"><i class="fas fa-terminal"></i> Show Console</div>
59+
<div class="menu-dropdown-item" data-action="switchOutput-terminal"><i class="fas fa-window-terminal"></i> Show Terminal</div>
60+
<div class="menu-dropdown-separator"></div>
61+
<div class="menu-dropdown-item" data-action="toggleDarkMode"><i class="fas fa-moon"></i> Toggle Dark Mode</div>
62+
<div class="menu-dropdown-item" data-action="toggleAutoRun"><i class="fas fa-sync"></i> Toggle Auto-Run</div>
63+
</div>
64+
<div class="menu-dropdown" id="menu-run">
65+
<div class="menu-dropdown-item" data-action="runCode"><i class="fas fa-play"></i> Run Code</div>
66+
<div class="menu-dropdown-separator"></div>
67+
<div class="menu-dropdown-item" data-action="clearConsole"><i class="fas fa-trash"></i> Clear Console</div>
68+
</div>
69+
<div class="menu-dropdown" id="menu-git-menu">
70+
<div class="menu-dropdown-item" data-action="gitStatus"><i class="fas fa-info-circle"></i> Status</div>
71+
<div class="menu-dropdown-item" data-action="gitDiff"><i class="fas fa-code-branch"></i> Diff</div>
72+
<div class="menu-dropdown-item" data-action="gitLog"><i class="fas fa-history"></i> Log</div>
73+
<div class="menu-dropdown-separator"></div>
74+
<div class="menu-dropdown-item" data-action="gitCommit"><i class="fas fa-check"></i> Commit</div>
75+
<div class="menu-dropdown-item" data-action="gitPush"><i class="fas fa-cloud-upload-alt"></i> Push</div>
76+
<div class="menu-dropdown-item" data-action="gitPull"><i class="fas fa-cloud-download-alt"></i> Pull</div>
77+
<div class="menu-dropdown-separator"></div>
78+
<div class="menu-dropdown-item" data-action="gitClone"><i class="fas fa-clone"></i> Clone</div>
79+
</div>
80+
<div class="menu-dropdown" id="menu-help">
81+
<div class="menu-dropdown-item" data-action="about"><i class="fas fa-info-circle"></i> About HTMLRunner</div>
82+
<div class="menu-dropdown-separator"></div>
83+
<div class="menu-dropdown-item" data-action="openShortcuts"><i class="fas fa-keyboard"></i> Keyboard Shortcuts</div>
84+
</div>
3085
<div class="main">
3186
<div class="editor-panel" id="editor-panel">
3287
<div class="editor-header">
@@ -36,7 +91,11 @@
3691
<div class="sidebar" id="sidebar">
3792
<div class="sidebar-header">
3893
<span>FILES</span>
39-
<button class="sidebar-new-btn" id="sidebar-new-btn" title="New File"><i class="fas fa-plus"></i></button>
94+
<div class="sidebar-actions">
95+
<button class="sidebar-new-btn" id="sidebar-new-btn" title="New File"><i class="fas fa-plus"></i></button>
96+
<button class="sidebar-folder-btn" id="sidebar-folder-btn" title="New Folder"><i class="fas fa-folder-plus"></i></button>
97+
<button class="sidebar-import-btn" id="sidebar-import-btn" title="Import Files"><i class="fas fa-folder-open"></i></button>
98+
</div>
4099
</div>
41100
<div class="file-tree" id="file-tree"></div>
42101
<div class="sidebar-section">
@@ -54,14 +113,23 @@
54113
<div class="output-tabs">
55114
<div class="tab active" data-output="preview">Preview</div>
56115
<div class="tab" data-output="console">Console</div>
116+
<div class="tab" data-output="terminal">Terminal</div>
57117
</div>
58118
<div class="output-content">
119+
<button class="preview-open-btn" id="preview-open-btn" title="Open preview in new tab"><i class="fas fa-external-link-alt"></i></button>
59120
<iframe id="preview" class="preview active"></iframe>
60121
<div id="console" class="console"></div>
122+
<div id="terminal" class="terminal-panel"></div>
61123
</div>
62124
</div>
63125
</div>
64126
</div>
127+
<div class="status-bar" id="status-bar">
128+
<span class="cursor-position" id="cursor-position">Ln 1, Col 1</span>
129+
<span class="status-bar-right">
130+
<span class="file-encoding">UTF-8</span>
131+
</span>
132+
</div>
65133
<script type="module" src="./src/main.ts"></script>
66134
</body>
67135
</html>

Build/package-lock.json

Lines changed: 46 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Build/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"@codemirror/lang-css": "^6.3.1",
3232
"@codemirror/lang-html": "^6.4.11",
3333
"@codemirror/lang-javascript": "^6.2.5",
34+
"@codemirror/lang-markdown": "^6.5.0",
3435
"@codemirror/lint": "^6.9.6",
3536
"@codemirror/state": "^6.6.0",
3637
"@codemirror/view": "^6.43.0",
@@ -51,7 +52,9 @@
5152
"jszip": "^3.10.1",
5253
"prettier": "^3.8.3",
5354
"prettier-plugin-css-order": "^2.2.0",
54-
"split.js": "^1.6.5"
55+
"split.js": "^1.6.5",
56+
"xterm": "^5.3.0",
57+
"xterm-addon-fit": "^0.8.0"
5558
},
5659
"devDependencies": {
5760
"@eslint/css": "^1.2.0",

Build/src/appState.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ const logFiltersInitial = Array.isArray(persistedState?.logFilters)
4343
? (persistedState!.logFilters as string[])
4444
: ["log", "error", "warn", "info"];
4545

46-
const ALLOWED_OUTPUTS = ["preview", "console"] as const;
46+
const ALLOWED_OUTPUTS = ["preview", "console", "terminal"] as const;
4747

4848
let _lastPersistedSnapshot = "";
4949
let _idleHandle: number | null = null;
@@ -73,19 +73,13 @@ effect(() => {
7373
export const autoRunText = signal(path("htmlrunner", "ui", "autoRunText"), autoRunState.get() ? "On" : "Off");
7474
effect(() => autoRunText.set(autoRunState.get() ? "On" : "Off"));
7575

76-
export const activeFileContent = signal(path("htmlrunner", "editor", "activeFileContent"), "");
77-
effect(() => {
78-
const files = filesState.get();
79-
const activeId = activeFileState.get();
80-
const file = files.find((f) => f.id === activeId);
81-
activeFileContent.set(file?.content ?? "");
82-
});
83-
8476
export const previewClass = signal(path("htmlrunner", "ui", "previewClass"), activeOutputState.get() === "preview" ? "tab active" : "tab");
8577
export const consoleClass = signal(path("htmlrunner", "ui", "consoleClass"), activeOutputState.get() === "console" ? "tab active" : "tab");
78+
export const terminalClass = signal(path("htmlrunner", "ui", "terminalClass"), activeOutputState.get() === "terminal" ? "tab active" : "tab");
8679
effect(() => {
8780
previewClass.set(activeOutputState.get() === "preview" ? "tab active" : "tab");
8881
consoleClass.set(activeOutputState.get() === "console" ? "tab active" : "tab");
82+
terminalClass.set(activeOutputState.get() === "terminal" ? "tab active" : "tab");
8983
});
9084

9185
export const bodyClass = signal(path("htmlrunner", "ui", "bodyClass"), darkModeState.get() ? "dark-mode" : "");
@@ -101,6 +95,8 @@ effect(() => {
10195

10296
export const globalActions = signal(path("htmlrunner", "global", "actions"), {} as Actions);
10397

98+
export const cursorPos = signal(path("htmlrunner", "ui", "cursorPos"), { line: 1, col: 1 });
99+
104100
effect(() => {
105101
if (!stateHydrated.get()) return;
106102

Build/src/biome.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,14 @@ export async function initBiome(): Promise<void> {
4848
recommended: true,
4949
},
5050
},
51+
files: {
52+
ignoreUnknown: true,
53+
},
54+
vcs: {
55+
enabled: false,
56+
},
5157
},
52-
workspaceDirectory: "./",
58+
workspaceDirectory: "",
5359
});
5460

5561
initialized = true;
@@ -64,7 +70,7 @@ export async function lintWithBiome(text: string, filepath = "file.ts"): Promise
6470
throw new Error("Biome workspace not initialized");
6571
}
6672

67-
workspace.openFile({
73+
await workspace.openFile({
6874
projectKey,
6975
path: filepath,
7076
content: { type: "fromClient", content: text, version: 0 },

Build/src/console.ts

Lines changed: 27 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ function renderConsoleEntries(entries: ConsoleMessage[]): void {
3838
});
3939
}
4040

41-
// stack rendering (same logic as before)
42-
const maybeStack = ev.data[1];
41+
const maybeStack = ev.data.length > 1 ? ev.data[1] : undefined;
4342
if (
4443
maybeStack &&
4544
typeof maybeStack === "object" &&
@@ -111,24 +110,29 @@ effect(() => {
111110
renderConsoleEntries(entries);
112111
});
113112

114-
// Console interceptor code that will be injected into the preview iframe
115113
export const consoleInterceptor = `
116-
const originalConsole = { log: console.log, error: console.error, warn: console.warn, info: console.info };
117-
function sendToConsole(level, ...args) {
118-
window.parent.postMessage({ type: 'console', level, data: args, timestamp: new Date().toISOString() }, '*');
119-
originalConsole[level].apply(console, args);
120-
}
121-
console.log = (...args) => sendToConsole('log', ...args);
122-
console.error = (...args) => sendToConsole('error', ...args);
123-
console.warn = (...args) => sendToConsole('warn', ...args);
124-
console.info = (...args) => sendToConsole('info', ...args);
125-
window.onerror = (message, source, lineno, colno, error) => {
126-
sendToConsole('error', error || message, { stack: error?.stack });
127-
return true;
128-
};
129-
window.onunhandledrejection = (event) => {
130-
sendToConsole('error', event.reason, { stack: event.reason?.stack });
131-
};
114+
const _orig={log:console.log,error:console.error,warn:console.warn,info:console.info};
115+
function _send(l,...a){
116+
try{window.parent.postMessage({type:'console',level:l,data:a,timestamp:new Date().toISOString()},location.origin)}catch{}
117+
_orig[l].apply(console,a)
118+
}
119+
console.log=(...a)=>_send('log',...a);
120+
console.error=(...a)=>_send('error',...a);
121+
console.warn=(...a)=>_send('warn',...a);
122+
console.info=(...a)=>_send('info',...a);
123+
window.onerror=(m,s,l,c,e)=>{_send('error',e||m,{stack:e?.stack});return true};
124+
window.onunhandledrejection=e=>{_send('error',e.reason,{stack:e.reason?.stack})};
125+
`;
126+
127+
export const navigationInterceptor = `
128+
document.addEventListener('click', function(e) {
129+
var link = e.target.closest('a');
130+
if (!link || link.getAttribute('target') === '_blank') return;
131+
var href = link.getAttribute('href');
132+
if (!href || href.startsWith('#') || href.startsWith('http://') || href.startsWith('https://') || href.startsWith('mailto:')) return;
133+
e.preventDefault();
134+
window.parent.postMessage({type:'navigate',href:href},location.origin);
135+
});
132136
`;
133137

134138
// Initialize console message handler
@@ -140,11 +144,10 @@ export function initializeConsole(): void {
140144
window.addEventListener("message", handleConsoleMessage);
141145
}
142146
function handleConsoleMessage(event: MessageEvent<ConsoleMessage>): void {
143-
if (event.data.type === "console") {
144-
// Append to signal list
145-
const prev = consoleEntries.get();
146-
consoleEntries.set([...prev, event.data]);
147-
}
147+
if (event.origin !== location.origin) return;
148+
if (event.data?.type !== "console") return;
149+
const prev = consoleEntries.get();
150+
consoleEntries.set([...prev, event.data]);
148151
}
149152

150153
function renderObject(
@@ -177,16 +180,6 @@ function renderObject(
177180
visited.add(asObj as object);
178181

179182
const container = document.createElement("span");
180-
if (obj instanceof Error) {
181-
const errorEl = document.createElement("span");
182-
errorEl.className = "console-error";
183-
errorEl.textContent = `${obj.name}: ${obj.message}`;
184-
if (obj.stack)
185-
errorEl.textContent += `\n${obj.stack.split("\n").slice(1).join("\n")}`;
186-
container.appendChild(errorEl);
187-
return container;
188-
}
189-
190183
const preview = document.createElement("span");
191184
preview.className = "console-object";
192185
preview.textContent = Array.isArray(asObj)

0 commit comments

Comments
 (0)