Compare commits
No commits in common. "907d8b53933e4cbb9840736c4c632720123de964" and "7ffffebd4ad08b023db6aa794aa6d9d7fdea95f8" have entirely different histories.
907d8b5393
...
7ffffebd4a
|
|
@ -6,11 +6,12 @@ import { join, extname } from "path"
|
||||||
|
|
||||||
import type { CommandOutput } from "@/shared/types"
|
import type { CommandOutput } from "@/shared/types"
|
||||||
import { isBinaryFile } from "@/utils"
|
import { isBinaryFile } from "@/utils"
|
||||||
import { projectDir } from "@/project"
|
import { projectName, projectDir } from "@/project"
|
||||||
import { sessionGet } from "@/session"
|
import { sessionGet } from "@/session"
|
||||||
import { highlight } from "../lib/highlight"
|
import { highlight } from "../lib/highlight"
|
||||||
|
|
||||||
export default async function (path: string) {
|
export default async function (path: string) {
|
||||||
|
const project = projectName()
|
||||||
const root = sessionGet("cwd") || projectDir()
|
const root = sessionGet("cwd") || projectDir()
|
||||||
|
|
||||||
let files: string[] = []
|
let files: string[] = []
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import { projectName, projectDir } from "@/project"
|
||||||
import { sessionGet } from "@/session"
|
import { sessionGet } from "@/session"
|
||||||
|
|
||||||
export default async function (path: string) {
|
export default async function (path: string) {
|
||||||
|
const project = projectName()
|
||||||
const root = sessionGet("cwd") || projectDir()
|
const root = sessionGet("cwd") || projectDir()
|
||||||
|
|
||||||
let files: string[] = []
|
let files: string[] = []
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Look around.
|
// Look around.
|
||||||
|
|
||||||
import { readdirSync } from "fs"
|
import { readdirSync } from "fs"
|
||||||
import { projectDir } from "@/project"
|
import { projectName, projectDir } from "@/project"
|
||||||
import { sessionGet } from "@/session"
|
import { sessionGet } from "@/session"
|
||||||
|
|
||||||
export default function () {
|
export default function () {
|
||||||
|
|
|
||||||
|
|
@ -80,16 +80,6 @@ a:visited {
|
||||||
color: var(--purple);
|
color: var(--purple);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* hide scrollbars, always */
|
|
||||||
* {
|
|
||||||
scrollbar-width: none;
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
*::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
font-family: var(--font-family);
|
font-family: var(--font-family);
|
||||||
|
|
|
||||||
|
|
@ -161,23 +161,3 @@
|
||||||
#scrollback .output {
|
#scrollback .output {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
#statusline {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
padding: 2px 8px;
|
|
||||||
|
|
||||||
background: var(--c64-light-gray);
|
|
||||||
color: var(--c64-dark-blue);
|
|
||||||
|
|
||||||
font-size: 14px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
#statusline a {
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
////
|
////
|
||||||
// Dispatch client->server Messages received via WebSocket
|
// Dispatch Messages received via WebSocket
|
||||||
|
|
||||||
import { basename } from "path"
|
import { basename } from "path"
|
||||||
import type { Message, InputMessage, SaveFileMessage } 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"
|
import { setState } from "./state"
|
||||||
|
|
@ -11,10 +11,10 @@ export async function dispatchMessage(ws: any, msg: Message) {
|
||||||
console.log("<- receive", msg)
|
console.log("<- receive", msg)
|
||||||
switch (msg.type) {
|
switch (msg.type) {
|
||||||
case "input":
|
case "input":
|
||||||
await inputMessage(ws, msg as InputMessage); break
|
await inputMessage(ws, msg); break
|
||||||
|
|
||||||
case "save-file":
|
case "save-file":
|
||||||
await saveFileMessage(ws, msg as SaveFileMessage); break
|
await saveFileMessage(ws, msg); break
|
||||||
|
|
||||||
case "ui:mode":
|
case "ui:mode":
|
||||||
setState("ui:mode", msg.data); break
|
setState("ui:mode", msg.data); break
|
||||||
|
|
@ -24,8 +24,8 @@ export async function dispatchMessage(ws: any, msg: Message) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function inputMessage(ws: any, msg: InputMessage) {
|
async function inputMessage(ws: any, msg: Message) {
|
||||||
const result = await runCommand(msg.session, msg.id, msg.data as string, ws)
|
const result = await runCommand(msg.session || "", msg.id || "", msg.data as string, ws)
|
||||||
|
|
||||||
if (typeof result.output === "object" && "game" in result.output) {
|
if (typeof result.output === "object" && "game" in result.output) {
|
||||||
send(ws, { id: msg.id, type: "game:start", data: result.output.game })
|
send(ws, { id: msg.id, type: "game:start", data: result.output.game })
|
||||||
|
|
@ -34,7 +34,7 @@ async function inputMessage(ws: any, msg: InputMessage) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveFileMessage(ws: any, msg: SaveFileMessage) {
|
async function saveFileMessage(ws: any, msg: Message) {
|
||||||
if (msg.id && typeof msg.data === "string") {
|
if (msg.id && typeof msg.data === "string") {
|
||||||
await Bun.write(msg.id.replace("..", ""), msg.data, { createPath: true })
|
await Bun.write(msg.id.replace("..", ""), msg.data, { createPath: true })
|
||||||
send(ws, { type: "output", data: { status: "ok", output: `saved ${basename(msg.id)}` } })
|
send(ws, { type: "output", data: { status: "ok", output: `saved ${basename(msg.id)}` } })
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,5 @@ export const Terminal: FC = async () => (
|
||||||
<li class="center">**** NOSE PLUTO V{new Date().getMonth() + 1}.{new Date().getDate()} ****</li>
|
<li class="center">**** NOSE PLUTO V{new Date().getMonth() + 1}.{new Date().getDate()} ****</li>
|
||||||
<li class="center">VRAM <span id="vram-size">000KB</span></li>
|
<li class="center">VRAM <span id="vram-size">000KB</span></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div id="statusline"><div><a href="#projects" id="project-name">root</a>: <a href="#ls" id="project-cwd">/</a></div></div>
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
@ -38,4 +38,5 @@ export function cacheCommands(cmds: string[]) {
|
||||||
commands.push(...cmds)
|
commands.push(...cmds)
|
||||||
commands.push(...Object.keys(browserCommands))
|
commands.push(...Object.keys(browserCommands))
|
||||||
commands.sort()
|
commands.sort()
|
||||||
|
console.log("CMDS", commands)
|
||||||
}
|
}
|
||||||
|
|
@ -1,12 +1,9 @@
|
||||||
////
|
|
||||||
// Dispatch server->client Messages received via WebSocket
|
|
||||||
|
|
||||||
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 { handleSessionStart, handleSessionUpdate } from "./session"
|
import { browserCommands } from "./commands"
|
||||||
|
|
||||||
// message received from server
|
// message received from server
|
||||||
export async function dispatchMessage(msg: Message) {
|
export async function dispatchMessage(msg: Message) {
|
||||||
|
|
@ -27,10 +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 "session:start":
|
case "ui:mode":
|
||||||
handleSessionStart(msg); break
|
browserCommands.mode?.(msg.data as string); break
|
||||||
case "session:update":
|
|
||||||
handleSessionUpdate(msg); break
|
|
||||||
default:
|
default:
|
||||||
console.error("unknown message type", msg)
|
console.error("unknown message type", msg)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,47 +2,6 @@
|
||||||
// Each browser tab is a shell session. This means you can run multiple sessions
|
// Each browser tab is a shell session. This means you can run multiple sessions
|
||||||
// in the same browser.
|
// in the same browser.
|
||||||
|
|
||||||
import type { SessionStartMessage, SessionUpdateMessage } from "@/shared/types"
|
|
||||||
import { browserCommands } from "./commands"
|
|
||||||
import { randomId } from "../shared/utils"
|
import { randomId } from "../shared/utils"
|
||||||
import { $ } from "./dom"
|
|
||||||
|
|
||||||
export const sessionId = randomId()
|
export const sessionId = randomId()
|
||||||
export const projectName = $("project-name") as HTMLAnchorElement
|
|
||||||
export const projectCwd = $("project-cwd") as HTMLAnchorElement
|
|
||||||
export const sessionStore = new Map<string, string>()
|
|
||||||
|
|
||||||
export function handleSessionStart(msg: SessionStartMessage) {
|
|
||||||
sessionStore.set("NOSE_DIR", msg.data.NOSE_DIR)
|
|
||||||
updateProjectName(msg.data.project)
|
|
||||||
updateCwd(msg.data.cwd)
|
|
||||||
browserCommands.mode?.(msg.data.mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function handleSessionUpdate(msg: SessionUpdateMessage) {
|
|
||||||
const data = msg.data as Record<string, string>
|
|
||||||
|
|
||||||
if (data.project)
|
|
||||||
updateProjectName(data.project)
|
|
||||||
|
|
||||||
if (data.cwd)
|
|
||||||
updateCwd(data.cwd)
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateProjectName(project: string) {
|
|
||||||
sessionStore.set("project", project)
|
|
||||||
projectName.textContent = project
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateCwd(cwd: string) {
|
|
||||||
cwd = displayProjectPath(cwd)
|
|
||||||
sessionStore.set("cwd", cwd)
|
|
||||||
projectCwd.textContent = cwd
|
|
||||||
}
|
|
||||||
|
|
||||||
function displayProjectPath(path: string): string {
|
|
||||||
let prefix = sessionStore.get("NOSE_DIR") || ""
|
|
||||||
prefix += "/" + sessionStore.get("project")
|
|
||||||
|
|
||||||
return path.replace(prefix, "") || "/"
|
|
||||||
}
|
|
||||||
|
|
@ -29,7 +29,7 @@ export function projectDir(name = projectName()): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function projectBin(name = projectName()): string {
|
export function projectBin(name = projectName()): string {
|
||||||
return join(projectDir(name), "bin")
|
return join(projectDir(), "bin")
|
||||||
}
|
}
|
||||||
|
|
||||||
export function projectFiles(name = projectName()): Dirent[] {
|
export function projectFiles(name = projectName()): Dirent[] {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import { prettyJSON } from "hono/pretty-json"
|
||||||
import color from "kleur"
|
import color from "kleur"
|
||||||
|
|
||||||
import type { Message } from "./shared/types"
|
import type { Message } from "./shared/types"
|
||||||
import { NOSE_ICON, NOSE_BIN, NOSE_DATA, NOSE_DIR, NOSE_ROOT_BIN, DEFAULT_PROJECT } from "./config"
|
import { NOSE_ICON, NOSE_BIN, NOSE_DATA, NOSE_DIR, NOSE_ROOT_BIN } from "./config"
|
||||||
import { transpile, isFile, tilde, isDir } from "./utils"
|
import { transpile, isFile, tilde, isDir } from "./utils"
|
||||||
import { serveApp } from "./webapp"
|
import { serveApp } from "./webapp"
|
||||||
import { commands, commandPath, loadCommandModule } from "./commands"
|
import { commands, commandPath, loadCommandModule } from "./commands"
|
||||||
|
|
@ -137,15 +137,8 @@ app.get("/ws", c => {
|
||||||
addWebsocket(ws)
|
addWebsocket(ws)
|
||||||
send(ws, { type: "commands", data: await commands() })
|
send(ws, { type: "commands", data: await commands() })
|
||||||
|
|
||||||
send(ws, {
|
const mode = getState("ui:mode") || "tall"
|
||||||
type: "session:start",
|
send(ws, { type: "ui:mode", data: mode })
|
||||||
data: {
|
|
||||||
NOSE_DIR: NOSE_DIR,
|
|
||||||
project: DEFAULT_PROJECT,
|
|
||||||
cwd: "/",
|
|
||||||
mode: getState("ui:mode") || "tall"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
async onMessage(event, ws) {
|
async onMessage(event, ws) {
|
||||||
let data: Message | undefined
|
let data: Message | undefined
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
// Session storage. 1 browser tab = 1 session
|
// Session storage. 1 browser tab = 1 session
|
||||||
|
|
||||||
import { AsyncLocalStorage } from "async_hooks"
|
import { AsyncLocalStorage } from "async_hooks"
|
||||||
import { send } from "./websocket"
|
|
||||||
|
|
||||||
export type Session = {
|
export type Session = {
|
||||||
taskId?: string
|
taskId?: string
|
||||||
|
|
@ -36,12 +35,6 @@ export function sessionSet(key: keyof Session, value: any) {
|
||||||
const store = ALS.getStore()
|
const store = ALS.getStore()
|
||||||
if (!store) throw "sessionSet() called outside of ALS.run"
|
if (!store) throw "sessionSet() called outside of ALS.run"
|
||||||
store[key] = value
|
store[key] = value
|
||||||
|
|
||||||
if (!store.ws) return
|
|
||||||
send(store.ws, {
|
|
||||||
type: "session:update",
|
|
||||||
data: { [key]: value }
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sessionStore(sessionId: string, taskId?: string, ws?: any): Session {
|
export function sessionStore(sessionId: string, taskId?: string, ws?: any): Session {
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,6 @@ export type Message = {
|
||||||
type: MessageType
|
type: MessageType
|
||||||
data?: CommandResult | CommandOutput
|
data?: CommandResult | CommandOutput
|
||||||
}
|
}
|
||||||
| InputMessage
|
|
||||||
| SaveFileMessage
|
|
||||||
| SessionStartMessage
|
|
||||||
| SessionUpdateMessage
|
|
||||||
|
|
||||||
export type MessageType = "error" | "input" | "output" | "commands" | "save-file"
|
export type MessageType = "error" | "input" | "output" | "commands" | "save-file"
|
||||||
| "game:start"
|
| "game:start"
|
||||||
|
|
@ -24,32 +20,3 @@ export type CommandResult = {
|
||||||
status: "ok" | "error"
|
status: "ok" | "error"
|
||||||
output: CommandOutput
|
output: CommandOutput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type InputMessage = {
|
|
||||||
type: "input"
|
|
||||||
id: string
|
|
||||||
session: string
|
|
||||||
data: CommandResult | CommandOutput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SaveFileMessage = {
|
|
||||||
type: "save-file"
|
|
||||||
id: string
|
|
||||||
session: string
|
|
||||||
data: CommandResult | CommandOutput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SessionStartMessage = {
|
|
||||||
type: "session:start"
|
|
||||||
data: {
|
|
||||||
NOSE_DIR: string
|
|
||||||
project: string
|
|
||||||
cwd: string
|
|
||||||
mode: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SessionUpdateMessage = {
|
|
||||||
type: "session:update"
|
|
||||||
data: Record<string, string>
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user