provision <root>/media, beside the checkout

where the images and the files will live. same row shape as the checkout above it,
because it is the same kind of fact: a thing that either is or is not at a known
path under the root, with one button that puts it there.

BESIDE `website`, never inside it, and that is the point of it being here rather
than a folder in the repository:

  - it must SURVIVE the checkout. deleting `website` and cloning it again is a
    normal thing to do when something is wrong with it, and it must never be the
    thing that destroys the client's photographs.
  - it must never be TRACKED. media in git is a repository nobody can clone a year
    from now, and a .gitignore is a promise somebody eventually breaks with
    `git add -f`.

both under one root, so a backup of the root is the whole site — the code and the
things it serves. that is the property that makes the root worth being a setting.

NOTHING ABOUT ITS INSIDES IS DECIDED. this creates the directory and reports
whether it is there; the layout, who writes to it and what goes where are still
being described. inventing a folder tree now would settle that in the file least
able to change its mind, because directories on disk are not a migration you can
run.

`mkdir -p`, so it also creates a root that is still only a default — saving a root
creates nothing, and provisioning into a path that does not exist is the ordinary
first run. idempotent by the same flag, which is what makes the button safe to
offer next to a status that might be stale.

no console for this one: making a directory either works or gives one line about
why not, and a black transcript box for a single sentence makes a small thing look
like a large one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-29 01:03:28 +01:00
co-authored by Claude Opus 5
parent c268e8438a
commit 003613872d
5 changed files with 202 additions and 1 deletions
+5
View File
@@ -7,6 +7,7 @@ import { readableError } from './errors';
import { DirectoryPicker } from './DirectoryPicker';
import { Block } from './Block';
import { WebsiteCheckout } from './WebsiteCheckout';
import { MediaFolder } from './MediaFolder';
import { useFlash } from './useFlash';
/**
@@ -164,6 +165,10 @@ export const RootLocationBlock = () => {
describes what is SAVED, and a half-typed path has nothing at it. */}
{!editing && <WebsiteCheckout rootPath={saved.rootPath} />}
{/* Beside the checkout, under the same root — a backup of the root is then the whole site, the
code and the things it serves. Hidden while editing for the same reason as the checkout. */}
{!editing && <MediaFolder rootPath={saved.rootPath} />}
{/* The block's bottom row. Everything you can DO is here, on its own line, so the path above it
gets the full width. */}
<div className="flex flex-wrap items-center gap-2 border-t border-border pt-3">