OverviewPage
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 <app></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
- tcmts.cli.changes.historyList
- tcmts.cli.codebase.needsFixing
- tcmts.cli.mcp.liveMcpServers
- tcmts.cli.running.liveRunning
- tcmts.cli.running.shownUptime
- tcmts.cli.traced.traceErrors
- tcmts.cli.traced.traceTotals
- tcmts.cli.traced.traceWorse
- tcmts.cli.traced.withTraces
- tcmts.share.ago
- tcmts.share.at
- tcmts.share.codeHref
- tcmts.share.servedCodebase
- tcmts.share.shareNow
- tcmts.store.entryTitle
- tcmts.studio.BranchLink
- tcmts.studio.Live
- tcmts.studio.StudioFrame
- tcmts.studio.scratchFiles
- tcmts.studio.studioShare
- tcmts.studio.traceOfHref