{{define "content"}} {{/* Adding content, on its own page (2026-09-29, Paul: "remove the Add forms from the index … that should be a separate Add content form"). The form, the drop zone, the upload tray and the outcome message moved here unchanged; POST /documents still receives them, and now redirects back HERE with its outcome rather than to the index. The index keeps search and the document list, and links here. */}}

Add content

{{/* The drop zone WRAPS the file input, it does not replace it. With no script the div is an unstyled box, the hint and the note stay hidden, and this is the same form it was: choose files, press Store it. static/drop.js adds rst-dropzone="ready", which is what the styling keys off — so a page whose script did not load never draws a drop target that cannot take a drop. What a DROP does has changed: it no longer fills this input and wait for Store it. Each dropped file is posted on its own, in the background, to this form's own action, and the tray below the form says how each one went. Choosing files here and pressing Store it is untouched and still posts the whole form. The data attributes are the handler's own limits and the handler's own words (see dropZone in handlers.go). The browser reads them from here rather than carrying a second copy, so it cannot come to refuse what the server would accept. The input is `multiple`, and that is a plain HTML affordance with no script behind it: createDocument iterates every part named "file" and stores one document each, so a scriptless chooser that hands it five files gets five documents. It was deliberately single-file while the handler read only the first part — four of five would have vanished without a word. */}} {{with .Drop}}
{{/* Muted by memoria.css on [rst-dropzone-hint]. (Not the design system's muted-cell utility: that is a class, and spelled as an attribute it styled nothing at all — nineteen places looked muted in the markup and read as body text on the screen. They are rst-muted now, which memoria.css styles.) Both paragraphs are `hidden` so a scriptless page promises no drop target. drop.js unhides the note while it is still EMPTY, before it ever writes into it: a live region that enters the accessibility tree in the same tick as its first message is commonly not announced at all, and a refusal nobody hears is the one outcome this feature must not have. */}}
{{end}}
{{if .Flash}}

{{.Flash}}

{{end}} {{/* The upload tray. It lives OUTSIDE the form on purpose: it is not something the form submits, and a scriptless browser must find the form exactly as it was. Scriptless it is `hidden` and empty, so it is not on the page at all. drop.js unhides it — still empty — at its ready step, for the same reason it unhides the note there: [rst-tray-status] is the live region that announces what a drop did, and a live region that enters the accessibility tree in the same tick as its first message is commonly not announced at all. Only [rst-tray-status] is live. The rows are not, and must not be: five files uploading is five rows changing several times a second, and a live region over that is a screen reader nobody can use. Every word here comes from dropZone in handlers.go, like the data attributes above — the tray and the flash sentence a scriptless browser sees are the same strings, so they cannot drift. The document list is on the index, not here, so once anything is stored the tray points there with a real link. Nothing follows it on its own: someone mid-drop does not want the ground moving under them. When a drop settles drop.js re-fetches this page and swaps its one rst-refresh region, the passage count in the footer. */}} {{with .Drop}} {{end}}
{{end}}