narrow Message types

This commit is contained in:
Chris Wanstrath 2025-10-03 07:00:25 -07:00
parent 24a17f2b46
commit 2b66107866
10 changed files with 59 additions and 36 deletions

View File

@ -11,13 +11,15 @@ export async function dispatchMessage(ws: any, msg: Message) {
console.log("<- receive", msg)
switch (msg.type) {
case "input":
await inputMessage(ws, msg as InputMessage); break
await inputMessage(ws, msg); break
case "save-file":
await saveFileMessage(ws, msg as SaveFileMessage); break
await saveFileMessage(ws, msg); break
case "ui:mode":
setState("ui:mode", msg.data); break
case "session:update":
for (const key of Object.keys(msg.data))
setState(key, msg.data[key]);
break
default:
send(ws, { type: "error", data: `unknown message: ${msg.type}` })

View File

@ -58,7 +58,6 @@ function closeBrowser() {
iframe.style.pointerEvents = "none"
iframe.tabIndex = -1
iframe.classList.remove("fullscreen", "active")
iframe.style.border = "2px solid var(--c64-light-blue)"
scrollback.append(iframe)
controls.style.display = "none"

View File

@ -22,7 +22,7 @@ export const browserCommands: Record<string, (...args: string[]) => void | Promi
mode: (mode?: string) => {
if (!mode) {
mode = document.body.dataset.mode === "tall" ? "cinema" : "tall"
send({ type: "ui:mode", data: mode })
send({ type: "session:update", data: { "ui:mode": mode } })
}
content.style.display = ""

View File

@ -1,3 +1,4 @@
import type { SaveFileMessage } from "../shared/types"
import { scrollback } from "./dom"
import { send } from "./websocket"
import { focusInput } from "./focus"
@ -49,10 +50,10 @@ function keydownHandler(e: KeyboardEvent) {
} else if ((e.ctrlKey && e.key === "s") || (e.ctrlKey && e.key === "Enter")) {
e.preventDefault()
send({
id: editor.dataset.path,
id: editor.dataset.path || "/tmp.txt",
type: "save-file",
data: editor.value
})
} as SaveFileMessage)
} else if (e.key === "{") {
if (editor.selectionStart !== editor.selectionEnd) {
insertAroundSelection(editor, '{', '}')

View File

@ -133,6 +133,7 @@ function handleInputClick(e: MouseEvent) {
export function handleOutput(msg: Message) {
const result = msg.data as CommandResult
setStatus(msg.id!, result.status)
addOutput(msg.id!, result.output)
const id = "id" in msg ? msg.id || "" : ""
setStatus(id, result.status)
addOutput(id, result.output)
}

View File

@ -1,6 +1,7 @@
////
// The shell runs on the server and processes input, returning output.
import type { InputMessage } from "../shared/types"
import { addInput, setStatus, addOutput } from "./scrollback"
import { send } from "./websocket"
import { randomId } from "../shared/utils"
@ -27,7 +28,7 @@ export async function runCommand(input: string) {
if (result) addOutput(id, result)
setStatus(id, "ok")
} else {
send({ id, type: "input", data: input })
send({ id, type: "input", data: input } as InputMessage)
}
}

View File

@ -1,25 +1,25 @@
import type { Message, CommandOutput } from "@/shared/types"
import type { StreamMessage } from "@/shared/types"
import { addOutput, appendOutput, replaceOutput } from "./scrollback"
export function handleStreamStart(msg: Message) {
const id = msg.id!
export function handleStreamStart(msg: StreamMessage) {
const id = msg.id
const status = document.querySelector(`[data-id="${id}"].input .status`)
if (!status) return
addOutput(id, msg.data as CommandOutput)
addOutput(id, msg.data)
status.classList.remove("yellow")
status.classList.add("purple")
}
export function handleStreamAppend(msg: Message) {
appendOutput(msg.id!, msg.data as CommandOutput)
export function handleStreamAppend(msg: StreamMessage) {
appendOutput(msg.id, msg.data)
}
export function handleStreamReplace(msg: Message) {
replaceOutput(msg.id!, msg.data as CommandOutput)
export function handleStreamReplace(msg: StreamMessage) {
replaceOutput(msg.id, msg.data)
}
export function handleStreamEnd(_msg: Message) {
export function handleStreamEnd(_msg: StreamMessage) {
}

View File

@ -9,7 +9,7 @@ import { addErrorMessage } from "./scrollback"
const MAX_RETRIES = 5
let retries = 0
let connected = false
let msgQueue: Message[] = []
let msgQueue: Omit<Message, "session">[] = []
let ws: WebSocket | null = null
@ -30,14 +30,15 @@ export function startConnection() {
}
// send any message
export function send(msg: Message) {
export function send(msg: Omit<Message, "session">) {
if (!connected) {
msgQueue.push(msg)
startConnection()
return
}
if (!msg.session) msg.session = sessionId
if (!(msg as any).session) (msg as any).session = sessionId
ws?.readyState === 1 && ws.send(JSON.stringify(msg))
console.log("-> send", msg)
}

View File

@ -1,19 +1,13 @@
export type Message = {
session?: string
id?: string
type: MessageType
data?: CommandResult | CommandOutput
}
export type Message =
| ErrorMessage
| InputMessage
| OutputMessage
| SaveFileMessage
| SessionStartMessage
| SessionUpdateMessage
| GameStartMessage
export type MessageType = "error" | "input" | "output" | "commands" | "save-file"
| "game:start"
| "stream:start" | "stream:end" | "stream:append" | "stream:replace"
| "ui:mode"
| StreamMessage
| CommandsMessage
export type CommandOutput = string | string[]
| { text: string, script?: string }
@ -26,6 +20,22 @@ export type CommandResult = {
output: CommandOutput
}
export type ErrorMessage = {
type: "error"
data: string
}
export type CommandsMessage = {
type: "commands"
data: string[]
}
export type OutputMessage = {
type: "output"
id?: string
data: CommandResult
}
export type InputMessage = {
type: "input"
id: string
@ -56,6 +66,14 @@ export type SessionUpdateMessage = {
}
export type GameStartMessage = {
type: "game:start"
id: string
data: string
}
export type StreamMessage = {
type: "stream:start" | "stream:end" | "stream:append" | "stream:replace"
id: string
session: string
data: CommandOutput
}

View File

@ -2,7 +2,7 @@ import { send as sendWs } from "./websocket"
import { sessionGet } from "./session"
import { processExecOutput } from "./shell"
import type { Child } from "hono/jsx"
import type { CommandOutput, Message } from "./shared/types"
import type { CommandOutput, StreamMessage } from "./shared/types"
type StreamFn = (output: Child) => Promise<void>
type StreamFns = { replace: StreamFn, append: StreamFn }
@ -16,7 +16,7 @@ export async function stream(initOrFn: StreamParamFn | string | any, fn?: Stream
const taskId = state.taskId
const session = state.sessionId
const send = (msg: Message) => {
const send = (msg: Omit<StreamMessage, "id" | "session">) => {
sendWs(state.ws, { ...msg, id: taskId, session })
}