Replace the oversized header and reference rail with stable editor slots and a retained supporting drawer. Bind diagnostic, result and menu actions to their original publication and visit; preserve effective selections on navigation and dismiss Actions, Find and the drawer one surface per Escape. Retain exact Windows source captures, eight final gates (871 desktop passes), independent reviews, native selection/rename/recovery trials and the original failures. All 526 tested inputs and staged evidence were byte-checked before commit. Combined preview integration and packaging remain with issue 27; Linux remains paused.
14 KiB
Text workspace interaction contract
This contract derives the editing-first layout for user issue 13 from the project goal and user-owned specification. It preserves the source/model distinction and navigation invariants in the text navigation design, integrated navigation record, and source alias rename design.
The dimensions and interaction rules below are derived design decisions, not quotations from the user. The verified Windows contribution links final gates and native correction evidence; combined peer integration and release acceptance remain separate. The first native trial retains the original Find selection defect and its failed acceptance. Each trial binds its captured source, not later corrections. Authored tests alone do not establish conformance.
Editing remains the primary surface
The Text center has five permanent sibling slots, inside a permanent contextual-menu host:
| Slot | Allocation | Responsibility |
|---|---|---|
| File/actions | 42 px | Current source path, Find, scoped Actions, Apply all |
| Find | 0 or 36 px | Explicit literal Find and Go to line |
| Editor | Remaining height | The same container, sensor, syntax highlighter and text editor |
| Supporting drawer | 0 or explicit requested height; default 180 px | References, Problems, Status |
| Source status | 22 px | Source error/warning counts and adoption/recovery state; always opens Status |
Visibility never inserts or removes a sibling before the editor. The editor's Content, selection, focus and scroll are not replaced merely to rebuild a view. Existing reveal tickets and interaction-scoped editor IDs continue to authorize actual navigation; rebuilding or resizing does not itself authorize a caret jump.
Returning focus from Find is not cursor navigation. Close and Escape retain the live editor's actual selection, including word and line selection modes, and its scroll. The guarded focus operation must not reconstruct selection from cursor endpoints: those endpoints alone do not preserve every selection mode. Subsequent text input replaces exactly the text selected before Find. Later interaction or a stale focus ticket still revokes focus restoration.
For a successful navigation departure, saved selection means the exact selected byte range and subsequent replacement behavior. Preserve forward/reverse direction when the editor exposes distinct anchor and active positions; an expanded word/line with coincident raw positions restores with the active endpoint at the range end. Back and file return restore this explicit range on fresh editor content. They do not retain the hidden word/line drag-extension mode or interior click point. This is distinct from Find, which keeps the actual live selection untouched. Capture occurs once after destination admission, reused for the outgoing buffer and navigation history. Failed/no-op requests cannot normalize selection. The implementation uses the supported editor collapse-to-start operation plus the copied selection's line/byte extent, with no ambiguous source-text search, full-file history snapshots or dependency fork.
Drawer size is calculated from the allocated Text center height, not the operating-system window height or an assumed toolbar margin. After the fixed strips, an open drawer normally retains at least 180 px and leaves 240 px for the editor where both fit. For smaller allocations, the default drawer is retained and the editor receives the remainder; impossible geometry must not be described as a guaranteed minimum. For example, a 480 px Text allocation with Find open leaves 200 px editor and 180 px drawer. A larger host permits an expanded drawer while retaining 240 px editor.
Larger/Smaller and local Alt+PageDown/Alt+PageUp explicitly request changes. Controls use the effective measured drawer height, so Smaller responds immediately after a previously oversized request is clamped by a window resize. The controller rejects non-finite requests and bounds stored preferences; layout applies the actual allocation bound. Closed drawers claim no resize shortcut. A contextual menu consumes input before the underlying workspace. Passive lint, query completion and scroll notifications must not open or enlarge a drawer, focus the editor, or change the stored height.
The drawer retains fixed References, Problems and Status positions. Only the active body is populated. Each tab has one scrolling body; no fixed-height scroller is nested inside another. The persistent diagnostic Guard remains mounted even when hidden, with no publication and an empty child, so an armed release can still be consumed. Zero-height clipping alone is not an input guard.
Retained results do not retain permission to navigate
The Text UI owns drawer visibility; a second independent source_symbols.open flag must not compete with it. An explicit query chooses References at initiation. Hide and tab changes retain the latest query's results, captured subject, groups and scroll, while revoking pending navigation intent. Completion releases its matching occupied worker but cannot reopen a hidden drawer or switch the user's tab.
Display ownership and navigation permission are separate checks. A hidden completion may replace retained display data only for the latest owned query and an exactly current source snapshot. A direct-definition jump additionally requires the original interaction, raw input interaction, source path, caret, snapshot and navigation admission. Keeping a result is never permission to jump. Session/document replacement clears this presentation; changed sources make retained results stale.
Changing a source destination, including Save As or a hosted destination, resets retained navigation and drawer state while preserving an occupied worker until its matching completion. Current diagnostics may be republished with a fresh generation only when their publication matched the source immediately before this identity-only reset. Already stale diagnostics must not be relabeled as current. Installing a different repair workspace likewise clears the previous project's presentation before publishing its own diagnostics; an Open that installs nothing preserves the current presentation.
Group actions carry the original query stamp, drawer visit and path. A queued action from a previous visit is rejected before changing groups or invalidating navigation intent, including after another accepted group change. Result actions retain original result indices, never visible-row ordinals. The origin file, or first matching file, initially expands; other groups are quiet. Expansion and scroll survive navigation and tab changes. Explicit restoration targets a fresh visit-scoped scroll ID; an old queued operation must not affect a later visit, including A→B→A. Passive completion performs no restoration.
One drawer visit identity owns both source-location actions and scroll restoration. A retained result choice must match the visible References visit; a diagnostic target must match the visible Problems visit. Rejection precedes interaction invalidation. Changing group expansion also rotates this identity because the visible row positions change, while retaining the selected groups and saved scroll. Both lists keep a permanent input guard inside their single scroller. Its identity combines the captured query job or diagnostic publication with the drawer visit, so a close/reopen coalesced into one redraw still cancels an old pressed row.
Show the captured alias, total counts and exceptional scope honestly: stale, partial, collision, detached/unverified and omitted results. Critical stale/partial indicators must remain visible at minimum width even with a long wide-Unicode alias; a clipped suffix is insufficient. Full explanations and paths remain available in the body without hover. Ordinary file headers and result rows target 24 px and 28 px respectively, allowing at least three useful results in one expanded group at the default drawer size.
Diagnostics and commands carry their original authority
Every diagnostic replacement or clear publishes a fresh generation with its source snapshot. Both primary and related location actions capture that publication and an exact source location. Dispatch checks the current generation, current snapshot, loaded path, valid byte boundaries and normal navigation admission. It must not reinterpret an old vector index against a new list.
The persistent Guard separately addresses pointer-down/list-replacement/pointer-up. Replacement cancels the armed child state and consumes its release instead of activating a new row at the same coordinates. Hiding the body retains that release guard. Stale queued messages and stale pressed widgets are distinct failure modes and require distinct checks. Source annotations, squiggles and hover rendering from issue 12 are not implemented by this contract.
Scoped commands capture document identity/revision, source snapshot, path, caret and interaction. A menu freezes its ordered labels, enabled actions and context for one invocation. Its popup token comes from the shared monotonic menu sequence, preventing unrelated menus from reusing widget interaction state. Validate and consume an invocation before dispatch, then recheck command-specific authority. Never regenerate a pressed row into another action. The permanent Popup owns dismissal releases and keyboard input while open.
The shared popup sequence survives document reset. The captured-Escape subscription is disabled while a Text menu owns input, because that popup already sends its tokened dismissal. One Escape closes the menu without also closing Find or the drawer beneath it. After higher-priority modal handling, successive Text Escapes close Find first and then the supporting drawer, one surface per key. Find dismissal preserves the drawer's visit, results, groups and scroll; drawer dismissal retains its data while revoking navigation authority. Native trial 2 identified the inherited simultaneous Find/drawer dismissal that motivated this explicit priority; that trial does not verify the later correction.
Find and text Undo remain useful with drafts. Text Undo affects one draft; model Undo restores accepted source files together; Undo rename uses the existing transaction authority. Status explains all simultaneous recovery, repair, edited-source and detached-draft conditions, including why recovery is unavailable and which graphical model remains active. The status strip always opens this same destination rather than changing its action according to the highest-priority condition. Global operation feedback and model checks remain outside this source-status summary.
Alternatives and boundaries
The former 42%-height header protected pointer targets from asynchronous lint changes, but spent that space even during ordinary editing. A fixed 300 px reference rail reduced text width and changed the editor wrapper topology when opened. The chosen bottom drawer recovers the closed editor area and preserves width, while explicit expansion supports longer investigations. Revisit a side inspector if realistic repeated navigation still requires excessive drawer manipulation; do not hide necessary warnings to make the chosen layout appear successful.
Existing palette and typography roles are retained. Global menu organization belongs to issue 19, and palette work to issue 26. The future diagram-usages command delegates to the peer-owned diagram chooser from issues 14/17; it must use that chooser's captured context and reveal semantics. Until that adapter exists, omit the command rather than present lexical References as diagram usages. This contract changes no model membership, source-language meaning, document transaction, or filesystem authority.
Verification and falsifiers
The relevant implementation seams are workspace presentation, typed controller, source query integration, and root view/diagnostic integration.
Acceptance requires the combined implementation to demonstrate:
- The real sensor/highlighter/editor retains selection, focus and an actually scrolled buffer through explicit Find/drawer changes and passive 0/1/100 diagnostic refreshes. Closing a focused Find field restores only the currently authorized editor; a stale operation cannot steal later focus.
- Minimum and wide allocated hosts, forced oversized preferences, pointer controls and exact resize shortcuts produce bounded geometry. Observe actual text clipping and useful rows, not only outer boxes. Include a 32-wide-glyph alias with every scope warning.
- Replaced primary and related diagnostic messages are refused, and a mounted press/replacement/release activates neither the old nor new row. Hidden/tab-switch paths must preserve the release guard.
- Frozen menu rows cannot retarget after source, revision, caret, menu-token or model-context changes. Dismissal releases do not reach the editor.
- Pending completion after Hide frees the worker without reopening or jumping. Newer queries win; stale snapshots never appear fresh. Grouping and visit-scoped restoration survive repeated navigation across a 30-file fixture with duplicate basenames.
- Combined recovery/edit/detached conditions remain understandable and actionable. Native editing, source/model Undo and rename transactions preserve complete source and model expectations.
The view contribution includes authored allocation, shortcut, retained-state and real graphics-editor scroll tests. Those tests are not native paint or usability measurements. A passing local subset cannot substitute for integration lifecycle tests, a successful combined build, and representative native workflows.