From 89d850b55f400ac477e5251a2d276d5faf525ad5 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Wed, 1 Oct 2025 19:17:39 -0700 Subject: [PATCH] remember your "mode" --- README.md | 2 +- src/dispatch.ts | 4 ++++ src/html/layout.tsx | 2 +- src/js/commands.ts | 15 +++++++++++---- src/js/dispatch.ts | 11 +++++++---- src/js/dom.ts | 1 + src/js/focus.ts | 2 +- src/js/shell.ts | 4 ++-- src/server.tsx | 4 ++++ src/shared/types.ts | 1 + 10 files changed, 33 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index f01cca6..21d2a96 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ https://wakamaifondue.com/ - [x] public tunnel for your NOSE webapps - [x] public tunnel lives through reboots - [ ] tunnel to the terminal -- [ ] remember your "mode" +- [x] remember your "mode" - [ ] `nose` CLI - [ ] status bar on terminal UX - [ ] "project"-based rehaul diff --git a/src/dispatch.ts b/src/dispatch.ts index 8db3e64..cd41198 100644 --- a/src/dispatch.ts +++ b/src/dispatch.ts @@ -5,6 +5,7 @@ import { basename } from "path" import type { Message } from "./shared/types" import { runCommand } from "./shell" import { send } from "./websocket" +import { setState } from "./state" export async function dispatchMessage(ws: any, msg: Message) { switch (msg.type) { @@ -14,6 +15,9 @@ export async function dispatchMessage(ws: any, msg: Message) { case "save-file": await saveFileMessage(ws, msg); break + case "ui:mode": + setState("ui:mode", msg.data); break + default: send(ws, { type: "error", data: `unknown message: ${msg.type}` }) } diff --git a/src/html/layout.tsx b/src/html/layout.tsx index 9ef2abd..5401fbb 100644 --- a/src/html/layout.tsx +++ b/src/html/layout.tsx @@ -17,7 +17,7 @@ export const Layout: FC = async ({ children, title }) => (
-
+
diff --git a/src/js/commands.ts b/src/js/commands.ts index 2e79dab..daf5f32 100644 --- a/src/js/commands.ts +++ b/src/js/commands.ts @@ -1,20 +1,27 @@ //// // temporary hack for browser commands -import { scrollback } from "./dom.js" +import { scrollback, content } from "./dom.js" import { resize } from "./resize.js" import { autoScroll } from "./scrollback.js" import { sessionId } from "./session.js" +import { send } from "./websocket.js" export const commands: string[] = [] -export const browserCommands: Record any> = { +export const browserCommands: Record void> = { "browser-session": () => sessionId, clear: () => scrollback.innerHTML = "", commands: () => commands.join(" "), fullscreen: () => document.body.requestFullscreen(), - mode: () => { - document.body.dataset.mode = document.body.dataset.mode === "tall" ? "cinema" : "tall" + mode: (mode?: string) => { + if (!mode) { + mode = document.body.dataset.mode === "tall" ? "cinema" : "tall" + send({ type: "ui:mode", data: mode }) + } + + content.style.display = "" + document.body.dataset.mode = mode resize() autoScroll() }, diff --git a/src/js/dispatch.ts b/src/js/dispatch.ts index 0b23e33..477251f 100644 --- a/src/js/dispatch.ts +++ b/src/js/dispatch.ts @@ -1,8 +1,9 @@ -import type { Message } from "@/shared/types"; -import { cacheCommands } from "./commands"; +import type { Message } from "@/shared/types" +import { cacheCommands } from "./commands" import { handleOutput } from "./scrollback" -import { handleStreamStart, handleStreamAppend, handleStreamReplace, handleStreamEnd } from "./stream"; -import { handleGameStart } from "./game"; +import { handleStreamStart, handleStreamAppend, handleStreamReplace, handleStreamEnd } from "./stream" +import { handleGameStart } from "./game" +import { browserCommands } from "./commands" // message received from server export async function dispatchMessage(msg: Message) { @@ -23,6 +24,8 @@ export async function dispatchMessage(msg: Message) { handleStreamReplace(msg); break case "game:start": await handleGameStart(msg); break + case "ui:mode": + browserCommands.mode?.(msg.data as string); break default: console.error("unknown message type", msg) } diff --git a/src/js/dom.ts b/src/js/dom.ts index b2fc711..80db68f 100644 --- a/src/js/dom.ts +++ b/src/js/dom.ts @@ -2,6 +2,7 @@ // DOM helpers and cached elements // elements we know will be there... right? +export const content = $("content") as HTMLDivElement export const cmdLine = $("command-line") as HTMLDivElement export const cmdInput = $("command-textbox") as HTMLTextAreaElement export const scrollback = $("scrollback") as HTMLUListElement diff --git a/src/js/focus.ts b/src/js/focus.ts index e4a6833..14ab2ef 100644 --- a/src/js/focus.ts +++ b/src/js/focus.ts @@ -5,7 +5,7 @@ import { cmdInput } from "./dom.js" export function initFocus() { window.addEventListener("click", focusHandler) - focusInput() + setTimeout(() => focusInput(), 10) } export function focusInput() { diff --git a/src/js/shell.ts b/src/js/shell.ts index c06958b..8ef7fcc 100644 --- a/src/js/shell.ts +++ b/src/js/shell.ts @@ -20,10 +20,10 @@ export function runCommand(input: string) { addToHistory(input) addInput(id, input) - const [cmd = "", ..._args] = input.split(" ") + const [cmd = "", ...args] = input.split(" ") if (browserCommands[cmd]) { - const result = browserCommands[cmd]() + const result = browserCommands[cmd](...args) if (typeof result === "string") addOutput(id, result) setStatus(id, "ok") diff --git a/src/server.tsx b/src/server.tsx index 6ab10bf..ec6386f 100644 --- a/src/server.tsx +++ b/src/server.tsx @@ -16,6 +16,7 @@ import { send, addWebsocket, removeWebsocket, closeWebsockets } from "./websocke import { initSneakers, disconnectSneakers } from "./sneaker" import { dispatchMessage } from "./dispatch" import { fatal } from "./fatal" +import { getState } from "./state" import { Layout } from "./html/layout" import { Terminal } from "./html/terminal" @@ -131,6 +132,9 @@ app.get("/ws", upgradeWebSocket(async c => { async onOpen(_e, ws) { addWebsocket(ws) send(ws, { type: "commands", data: await commands() }) + + const mode = getState("ui:mode") + if (mode) send(ws, { type: "ui:mode", data: mode }) }, async onMessage(event, ws) { let data: Message | undefined diff --git a/src/shared/types.ts b/src/shared/types.ts index d4c07cc..4129e37 100644 --- a/src/shared/types.ts +++ b/src/shared/types.ts @@ -8,6 +8,7 @@ export type Message = { export type MessageType = "error" | "input" | "output" | "commands" | "save-file" | "game:start" | "stream:start" | "stream:end" | "stream:append" | "stream:replace" + | "ui:mode" export type CommandOutput = string | string[] | { text: string, script?: string }