OverviewPage

tcmts.studio.OverviewPage · #xl4ridus · term · runs on the server

is an async component

The studio's first page: what needs you, and what's going on. What runs worse than its version before (and the change that did it); what tcm runs on the codebase (apps, servers) and agents' sessions; the scratch files holding work; beside them the last day's calls and errors, what needs fixing, and the latest history.

import { historyList } from "#codebase/tcmts/cli/changes";
import { needsFixing } from "#codebase/tcmts/cli/codebase";
import { liveMcpServers } from "#codebase/tcmts/cli/mcp";
import { liveRunning, shownUptime } from "#codebase/tcmts/cli/running";
import { traceErrors, traceTotals, traceWorse, withTraces } from "#codebase/tcmts/cli/traced";
import { ago, at, codeHref, servedCodebase, shareNow } from "#codebase/tcmts/share";
import { entryTitle } from "#codebase/tcmts/store";
import { BranchLink, Live, scratchFiles, StudioFrame, studioShare, traceOfHref } from "#codebase/tcmts/studio";

export async function OverviewPage() {
  studioShare();
  const s = shareNow();
  if (!s.signedIn) return <StudioFrame tab="overview" title="Sign in">{null}</StudioFrame>;
  const root = s.cb.lay.root;
  const since = Date.now() - 86_400_000;
  const t = withTraces(s.cb, (db) => {
    const rows = traceTotals(db, s.cb, since, 50, { byNameNow: true });
    return { rows, worse: traceWorse(db, s.cb, since, rows), errors: traceErrors(db, s.cb, since, 50) };
  });
  const answered = (t?.rows ?? [])
    .filter((r) => ["call", "page", "http", "job"].includes(r.kind))
    .reduce((n, r) => n + r.n, 0);
  const failed = (t?.errors ?? []).reduce((n, e) => n + e.times, 0);
  const running = liveRunning(root);
  const agents = liveMcpServers(root);
  const broken = needsFixing(s.cb);
  const lately = historyList(s.cb, 5);
  const scratch = scratchFiles(servedCodebase().cb);
  return (
    <StudioFrame tab="overview" title="Overview">
      <Live every={5000} />
      <div className="pagehead">
        <h2 className="title">Overview</h2>
      </div>
      {t?.worse.map((w) => (
        <div className="notice" key={`${w.name} ${w.kind}`} style={{ borderColor: "var(--warn)", background: "var(--warnbg)" }}>
          <b className="warn">
            <a href={traceOfHref(w.name, w.kind)} style={{ color: "inherit" }}>{w.name}</a> runs worse than its version before.
          </b>{" "}
          {w.changed.length ? <>What changed: {w.changed.join(", ")}. </> : null}
          {w.since && <a href={at(`/history/${w.since.causal}`)}>See the change</a>}
        </div>
      ))}
      <div className="cols-2" style={{ display: "grid", gap: 24 }}>
        <div>
          <div className="box">
            <div className="boxhead">
              <b>Running</b>
              <span className="muted">tcm dev, start, serve</span>
              <a className="right" href={at("/processes")}>Processes</a>
            </div>
            {running.length ? (
              running.map((r) => (
                <div className="lrow" key={r.pid}>
                  <div>
                    <span className="live" />{" "}
                    <code>{[`tcm ${r.command}`, r.app].filter(Boolean).join(" ")}</code>{" "}
                    <span className="muted">{[r.branch, `${shownUptime(Date.now() - r.started)}`].filter(Boolean).join(" · ")}</span>
                  </div>
                  {r.url && <a className="btn small" href={r.url} target="_blank" rel="noreferrer">Open</a>}
                </div>
              ))
            ) : (
              <div className="empty">Nothing's running: <code>tcm dev &lt;app&gt;</code> runs one.</div>
            )}
          </div>
          <div className="box">
            <div className="boxhead">
              <b>Agents</b>
              <span className="muted">their sessions' MCP servers</span>
              <a className="right" href={at("/branches")}>Branches</a>
            </div>
            {agents.length ? (
              agents.map((a) => (
                <div className="lrow" key={a.pid}>
                  <div>
                    <span className="live" />{" "}
                    {a.branch ? <BranchLink branch={a.branch} /> : <span className="muted">on the codebase's own branch</span>}{" "}
                    {a.client && (
                      <span className="muted" title={a.client}>
                        for …/{a.client.split("/").filter(Boolean).pop()}
                      </span>
                    )}
                  </div>
                </div>
              ))
            ) : (
              <div className="empty">No agent is working on this codebase.</div>
            )}
          </div>
          <div className="box">
            <div className="boxhead">
              <b>Scratch files</b>
              <span className="muted">work not saved to the codebase yet</span>
              <a className="right" href={at("/scratch")}>All</a>
            </div>
            {scratch.length ? (
              scratch.slice(0, 5).map((x) => (
                <a className="lrow tracerow" key={x.file} href={at(`/scratch/${encodeURIComponent(x.file)}`)}>
                  <span className="mono">{x.file}</span>
                  <span className="muted">{`changed ${ago(x.at)}`}</span>
                </a>
              ))
            ) : (
              <div className="empty">None.</div>
            )}
          </div>
        </div>
        <aside className="side">
          <div className="sect">
            <h4>The last day</h4>
            {t ? (
              <div className="stats">
                <div className="stat"><b>{answered}</b>answered</div>
                <div className="stat"><b className={failed ? "bad" : ""}>{failed}</b>errors</div>
                <div className="stat"><b className={t.worse.length ? "warn" : ""}>{t.worse.length}</b>slower or failing more</div>
              </div>
            ) : (
              <span className="muted">No traces yet: tcm dev and tcm start record them as the app is used.</span>
            )}
          </div>
          <div className="sect">
            <h4><a href={at("/fix")} style={{ color: "inherit" }}>Needs fixing</a></h4>
            {broken.length ? (
              <>
                {broken.slice(0, 8).map((b) => {
                  const n = s.cb.store.pathsOf(b.hash)[0];
                  return (
                    <div key={b.hash}>
                      {n ? <a className="mono" href={codeHref(n)}>{n}</a> : <code>#{b.hash.slice(0, 8)}</code>}{" "}
                      <span className="muted">{b.message.split("\n")[0]}</span>
                    </div>
                  );
                })}
                {broken.length > 8 && <a className="muted" href={at("/fix")}>and {broken.length - 8} more</a>}
              </>
            ) : (
              <span className="muted">Nothing: everything typechecks.</span>
            )}
          </div>
          <div className="sect">
            <h4>Lately</h4>
            {lately.map((e) => (
              <div key={e.hash}>
                <a href={at(`/history/${e.hash}`)}>{entryTitle(e)}</a>
                <span className="muted" style={{ display: "block", fontSize: 12, marginBottom: 6 }}>{ago(e.ts)}</span>
              </div>
            ))}
          </div>
        </aside>
      </div>
    </StudioFrame>
  );
}

uses

used by