# Editor Route: `/article/:id/edit`. API: GET /v1/article/{id} · PATCH /v1/article/{id} · PATCH …/publish|unpublish · POST …/image · DELETE. On mobile, Save and Publish sit in a sticky bottom bar and the details panel opens as a slideover. A session expiry mid-edit keeps the draft in sessionStorage and goes to /login?redirect=/article/12/edit. After logging in, the editor restores the draft and shows "Unsaved changes". Visual reference: `RenewCMS Screens Back-office.dc.html`, groups "Editor". Frames: `AppFrame.dc.html` (shell, sidebar, topbar, drawer) plus the screen frame where listed in components.md. ## Copy per state (generated from the board source, pass 2) Every state below is drawn in light and dark (theme switch on the board), at 1440×900 and 390×844 (both frames sit side by side). Board: `RenewCMS Screens Back-office.dc.html`. Copy is exact; where a cell is empty the state reuses the fixed copy. ### Fixed copy in the frame - This article doesn't exist - It may have been deleted, or the link is wrong. - All articles - Online - Draft - Retry - Preview - Save - Delete - Every bathroom is different, but the way we price them isn't. We start with a visit, measure everything, and - write down what stays - We also check the - timber windows - /articles/winter-checklist - Edit - Open - Remove - before any tiling starts. - What goes into the price - Stripping out and making good - Plumbing moves, if any - Tiles, fittings and a day for silicone - /static/uploadedImages/3f9c…e1.jpg - We don't add a margin on materials you choose yourself. - Details - Created - 12 Mar 2026, 09:14 - Updated - Today, 11:02 - ID - Images - Public API URL - /v1/article/12 - Title - Titles can't be changed after an article is created. To use a different title, create a new article and delete this one. ### Editor (group "Editor", 41 states) Route: `/article/:id/edit`. API: GET /v1/article/{id} · PATCH /v1/article/{id} · PATCH …/publish|unpublish · POST …/image · DELETE. On mobile, Save and Publish sit in a sticky bottom bar and the details panel opens as a slideover. A session expiry mid-edit keeps the draft in sessionStorage and goes to /login?redirect=/article/12/edit. After logging in, the editor restores the draft and shows "Unsaved changes". | # | State | Trigger | Copy | |---|---|---|---| | 1 | Loading | GET in flight. Skeletons for the title, content and details. | | | 2 | Saved | Just opened or just saved. Save is disabled. | | | 3 | Unsaved changes | Any edit. Publish becomes "Save and publish". Leaving asks for confirmation. | | | 4 | Saving | Save, or Ctrl/⌘ S. | | | 5 | Save failed | 500 or network failure. The changes are kept and Retry resends them. | | | 6 | Very long article | No length limit. The count stays neutral and correct at 120,000 characters. Save and Publish stay enabled. | | | 7 | Very long article: saving | Save with large content. The status shows the spinner and the page stays editable. | "Saving 120,000 characters. This can take a few seconds." | | 8 | Very long article: saved | 200. Status reads Saved with the time. The count is unchanged. | | | 9 | Very long article: save failed | 500, 413 or network failure. Nothing is lost; Retry resends the same content. | "Couldn't save. Your text is kept. Retry." | | 10 | Preview | The Preview switch. Shows the title and body in the content scale, the way a public site might. | | | 11 | Link: create from selection | Ctrl/⌘ K or the link button with text selected. Link text is prefilled from the selection; URL has focus. | "Link text" / "timber windows" / "URL" | | 12 | Link: create with no selection | Nothing selected. Link text is empty and has focus. | "Link text" / "What the reader sees" / "URL" | | 13 | Link: edit existing | Edit in the link popover, or Ctrl/⌘ K inside a link. Prefilled; "Remove link" is offered. | "Edit link" / "Save link" / "Link text" / "timber windows" / "URL" | | 14 | Link: invalid URL | On submit or blur. Nothing is inserted. | "Link text" / "timber windows" / "URL" / "Enter a valid URL. Use http(s), mailto, a path or #anchor." | | 15 | Link: empty text | On submit with empty link text. | "Link text" / "Enter the link text." / "What the reader sees" / "URL" | | 16 | Link: long URL | The field truncates in the middle of the line; the full value is available by scrolling the field and in the title attribute. | "Edit link" / "Save link" / "Link text" / "planning guidance" / "URL" | | 17 | Link button: default | Toolbar button, icon link, after the list buttons. Desktop 28 px, mobile 40 px. Text colour --ui-text-toned. The toolbar scrolls sideways on mobile (see "Toolbar overflow"). | | | 18 | Link button: hover and tooltip | Pointer or keyboard focus after 400 ms. Tooltip "Insert link (Ctrl+K)"; on macOS it reads "Insert link (⌘K)". Touch has no tooltip. | | | 19 | Link button: focus-visible | 2 px --ui-primary outline, 1 px offset inside the toolbar. Enter or Space opens the link dialog. | | | 20 | Link button: active (cursor in a link) | The cursor or selection is inside a link: aria-pressed true, --rc-sand fill and --rc-on-sand icon. Pressing it opens the dialog in edit mode. | | | 21 | Link button: disabled | An image is selected in the editor. The button is at 40% opacity, aria-disabled, no tooltip. Ctrl/⌘ K does nothing. | | | 22 | Link popover and sheet | Click a link, or Enter on a focused link. Desktop: popover under the link with the URL and Edit, Open and Remove. Mobile (touch): bottom sheet with the link text, the URL and Edit link, Open link, Remove link at 48 px. Esc or a tap outside closes it. Remove asks for no confirmation; undo restores the link. | | | 23 | Rendered link: default | Same as the published page: --ui-text-highlighted, 1 px underline, 3 px offset. | | | 24 | Rendered link: hover | Underline thickens to 2 px. | | | 25 | Rendered link: focus | Tab or arrow keys into the link: 2 px --ui-primary outline, 2 px offset. | | | 26 | Rendered link: selected | Text selected across the link: --rc-sand fill, --rc-on-sand text. | | | 27 | Toolbar overflow (390 px) | The full toolbar does not fit at 390 px. It scrolls sideways in one row (no wrap); a 24 or 32 px fade marks each hidden side. This frame is scrolled to the end, so the link, remove-formatting and Image buttons are visible. Desktop is unchanged. | | | 28 | Upload: choose | The image button in the toolbar, or a file dropped on the editor. | "Add an image" / "Cancel" | | 29 | Upload: crop and resize | A JPEG, PNG or WEBP passed the checks, and nothing is sent yet. The crop starts as the whole image and the width defaults to 1600 px. SVGs skip this step. | | | 30 | Upload: crop to a ratio | 16:9 chosen. The frame keeps the ratio while it's dragged or resized. Arrow keys move it; Shift and arrow keys resize it. | | | 31 | Upload: crop narrower than the width | The crop is smaller than the chosen width, so it keeps its own size. | | | 32 | Upload: in progress | Upload image was pressed. The cropped, resized file is uploading. Closing the dialog cancels the upload. | "Add an image" / "Cancel upload" | | 33 | Upload: rejected | Checked in the browser, so nothing is sent. | "Add an image" / "Cancel" / "Choose another file" | | 34 | Upload: inserted | 201: the image goes in at the cursor, and the article now has unsaved changes. | "Image added" / "Save to keep it in the article." | | 35 | Publishing | "Save and publish": the PATCH body, then the publish request. | "Saving and publishing…" | | 36 | Published | 200: the badge flips, Unpublish is offered and a toast confirms. | "Article published" / ""How we price a bathroom renovation" is now online." | | 37 | Unpublishing | Unpublish doesn't save pending edits. | "Unpublishing…" | | 38 | Delete: confirm | Delete… from the overflow menu. After a 204, it goes to /article with the "Article deleted" toast. | "Delete this article?" / ""How we price a bathroom renovation" and its 3 images will be deleted. This can't be undone." / "Cancel" / "Delete article" | | 39 | Leave with unsaved changes | Back, a nav link or closing the tab. | "Leave without saving?" / "Your changes to "How we price a bathroom renovation" haven't been saved." / "Discard changes" / "Keep editing" | | 40 | Offline | The connection dropped. Writing continues, and Save and Publish wait. | | | 41 | Not found (404) | Unknown id, or the article was deleted elsewhere. | |