From b411ce7013af9e5fada03ed9794991b58257f6f0 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Tue, 30 Sep 2025 23:17:45 -0700 Subject: [PATCH] choppin --- src/css/main.css | 8 +++---- src/js/drop.ts | 25 ++++++++++++++++++++++ src/js/main.ts | 2 ++ src/server.tsx | 2 +- src/shell.ts | 54 ++++++++++++++++++------------------------------ 5 files changed, 51 insertions(+), 40 deletions(-) create mode 100644 src/js/drop.ts diff --git a/src/css/main.css b/src/css/main.css index 7c60067..7330afb 100644 --- a/src/css/main.css +++ b/src/css/main.css @@ -140,11 +140,9 @@ input[type="file"]::file-selector-button { cursor: pointer; } -input[type="file"] { - color: var(--c64-dark-blue); -} - -input[type="submit"] { +input[type="file"], +input[type="submit"], +button { color: var(--c64-dark-blue); padding: 5px; } \ No newline at end of file diff --git a/src/js/drop.ts b/src/js/drop.ts new file mode 100644 index 0000000..bd2c489 --- /dev/null +++ b/src/js/drop.ts @@ -0,0 +1,25 @@ +export function initDrop() { + ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { + document.body.addEventListener(eventName, preventDefaults, false); + }) + + document.body.addEventListener("drop", handleDrop) +} + +function preventDefaults(e: Event) { + e.preventDefault() + e.stopPropagation() +} + +function handleDrop(e: DragEvent) { + const fileInput = document.querySelector("input[type=file]") as HTMLInputElement + + const files = e.dataTransfer?.files ?? [] + + if (files.length > 0) { + const dt = new DataTransfer() + Array.from(files).forEach(f => dt.items.add(f)) + fileInput.files = dt.files + fileInput.dispatchEvent(new Event('change', { bubbles: true })) + } +} \ No newline at end of file diff --git a/src/js/main.ts b/src/js/main.ts index feb6711..c6adf71 100644 --- a/src/js/main.ts +++ b/src/js/main.ts @@ -1,5 +1,6 @@ import { initCompletion } from "./completion.js" import { initCursor } from "./cursor.js" +import { initDrop } from "./drop.js" import { initEditor } from "./editor.js" import { initFocus } from "./focus.js" import { initForm } from "./form.js" @@ -14,6 +15,7 @@ import { startConnection } from "./websocket.js" initCompletion() initCursor() +initDrop() initFocus() initForm() initEditor() diff --git a/src/server.tsx b/src/server.tsx index b8a9f74..568bc4b 100644 --- a/src/server.tsx +++ b/src/server.tsx @@ -100,7 +100,7 @@ app.on(["GET", "POST"], ["/cmd/:name"], async c => { if (!mod || !mod[method]) return c.json({ status: "error", output: `No ${method} export in ${cmd}` }, 500) - return c.json(await runCommandFn(sessionId, async () => mod[method](c))) + return c.json(await runCommandFn({ sessionId }, async () => mod[method](c))) } catch (e: any) { return c.json({ status: "error", output: e.message || e.toString() }, 500) } diff --git a/src/shell.ts b/src/shell.ts index 0122295..1512a15 100644 --- a/src/shell.ts +++ b/src/shell.ts @@ -15,66 +15,52 @@ export async function runCommand(sessionId: string, taskId: string, input: strin if (!commandExists(cmd)) return { status: "error", output: `${cmd} not found` } - let status: "ok" | "error" = "ok" - let output: CommandOutput = "" - const state = getState(sessionId, taskId, ws) + return runCommandFn({ sessionId, taskId, ws }, async () => exec(cmd, args)) - try { - [status, output] = await ALS.run(state, async () => await exec(cmd, args)) - } catch (err) { - status = "error" - output = errorMessage(err) - } - - return { status, output } } -export async function runCommandFn(sessionId: string, fn: () => Promise) { - let status: "ok" | "error" = "ok" - let output: CommandOutput = "" - const state = getState(sessionId) - +export async function runCommandFn( + { sessionId, taskId, ws }: { sessionId: string, taskId?: string, ws?: any }, + fn: () => Promise +): Promise { try { - const execOutput = await ALS.run(state, async () => await fn()) - ;[status, output] = processExecOutput(execOutput) + const state = getState(sessionId, taskId, ws) + return processExecOutput(await ALS.run(state, async () => fn())) } catch (err) { - status = "error" - output = errorMessage(err) + return { status: "error", output: errorMessage(err) } } - - return { status, output } } -async function exec(cmd: string, args: string[]): Promise<["ok" | "error", CommandOutput]> { +async function exec(cmd: string, args: string[]): Promise { const module = await loadCommandModule(cmd) if (module?.game) - return ["ok", { game: cmd }] + return { status: "ok", output: { game: cmd } } if (!module || !module.default) - return ["error", `${cmd} has no default export`] + return { status: "error", output: `${cmd} has no default export` } - return processExecOutput(await module.default(...args)) + return await module.default(...args) } -export function processExecOutput(output: string | any): ["ok" | "error", CommandOutput] { +export function processExecOutput(output: string | any): CommandResult { if (typeof output === "string") { - return ["ok", output] + return { status: "ok", output } } else if (typeof output === "object") { if (output.error) { - return ["error", output.error] + return { status: "error", output: output.error } } else if (isJSX(output)) { - return ["ok", { html: output.toString() }] + return { status: "ok", output: { html: output.toString() } } } else if (output.html && isJSX(output.html)) { output.html = output.html.toString() - return ["ok", output] + return { status: "ok", output } } else { - return ["ok", output] + return { status: "ok", output } } } else if (output === undefined) { - return ["ok", ""] + return { status: "ok", output: "" } } else { - return ["ok", String(output)] + return { status: "ok", output: String(output) } } }