editor: resizable rail, Esc closes panel, dev tab remembered

Rail gets a drag handle on its edge (240-640px, width persisted like
the rest of the panel state). Escape now closes the panel as the last
step of its chain (dialog > inline > selection > panel). The dev panel
remembers its tab across reloads. Panel toggle button title notes the
Esc shortcut. Harness covers resize/persist, Esc-close and dev tab.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
This commit is contained in:
mats committed 2026-11-07 12:40:46 +01:00
1 parent bb875a0956
commit fc0eb2c6be
8 files changed
+111 -13

No files matched your search

+31 -2
View File
@@ -17,9 +17,13 @@ import type { Block, Controller } from '../controller'
* - Blocks are the new types that fit this page (drag onto the page or the outline).
* - Nodes finds *existing* nodes (type filter + text) to link into the page.
*/
const RAIL_W_MIN = 240
const RAIL_W_MAX = 640
export function Rail() {
const ctrl = useCtrl()
const open = useEditorState((s) => s.rail)
const [width, setWidth] = usePersistentState<number>('cms-onpage.railwidth', 320)
useEffect(() => {
if (!open) return
// Types carry every schema (titles, groups, slots): one request seeds them all.
@@ -33,16 +37,41 @@ export function Rail() {
void ctrl.fillHandles()
}, [open, ctrl])
if (!open) return null
const w = Math.max(RAIL_W_MIN, Math.min(width, window.innerWidth - 24))
const startResize = (e: React.PointerEvent) => {
if (e.button !== 0) return
e.preventDefault()
const doc = e.currentTarget.ownerDocument
const rail = e.currentTarget.parentElement!
const move = (m: PointerEvent) => {
const left = rail.getBoundingClientRect().left
setWidth(Math.round(Math.max(RAIL_W_MIN, Math.min(RAIL_W_MAX, m.clientX - left))))
}
const stop = () => {
doc.removeEventListener('pointermove', move, true)
doc.removeEventListener('pointerup', stop, true)
}
doc.addEventListener('pointermove', move, true)
doc.addEventListener('pointerup', stop, true)
}
return (
<aside className="cms-rail" aria-label="Page panel">
<aside className="cms-rail" aria-label="Page panel" style={{ width: w }}>
<header>
<span className="cms-rail__title">
<Icon name="outline" size={16} /> Page
</span>
<button className="cms-ico" onClick={() => ctrl.setRail(false)} aria-label="Close panel">
<button className="cms-ico" onClick={() => ctrl.setRail(false)} aria-label="Close panel" title="Close panel (Esc)">
<Icon name="close" />
</button>
</header>
<div
className="cms-rail__resize"
role="separator"
aria-orientation="vertical"
aria-label="Resize panel"
title="Drag to resize"
onPointerDown={startResize}
/>
<div className="cms-rail__body">
<OutlineTree />
<Palette />