diff --git a/app/src/js/game.ts b/app/src/js/game.ts
new file mode 100644
index 0000000..f6938ff
--- /dev/null
+++ b/app/src/js/game.ts
@@ -0,0 +1,56 @@
+import { type Message, Context } from "../shared/types.js"
+import { focusInput } from "./focus.js"
+import { $ } from "./dom.js"
+import { randomId } from "../shared/utils.js"
+import { setStatus, addOutput } from "./scrollback.js"
+import { browserCommands } from "./commands.js"
+
+let oldMode = "cinema"
+
+export async function handleGameStart(msg: Message) {
+ const msgId = msg.id as string
+ const name = msg.data as string
+ const game = await import(`/command/${name}`)
+ const id = randomId()
+ let stopGame = false
+
+ addOutput(msgId, { html: `` })
+
+ if (document.body.dataset.mode === "tall") {
+ browserCommands.mode?.()
+ oldMode = "tall"
+ }
+
+ setStatus(msgId, "ok")
+
+ const canvas = $(id) as HTMLCanvasElement
+ const ctx = new Context(canvas.getContext("2d")!)
+ canvas.focus()
+ canvas.addEventListener("keydown", e => {
+ e.preventDefault()
+
+ if (e.key === "Escape" || (e.ctrlKey && e.key === "c")) {
+ stopGame = true
+ if (oldMode === "tall") browserCommands.mode?.()
+ canvas.classList.remove("active")
+ canvas.style.height = canvas.height / 2 + "px"
+ canvas.style.width = canvas.width / 2 + "px"
+ focusInput()
+ }
+ })
+
+ let last = 0
+ function loop(ts: number) {
+ if (stopGame) return
+
+ const delta = ts - last
+ if (delta >= 1000 / 30) {
+ if (game.update) game.update(delta)
+ if (game.draw) game.draw(ctx)
+ last = ts
+ }
+ requestAnimationFrame(loop)
+ }
+ requestAnimationFrame(loop)
+}
+
diff --git a/app/src/js/shell.ts b/app/src/js/shell.ts
index 7bcd808..ceda0ff 100644
--- a/app/src/js/shell.ts
+++ b/app/src/js/shell.ts
@@ -2,14 +2,12 @@
// The shell runs on the server and processes input, returning output.
import type { Message, CommandResult, CommandOutput } from "../shared/types.js"
-import { Context } from "../shared/types.js"
import { addInput, setStatus, addOutput, appendOutput, replaceOutput } from "./scrollback.js"
import { send } from "./websocket.js"
import { randomId } from "../shared/utils.js"
import { addToHistory } from "./history.js"
-import { focusInput } from "./focus.js"
import { browserCommands, cacheCommands } from "./commands.js"
-import { $ } from "./dom.js"
+import { handleGameStart } from "./game.js"
export function runCommand(input: string) {
if (!input.trim()) return
@@ -84,52 +82,3 @@ function handleStreamReplace(msg: Message) {
function handleStreamEnd(_msg: Message) {
}
-let oldMode = "cinema"
-
-async function handleGameStart(msg: Message) {
- const msgId = msg.id as string
- const name = msg.data as string
- const game = await import(`/command/${name}`)
- const id = randomId()
- let stopGame = false
-
- addOutput(msgId, { html: `` })
-
- if (document.body.dataset.mode === "tall") {
- browserCommands.mode?.()
- oldMode = "tall"
- }
-
- setStatus(msgId, "ok")
-
- const canvas = $(id) as HTMLCanvasElement
- const ctx = new Context(canvas.getContext("2d")!)
- canvas.focus()
- canvas.addEventListener("keydown", e => {
- e.preventDefault()
-
- if (e.key === "Escape" || (e.ctrlKey && e.key === "c")) {
- stopGame = true
- if (oldMode === "tall") browserCommands.mode?.()
- canvas.classList.remove("active")
- canvas.style.height = canvas.height / 2 + "px"
- canvas.style.width = canvas.width / 2 + "px"
- focusInput()
- }
- })
-
- let last = 0
- function loop(ts: number) {
- if (stopGame) return
-
- const delta = ts - last
- if (delta >= 1000 / 30) {
- if (game.update) game.update(delta)
- if (game.draw) game.draw(ctx)
- last = ts
- }
- requestAnimationFrame(loop)
- }
- requestAnimationFrame(loop)
-}
-