NeedsFixingPage
is an async component
What needs fixing on the branch, as tcm todo says, each with what fixes it: a merge waiting apart (what it
left, as merged, and Drop the merge); conflicted names, each definition with its side (Edit merges them into a file,
Keep ours, theirs or one by hash settles it); definitions that stopped typechecking, with why (Edit writes each
against what replaced what it used); and code using a version nothing names. On a branch only read, nothing to press.
import { at, codeHref, servedCodebase, shareNow } from "#codebase/tcmts/share"; import { AbortMergeButton, ConflictButtons, EditButton, fixesOf, Live, mergeRuns, scratchFiles, StudioFrame, studioShare, } from "#codebase/tcmts/studio"; export async function NeedsFixingPage() { studioShare(); const s = shareNow(); if (!s.signedIn) return ( <StudioFrame tab="fix" title="Sign in"> {null} </StudioFrame> ); const own = s.cb === servedCodebase().cb; const list = await fixesOf(s.cb); const running = mergeRuns.some((r) => r.ended === undefined); const name = (n: string | undefined, hash: string) => n ? ( <a className="mono" href={codeHref(n)}> {n} </a> ) : ( <code>#{hash.slice(0, 8)}</code> ); const merged = list.merging ? scratchFiles(s.cb).filter((f) => list.conflicts.some((c) => f.file.startsWith(`${c.path}.`))) : []; const nothing = !list.conflicts.length && !list.broken.length && !list.unnamed.length; return ( <StudioFrame tab="fix" title="Needs fixing"> {(running || list.merging) && <Live every={4000} />} <div className="pagehead"> <h2 className="title">Needs fixing</h2> <span className="muted"> {nothing ? "" : [ list.conflicts.length && `${list.conflicts.length} conflicted`, list.broken.length && `${list.broken.length} no longer typecheck${list.broken.length === 1 ? "s" : ""}`, list.unnamed.length && `${list.unnamed.length} use${list.unnamed.length === 1 ? "s" : ""} an unnamed version`, ] .filter(Boolean) .join(" · ")} </span> </div> {list.merging && ( <div className="notice mergewait"> <div> <b>{`Merging ${list.merging} into ${list.branch} isn't finished.`}</b>{" "} {`It waits apart: ${list.branch} stays as it was until what's below is settled, and then has the merge. What's here is as merged.`} {merged.length > 0 && ( <> {" Its conflicts are written out, to settle: "} {merged.map((f, i) => ( <span key={f.file}> {i ? ", " : ""} <a className="mono" href={at(`/scratch/${encodeURIComponent(f.file)}`)}> {f.file} </a> </span> ))} . </> )} </div> {own && <AbortMergeButton theirs={list.merging} into={list.branch} />} </div> )} {nothing && ( <div className="box"> <div className="empty"> {list.merging ? "Nothing's left to settle: the next change on the branch finishes the merge." : "Nothing needs fixing: everything typechecks, and no name is conflicted."} </div> </div> )} {list.conflicts.length > 0 && ( <div className="box"> <div className="boxhead"> <b>Conflicted</b> <span className="muted">two definitions or more under one name: keep one, or merge them in a file</span> </div> {list.conflicts.map((c) => ( <div className="failrow" key={c.key}> <div className="lrow"> <span> {name(c.path, c.sides[0]?.hash ?? "")} {c.key !== c.path && <span className="muted"> (its type)</span>} </span> {own && <ConflictButtons name={c.path} sides={c.sides} />} </div> <div className="conflictsides muted"> {c.sides.map((x) => ( <span key={x.hash}> <code>#{x.hash.slice(0, 8)}</code> {x.side ? ` ${x.side}: ${x.label}` : ""} </span> ))} </div> </div> ))} </div> )} {list.broken.length > 0 && ( <div className="box"> <div className="boxhead"> <b>No longer typechecks</b> <span className="muted"> what it used changed; Edit writes it with what replaced that, to fix (fixing the first often fixes the rest) </span> </div> {list.broken.map((b) => ( <div className="failrow" key={b.hash}> <div className="lrow"> <span>{name(b.names[0], b.hash)}</span> {own && b.names[0] && ( <span className="actions"> <EditButton name={b.names[0]} /> </span> )} </div> <pre className="code errmsg">{b.message.split("\n").slice(0, 6).join("\n")}</pre> </div> ))} </div> )} {list.unnamed.length > 0 && ( <div className="box"> <div className="boxhead"> <b>Uses an unnamed version</b> <span className="muted"> not wrong: from a change it couldn't follow, or a name removed with Delete; Edit imports what replaced it, where something names that </span> </div> {list.unnamed.map((u) => ( <div className="lrow" key={`${u.path} ${u.uses}`}> <span> {name(u.path, u.uses)}{" "} <span className="muted"> uses <code>#{u.uses.slice(0, 8)}</code>, made as <span className="mono">{u.madeAs}</span> </span> </span> {own && ( <span className="actions"> <EditButton name={u.path} /> </span> )} </div> ))} </div> )} </StudioFrame> ); }
uses
- tcmts.share.at
- tcmts.share.codeHref
- tcmts.share.servedCodebase
- tcmts.share.shareNow
- tcmts.studio.AbortMergeButton
- tcmts.studio.ConflictButtons
- tcmts.studio.EditButton
- tcmts.studio.Live
- tcmts.studio.StudioFrame
- tcmts.studio.fixesOf
- tcmts.studio.mergeRuns
- tcmts.studio.scratchFiles
- tcmts.studio.studioShare