diff --git a/README.md b/README.md index e140f02..80c633c 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,11 @@ ## Commands -Commands can return one of two types: +Commands can return one of three types: - string - { error: string } +- { html: string } ## Fonts diff --git a/nose/bin/projects.ts b/nose/bin/projects.ts index 22993ae..179bd7d 100644 --- a/nose/bin/projects.ts +++ b/nose/bin/projects.ts @@ -1,5 +1,9 @@ import { apps } from "@/webapp" +import { Thread } from "@/shell" export default function () { - return apps().join(" ") + const state = Thread.getStore() + if (!state) return { error: "no state" } + + return { html: apps().map(app => app === state.project ? `${app}` : app).join(" ") } } \ No newline at end of file diff --git a/src/js/scrollback.ts b/src/js/scrollback.ts index 5c08dd6..8d3a054 100644 --- a/src/js/scrollback.ts +++ b/src/js/scrollback.ts @@ -3,9 +3,16 @@ // input, output, etc import { scrollback, $$ } from "./dom.js" +import type { CommandOutput } from "../shared/types.js" type InputStatus = "waiting" | "streaming" | "ok" | "error" +export function autoScroll() { + setTimeout(() => { + requestAnimationFrame(() => scrollback.scrollTop = scrollback.scrollHeight) + }, 10) +} + export function addInput(id: string, input: string) { const parent = $$("li.input") const status = $$("span.status.yellow", "•") @@ -38,17 +45,34 @@ export function setStatus(id: string, status: InputStatus) { } } -export function addOutput(id: string, output: string) { - const item = $$("li", output) +export function addOutput(id: string, output: CommandOutput) { + const item = $$("li") item.classList.add("output") item.dataset.id = id + const [format, content] = processOutput(output) + + if (format === "html") + item.innerHTML = content + else + item.textContent = content + scrollback.append(item) autoScroll() } -export function autoScroll() { - setTimeout(() => { - requestAnimationFrame(() => scrollback.scrollTop = scrollback.scrollHeight) - }, 10) -} \ No newline at end of file +function processOutput(output: CommandOutput): ["html" | "text", string] { + let content = "" + let html = false + + if (typeof output === "string") { + content = output + } else if (output.html) { + html = true + content = output.html + } else { + content = JSON.stringify(output) + } + + return [html ? "html" : "text", content] +} diff --git a/src/shared/types.ts b/src/shared/types.ts index 75f3044..76c66d8 100644 --- a/src/shared/types.ts +++ b/src/shared/types.ts @@ -5,7 +5,9 @@ export type Message = { data: CommandResult | string | string[] } +export type CommandOutput = string | { html: string } + export type CommandResult = { status: "ok" | "error" - output: string + output: CommandOutput } \ No newline at end of file diff --git a/src/shell.ts b/src/shell.ts index ab78bb5..a63638b 100644 --- a/src/shell.ts +++ b/src/shell.ts @@ -1,7 +1,7 @@ //// // runs commands and such. -import type { CommandResult } from "./shared/types" +import type { CommandResult, CommandOutput } from "./shared/types" import { join } from "path" import { NOSE_SYS_BIN, NOSE_BIN } from "./config" import { isFile } from "./utils" @@ -27,7 +27,7 @@ export async function runCommand(session: string, id: string, input: string): Pr } let status: "ok" | "error" = "ok" - let output = "" + let output: CommandOutput = "" let state = sessions.get(session) if (!state) { @@ -46,22 +46,23 @@ export async function runCommand(session: string, id: string, input: string): Pr return { status, output } } -async function exec(cmd: string, args: string[]): Promise<["ok" | "error", string]> { +async function exec(cmd: string, args: string[]): Promise<["ok" | "error", CommandOutput]> { const module = await import(commandPath(cmd) + "?t+" + Date.now()) - if (!module || !module.default) { + if (!module || !module.default) return ["error", `${cmd} has no default export`] - } - const output = await module.default(...args) + return processExecOutput(await module.default(...args)) +} +function processExecOutput(output: string | any): ["ok" | "error", CommandOutput] { if (typeof output === "string") { return ["ok", output] } else if (typeof output === "object") { if (output.error) { return ["error", output.error] } else { - return ["ok", JSON.stringify(output)] + return ["ok", output] } } else { return ["ok", String(output)]