Root location folds away once there is nothing left to do

Everything under it is in order most of the time, and a card that is four
paragraphs of "fine" pushes the settings that are not fine off the screen. So it
shuts, with a check on the right, and opens on a click.

"Finished" is three bits reported upward by the parts that own them: the path is
saved, the checkout is a repository, and media/ both exists and is current. None
of those is knowable in the parent — each child fetches its own status — and
lifting all of it up to answer one question would be a far worse trade than
three booleans.

It is only collapsible while settled. A block you can shut on unfinished setup
is a way to hide the thing you came here to fix. And the children are NOT
RENDERED while it is shut rather than hidden, because a collapsed block whose
children still poll is a settled panel making requests all afternoon about
something nobody is looking at.

The `null` in `collapsed` is a sentinel for "nobody has decided", which is what
lets a deliberate expand survive. Setting it to false while unsettled spent it
on the first render — before any status had arrived — so the block could never
fold itself away at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-29 11:52:54 +01:00
co-authored by Claude Opus 5
parent f30d40446a
commit cc283ee7b6
5 changed files with 139 additions and 14 deletions
+13 -1
View File
@@ -52,7 +52,13 @@ type Status = {
type RunResult = { ok: boolean; output: string; status: Status };
export const WebsiteCheckout = ({ rootPath }: { rootPath: string }) => {
export const WebsiteCheckout = ({
rootPath,
onSettled,
}: {
rootPath: string;
onSettled?: (settled: boolean) => void;
}) => {
const client = useClient();
const clientRef = useRef(client);
clientRef.current = client;
@@ -80,6 +86,12 @@ export const WebsiteCheckout = ({ rootPath }: { rootPath: string }) => {
void load();
}, [load, rootPath]);
// One bit upward — see the note in `MediaSync`. A checkout that is present but not a repository is
// NOT settled: something is in the way, and that is exactly the state worth staying open for.
useEffect(() => {
onSettled?.(Boolean(status?.isRepo));
}, [status?.isRepo, onSettled]);
const act = async (what: 'clone' | 'update') => {
setBusy(what);
setOutput(null);