AppOutputPage

tcmts.studio.AppOutputPage · #nzziyegx · term · runs on the server

uses node:fs

What an app the studio started (Start, as tcm dev) has said: its output's last 400 lines, as they come (made again every two seconds while it's shown), and whether it's running still, to open or stop.

import { liveRunning } from "#codebase/tcmts/cli/running";
import { at, servedCodebase, shareNow } from "#codebase/tcmts/share";
import { appLog, Live, StopButton, StudioFrame, studioShare } from "#codebase/tcmts/studio";
import fs from "node:fs";
import { stripVTControlCharacters } from "node:util";

export async function AppOutputPage({ app }: { app: string }) {
  studioShare();
  const s = shareNow();
  if (!s.signedIn)
    return (
      <StudioFrame tab="processes" title="Sign in">
        {null}
      </StudioFrame>
    );
  const cb = servedCodebase().cb;
  const file = appLog(cb, app);
  const text =
    file && fs.existsSync(file)
      ? stripVTControlCharacters(fs.readFileSync(file, "utf8"))
      : undefined;
  const lines = text?.split("\n") ?? [];
  const running = liveRunning(cb.lay.root).find((r) => r.app === app && r.command === "dev");
  return (
    <StudioFrame tab="processes" title={`${app} · output`}>
      <Live every={2000} />
      <div className="crumbs">
        <a href={at("/processes")}>Processes</a> <span className="muted">/</span>
      </div>
      <div className="pagehead">
        <h2 className="title mono">{app}</h2>
        <span className="muted">{running ? `running, process ${running.pid}` : "not running"}</span>
        {running && (
          <span className="right actions">
            {running.url && (
              <a className="btn small" href={running.url} target="_blank" rel="noreferrer">
                Open
              </a>
            )}
            <StopButton pid={running.pid} />
          </span>
        )}
      </div>
      <div className="box">
        <div className="boxhead">
          <b>What it said</b>
          {lines.length > 400 && (
            <span className="muted">{`its last 400 lines of ${lines.length}`}</span>
          )}
        </div>
        {text === undefined ? (
          <div className="empty">Nothing kept: the studio keeps what the apps it starts say.</div>
        ) : (
          <pre className="code applog">{lines.slice(-400).join("\n")}</pre>
        )}
      </div>
    </StudioFrame>
  );
}

uses

used by