put the open file in the url on /code-editor

audit m5. the active file is `?file=`, tree file rows and tabs are links, and a
`?file=` naming something that is not open now opens it — which is the part that makes
a pasted link actually work rather than just describe.

the open-tab *set* stays local state and i want that on the record as a choice, not an
omission. it is a working session, not an address: it grows without bound, every entry
costs a read on load, and nobody has ever linked someone else to a tab bar.

opt-in via a prop from the screen rather than the workspace identity the file browser
uses, because /code-editor renders CodeEditorView directly inside a Widget instead of
through the panel wrapper — there is no workspace to ask. a dashboard editor is
unchanged.

tree *folder* rows stay buttons, and unlike the file browser's folders this needs
nobody's call: expanding a directory is disclosure, not navigation.

two things fixed while in here. the tab close control was a role="button" span nested
inside the tab's own button — invalid before, and a nested interactive inside an anchor
after — so it is a sibling button with an aria-label now. and closeFile picked the
next-active file inside a setFiles updater, which is the impurity react double-invokes
in development to catch.

a path that fails to read is remembered, so a broken link errors once instead of once
per render, and the address is left alone rather than rewritten.
This commit is contained in:
2026-08-07 12:34:10 +00:00
parent 5daa598b63
commit 990ead93b9
6 changed files with 202 additions and 69 deletions
@@ -4,7 +4,9 @@ import { Widget } from 'widgets/Widget';
export const CodeEditor = () => (
<div className="h-full w-full flex items-center justify-center">
<Widget title="Code Editor" className="h-[70vh] w-[70vw] overflow-hidden" resizable moveable>
<CodeEditorView className="h-full w-full" />
{/* The screen is the one editor that owns the address bar, so the open file is `?file=`. The panel
wrapper deliberately does not pass this: a dashboard can hold two editors. */}
<CodeEditorView className="h-full w-full" urlState />
</Widget>
</div>
);