From d80735d120118819dc19da1819ce42ad4b5b28a6 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath <2+defunkt@users.noreply.github.com> Date: Sat, 20 Sep 2025 11:27:12 -0700 Subject: [PATCH] cli wip --- src/css/terminal.css | 10 ++++++---- src/js/dom.ts | 7 ++++++- src/js/focus.ts | 4 ++++ src/js/input.ts | 28 ++++++++++++++++++++++++++++ src/js/main.ts | 2 ++ src/js/resize.ts | 4 ++++ src/js/vram.ts | 4 ++-- src/js/websocket.ts | 10 ++++++++-- src/server.tsx | 8 ++++++-- src/shared/message.ts | 6 ++++++ 10 files changed, 72 insertions(+), 11 deletions(-) create mode 100644 src/js/input.ts create mode 100644 src/shared/message.ts diff --git a/src/css/terminal.css b/src/css/terminal.css index 5ab8215..514d8be 100644 --- a/src/css/terminal.css +++ b/src/css/terminal.css @@ -13,6 +13,10 @@ font-size: var(--cli-font-size); } +#command-line[data-extended="true"] { + border: 1px solid red; +} + #command-prompt { position: absolute; top: 10px; @@ -24,14 +28,13 @@ background: transparent; padding: var(--cli-spacing-vertical) var(--cli-element-spacing-horizontal); - min-height: 1.2em; resize: none; overflow: hidden; font: inherit; letter-spacing: inherit; line-height: inherit; - height: var(--cli-height); + min-height: var(--cli-height); width: 100%; box-shadow: none; box-sizing: border-box; @@ -54,13 +57,12 @@ background: transparent; resize: none; overflow: hidden; - min-height: 1.2em; height: auto; font: inherit; letter-spacing: inherit; line-height: inherit; - height: var(--cli-height); + min-height: var(--cli-height); width: 100%; box-shadow: none; box-sizing: border-box; diff --git a/src/js/dom.ts b/src/js/dom.ts index 7686191..7e0a01a 100644 --- a/src/js/dom.ts +++ b/src/js/dom.ts @@ -1,3 +1,7 @@ +//// +// dom helpers and cached elements +// + // finds an element by ID export const $ = (id: string): HTMLElement | null => document.getElementById(id) @@ -10,4 +14,5 @@ export const $$ = (tag: string, innerHTML = ""): HTMLElement => { } // elements we know will be there... right? -export const cmdTextbox = document.getElementById("command-textbox")! \ No newline at end of file +export const cmdLine = document.getElementById("command-line") as HTMLDivElement +export const cmdTextbox = document.getElementById("command-textbox") as HTMLTextAreaElement \ No newline at end of file diff --git a/src/js/focus.ts b/src/js/focus.ts index 1c586b2..c8ce8d1 100644 --- a/src/js/focus.ts +++ b/src/js/focus.ts @@ -1,3 +1,7 @@ +//// +// try to keep the command textbox focused at all times +// + import { cmdTextbox } from "./dom.js" export function initFocus() { diff --git a/src/js/input.ts b/src/js/input.ts new file mode 100644 index 0000000..d94e1c0 --- /dev/null +++ b/src/js/input.ts @@ -0,0 +1,28 @@ +//// +// this file controls the command textbox +// + +import { cmdTextbox, cmdLine } from "./dom.js" + +export function initInput() { + cmdTextbox.addEventListener("keydown", inputHandler) +} + +function inputHandler(event: KeyboardEvent) { + const target = event.target as HTMLElement + if (target?.id !== cmdTextbox.id) return + + console.log(event.key) + if (event.key === "Tab") { + event.preventDefault() + } else if (event.shiftKey && event.key === "Enter") { + cmdTextbox.rows += 1 + cmdLine.dataset.extended = "true" + console.log(cmdTextbox.value) + } else if (event.key === "Enter") { + cmdTextbox.value = "" + cmdTextbox.rows = 1 + cmdLine.dataset.extended = "false" + event.preventDefault() + } +} \ No newline at end of file diff --git a/src/js/main.ts b/src/js/main.ts index 2488457..227dbe2 100644 --- a/src/js/main.ts +++ b/src/js/main.ts @@ -1,10 +1,12 @@ import { initResize } from "./resize.js" +import { initInput } from "./input.js" import { initFocus } from "./focus.js" import { startVramCounter } from "./vram.js" import { startConnection } from "./websocket.js" initFocus() initResize() +initInput() startConnection() startVramCounter() \ No newline at end of file diff --git a/src/js/resize.ts b/src/js/resize.ts index 53f1e72..61b78fd 100644 --- a/src/js/resize.ts +++ b/src/js/resize.ts @@ -1,3 +1,7 @@ +//// +// maintain the two video modes: cinema and tall +// + const content = document.getElementById("content")! export function initResize() { diff --git a/src/js/vram.ts b/src/js/vram.ts index 1b0a539..3c52840 100644 --- a/src/js/vram.ts +++ b/src/js/vram.ts @@ -1,5 +1,5 @@ -// -// vram +//// +// fun vram counter at startup // import { $ } from "./dom.js" diff --git a/src/js/websocket.ts b/src/js/websocket.ts index 4f75eee..6ee9e75 100644 --- a/src/js/websocket.ts +++ b/src/js/websocket.ts @@ -1,3 +1,9 @@ +//// +// the terminal communicates with the shell via websockets +// + +import type { Message } from "../shared/message.js" + let ws: WebSocket | null = null // open our websocket connection @@ -13,8 +19,8 @@ export function startConnection() { } // send any message -export function send(obj: any) { - ws?.readyState === 1 && ws.send(JSON.stringify(obj)) +export function send(msg: Message) { + ws?.readyState === 1 && ws.send(JSON.stringify(msg)) } // close it... plz don't do this, though diff --git a/src/server.tsx b/src/server.tsx index 81e85fa..bc13609 100644 --- a/src/server.tsx +++ b/src/server.tsx @@ -30,8 +30,12 @@ app.use("*", async (c, next) => { console.log(fn(`${c.res.status} ${c.req.method} ${c.req.url} (${end - start}ms)`)) }) -app.get("/js/:path{.+}", async c => { - const path = "./src/js/" + c.req.param("path") +app.on("GET", ["/js/:path{.+}", "/shared/:path{.+}"], async c => { + const path = "./src/" + c.req.path.replace("..", ".") + + // path must end in .js + if (!path.endsWith(".js")) return c.text("File not found", 404) + const ts = path.replace(".js", ".ts") if (isFile(ts)) { return new Response(await transpile(ts), { headers: { "Content-Type": "text/javascript" } }) diff --git a/src/shared/message.ts b/src/shared/message.ts new file mode 100644 index 0000000..f8cfbd4 --- /dev/null +++ b/src/shared/message.ts @@ -0,0 +1,6 @@ +export type Message = { + session: string + id: string + type: "output" + data: any +}