Multilingual editor (3/3): page properties, page menu, pending changes, paste confirmation, Languages view, module lists #200
Labels
No labels
bug
enhancement
needs-triage
ready-for-agent
ready-for-human
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/deckle#200
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Third of three editor slices of the multilingual work (contract: the PRD in #192, section 8; visual reference:
docs/mockups/multilingual/, artifact https://claude.ai/artifact/6RYbRG7y4yMyeGxYbFsh1n). This slice is everything around the page: page properties of a translation, the page menu (Translate to…, Disconnect, Delete translation, the page delete plan), per-file pending changes and badges, the cut-and-paste confirmation that names dropped translations, the Languages view, the module and category lists with a translation-state column, and the link picker's language note.Builds on #192 and #199. The API (README "Translations on the wire") already answers everything below; nothing in the engine changes.
Current behavior
After #199 a translation is editable, but its properties form is the default-language form, the page menu has no translation items, pending changes still send
langand show one row per page,fetchMoveRefssends notoParentUidand ignores thetranslationsblock, there is no Languages view, and module lists ignore the entries'translationstate.Desired behavior
Every rule is phrased in terms of the default language (
languages[0]ofGET /api/site). Mockup: Properties on Leistungen, the page ⋯ menu in EN and DE, cut Hero on Services → paste on Hosting, the "changes" chip, the globe toggle, the News module in DE.Page properties in a translation (
POST /api/formkindpropertiesin a non-default language already describes the translation's own fields —title,slugwithplaceholder= the source segment (absent on the home page, the categories section and a category),menuTitle/descriptionwhere the page type has them — then the source's other fieldsreadOnlywith anote, and the translation's ownenabled;POST /api/page/settings/savewrites only the own keys):addressin this language (tree node) + the typed segment (or the placeholder when empty) +/. The home page shows no segment field, with a hint that a language's home is always/<code>/.slug(segment already a sibling's address in this language) shows on the field like every other field error and writes nothing.note(layout, type and weight follow the source).addressin this language) and the file (content/<path>with the language suffix).Page menu:
GET /api/page/delete-planand names the translations deleted with the page (translations): "a translation cannot exist without its source".POST /api/page/disconnect {page, lang}; the page re-renders as free.content/<path>/_index.<code>.md), that only that file goes, that subpages keep their translations →POST /api/page/deletewithlang(the plan answers{deleted: 1, changed: 0}); afterwards the page shows the untranslated-page screen of #192.POST /api/page/renamealready does); "Open in " and the other items unchanged.Cut and paste across pages: the confirmation before pasting a cut element calls
GET /api/element/move-refswithtoParentUid(the uid of the container the paste lands in; empty for a slot) and readstranslations: {moved: [code], dropped: [code]}. The dialog says which languages' translations move with the element and names the dropped ones in a warning (": the translation is deleted. has no connected translation to move it into. Translate first to keep it."); the confirm button reads "Move and drop " (danger) when anything is dropped. Ask for a same-page move into a container too (toPage=page), since a container untranslated in a language drops the copy there. Todayweb/src/api/links.tssends notoParentUidand ignores the block.Pending changes:
GET /api/publish/pending,POST /api/publish-alland/api/publish/discard-alllose theirlangargument (the server ignores a sent one). The panel lists one row per file: a translation is its source page's row in that language (page: {path, lang}) with a language badge; clicking a row opens that page in that language. The panel header says publishing takes every language together. Publish all and Discard all act site-wide. Page Undo and Discard… stay per file; their tooltips name the file. Tree badges are already per language (GET /api/badges?lang=); confirm they read per file. The publish link check'slinkedFromentries withlangare shown with the language badge inDisabledLinksDialog.Languages view: the Columns / Languages toggle in the page docheader (introduced as a disabled control or omitted in #192; enabled here). For each slot of the layout (inherited slots skipped) a row of columns, one per language in
languagesorder: the default language first as "source", each translation's cards in a dense variant ofElementCard(placeholders for untranslated uids as a dense row with an icon-only Translate), or "Not translated to " with Translate page…, and an Open button per column that switches to that language's Columns view. Data: one page fetch per language, cached with the per-language trees of #192. The view is UI state, not part of the URL.Modules and categories: in a non-default language the entry list (
GET /api/module/entries?lang=) shows the translation's title, an italic default-language title with the default code tag for an untranslated entry, and a Translation column (translated · changed since · unpublished changes · not translated, with the switch's dots; "changed" and "unpublished" from the entry'stranslationand badge state where the API provides them, else only translated / not translated) with an inline Translate… on untranslated entries. New is disabled outside the default language with a hint. An entry's ⋮ menu offers "Translate to …" for each language in which it is untranslated (in the default language: per-language entry lists, cached like the trees) and "Delete translation…" on a translated entry in a non-default language. The Categories module behaves the same onGET /api/categories?lang=(translation: "translated"|"missing").Link picker and
/api/link/resolve: the resolved target shows theaddressin the current language; when the answer carriesfallback: truea note says the page is untranslated in this language and the link goes to the default-language page.Skills and AGENTS.md (CLAUDE.md rule): no skill walks through editor screens; confirm with
grep -ril "translat\|language" scaffold/site/.claude/skills/and leave them untouched unless a step describes the editor's behaviour. AGENTS.md "Editor modules": add that a module's list shows the selected language's entries and that "New" creates in the default language, if the section's wording does not already say so.Decisions
Settled in the grilling session and the mockup review; implement, do not reopen.
/<code>/.PageForm,PendingChanges,PublishChip,ModuleScreen,Menu,Dialog,LinkPickerare extended.Key interfaces
web/src/api/links.ts(fetchMoveRefs) —toParentUid,translations;api/publish.ts— droplang, rows carrypage.lang;api/pagemanage.ts/pagesettings.ts— disconnect, delete with lang, delete-plantranslations, the properties form of a translation;api/modules.ts,api/categories.ts—translationper entry.components/PageForm.tsx,PageScreen.tsx,PageEditorChrome.tsx,Docheader.tsx,Menu.tsx,DeleteDialog.tsx,ClipboardBar.tsx/ the paste confirmation,PendingChanges.tsx,PublishChip.tsx,DisabledLinksDialog.tsx,ModuleScreen.tsx,LinkPicker.tsx; a newLanguagesView.pendingChanges.ts,clipboard.ts,pageEditor.ts,modules.ts.docs/mockups/multilingual/m/m.js—TrPageForm,savePageProps,pageMenu,disconnect, the patchedCLIP.paste,openPendingChanges,LanguagesView,ModuleMain.Acceptance criteria
servicesindeedit title, segment, menu title, description with source values and Copy; the address hint updates while typing; a segment that is a sibling's address shows the server's field error and writes nothing; Appearance is read-only; Access shows per-language Enabled, the address and the file; the home page has no segment field.toParentUid; a same-page move into an untranslated container asks too.langsent; tree badges are per file; row clicks open the file's language.webunit tests cover: the properties form of a translation and the slug error, the page menu per state, the move-refs request and dialog text for moved/dropped, the pending-changes shape, the Languages view, the module list column;go test ./...and the skills drift test pass.How to verify
make build,harness serveontestdata/multilingual-site, walk every screen above against the mockup; check the URL at each step.cd web && npm test;go test ./....Out of scope
Blocked by