remember your "mode"

This commit is contained in:
Chris Wanstrath 2025-10-01 19:17:39 -07:00
parent 07840aabd8
commit 89d850b55f
10 changed files with 33 additions and 13 deletions

View File

@ -79,7 +79,7 @@ https://wakamaifondue.com/
- [x] public tunnel for your NOSE webapps - [x] public tunnel for your NOSE webapps
- [x] public tunnel lives through reboots - [x] public tunnel lives through reboots
- [ ] tunnel to the terminal - [ ] tunnel to the terminal
- [ ] remember your "mode" - [x] remember your "mode"
- [ ] `nose` CLI - [ ] `nose` CLI
- [ ] status bar on terminal UX - [ ] status bar on terminal UX
- [ ] "project"-based rehaul - [ ] "project"-based rehaul

View File

@ -5,6 +5,7 @@ import { basename } from "path"
import type { Message } from "./shared/types" import type { Message } from "./shared/types"
import { runCommand } from "./shell" import { runCommand } from "./shell"
import { send } from "./websocket" import { send } from "./websocket"
import { setState } from "./state"
export async function dispatchMessage(ws: any, msg: Message) { export async function dispatchMessage(ws: any, msg: Message) {
switch (msg.type) { switch (msg.type) {
@ -14,6 +15,9 @@ export async function dispatchMessage(ws: any, msg: Message) {
case "save-file": case "save-file":
await saveFileMessage(ws, msg); break await saveFileMessage(ws, msg); break
case "ui:mode":
setState("ui:mode", msg.data); break
default: default:
send(ws, { type: "error", data: `unknown message: ${msg.type}` }) send(ws, { type: "error", data: `unknown message: ${msg.type}` })
} }

View File

@ -17,7 +17,7 @@ export const Layout: FC = async ({ children, title }) => (
</head> </head>
<body data-mode="tall"> <body data-mode="tall">
<main> <main>
<div id="content"> <div id="content" style="display:none">
{children} {children}
</div> </div>
</main> </main>

View File

@ -1,20 +1,27 @@
//// ////
// temporary hack for browser commands // temporary hack for browser commands
import { scrollback } from "./dom.js" import { scrollback, content } from "./dom.js"
import { resize } from "./resize.js" import { resize } from "./resize.js"
import { autoScroll } from "./scrollback.js" import { autoScroll } from "./scrollback.js"
import { sessionId } from "./session.js" import { sessionId } from "./session.js"
import { send } from "./websocket.js"
export const commands: string[] = [] export const commands: string[] = []
export const browserCommands: Record<string, () => any> = { export const browserCommands: Record<string, (...args: string[]) => void> = {
"browser-session": () => sessionId, "browser-session": () => sessionId,
clear: () => scrollback.innerHTML = "", clear: () => scrollback.innerHTML = "",
commands: () => commands.join(" "), commands: () => commands.join(" "),
fullscreen: () => document.body.requestFullscreen(), fullscreen: () => document.body.requestFullscreen(),
mode: () => { mode: (mode?: string) => {
document.body.dataset.mode = document.body.dataset.mode === "tall" ? "cinema" : "tall" 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() resize()
autoScroll() autoScroll()
}, },

View File

@ -1,8 +1,9 @@
import type { Message } from "@/shared/types"; import type { Message } from "@/shared/types"
import { cacheCommands } from "./commands"; import { cacheCommands } from "./commands"
import { handleOutput } from "./scrollback" import { handleOutput } from "./scrollback"
import { handleStreamStart, handleStreamAppend, handleStreamReplace, handleStreamEnd } from "./stream"; import { handleStreamStart, handleStreamAppend, handleStreamReplace, handleStreamEnd } from "./stream"
import { handleGameStart } from "./game"; import { handleGameStart } from "./game"
import { browserCommands } from "./commands"
// message received from server // message received from server
export async function dispatchMessage(msg: Message) { export async function dispatchMessage(msg: Message) {
@ -23,6 +24,8 @@ export async function dispatchMessage(msg: Message) {
handleStreamReplace(msg); break handleStreamReplace(msg); break
case "game:start": case "game:start":
await handleGameStart(msg); break await handleGameStart(msg); break
case "ui:mode":
browserCommands.mode?.(msg.data as string); break
default: default:
console.error("unknown message type", msg) console.error("unknown message type", msg)
} }

View File

@ -2,6 +2,7 @@
// DOM helpers and cached elements // DOM helpers and cached elements
// elements we know will be there... right? // elements we know will be there... right?
export const content = $("content") as HTMLDivElement
export const cmdLine = $("command-line") as HTMLDivElement export const cmdLine = $("command-line") as HTMLDivElement
export const cmdInput = $("command-textbox") as HTMLTextAreaElement export const cmdInput = $("command-textbox") as HTMLTextAreaElement
export const scrollback = $("scrollback") as HTMLUListElement export const scrollback = $("scrollback") as HTMLUListElement

View File

@ -5,7 +5,7 @@ import { cmdInput } from "./dom.js"
export function initFocus() { export function initFocus() {
window.addEventListener("click", focusHandler) window.addEventListener("click", focusHandler)
focusInput() setTimeout(() => focusInput(), 10)
} }
export function focusInput() { export function focusInput() {

View File

@ -20,10 +20,10 @@ export function runCommand(input: string) {
addToHistory(input) addToHistory(input)
addInput(id, input) addInput(id, input)
const [cmd = "", ..._args] = input.split(" ") const [cmd = "", ...args] = input.split(" ")
if (browserCommands[cmd]) { if (browserCommands[cmd]) {
const result = browserCommands[cmd]() const result = browserCommands[cmd](...args)
if (typeof result === "string") if (typeof result === "string")
addOutput(id, result) addOutput(id, result)
setStatus(id, "ok") setStatus(id, "ok")

View File

@ -16,6 +16,7 @@ import { send, addWebsocket, removeWebsocket, closeWebsockets } from "./websocke
import { initSneakers, disconnectSneakers } from "./sneaker" import { initSneakers, disconnectSneakers } from "./sneaker"
import { dispatchMessage } from "./dispatch" import { dispatchMessage } from "./dispatch"
import { fatal } from "./fatal" import { fatal } from "./fatal"
import { getState } from "./state"
import { Layout } from "./html/layout" import { Layout } from "./html/layout"
import { Terminal } from "./html/terminal" import { Terminal } from "./html/terminal"
@ -131,6 +132,9 @@ app.get("/ws", upgradeWebSocket(async c => {
async onOpen(_e, ws) { async onOpen(_e, ws) {
addWebsocket(ws) addWebsocket(ws)
send(ws, { type: "commands", data: await commands() }) 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) { async onMessage(event, ws) {
let data: Message | undefined let data: Message | undefined

View File

@ -8,6 +8,7 @@ export type Message = {
export type MessageType = "error" | "input" | "output" | "commands" | "save-file" export type MessageType = "error" | "input" | "output" | "commands" | "save-file"
| "game:start" | "game:start"
| "stream:start" | "stream:end" | "stream:append" | "stream:replace" | "stream:start" | "stream:end" | "stream:append" | "stream:replace"
| "ui:mode"
export type CommandOutput = string | string[] export type CommandOutput = string | string[]
| { text: string, script?: string } | { text: string, script?: string }