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.
11 KiB
Back/file selection snapshot research
Read-only source research, Codex-9e8r6t /root/native_archive, 2026-09-13. No source edits, compilation, application/UI execution or vendoring. Native Find Close failure is established separately by trial-text-1. Back and file-switch defects below are source-derived, not new native observations.
Stable input and requirement
Inspected the immutable attempt-4 source ZIP C:/ar-text-9e8r6t/.local/text-workspace-gates-20260912/attempt-4/inputs.zip, SHA e1e566295bbbd6c1d571f7c8908b7344397293496481790f0f8dacab7eba5366. Relevant member hashes:
| Member | SHA-256 |
|---|---|
crates/oa-desktop/src/app/source_symbols.rs |
1fb3709bb973e7dcfbcde56054d028e47d770a62f3b7c8b6fac411543a234b7b |
crates/oa-desktop/src/source_workspace.rs |
388c71f63829c0c76526a1700ebf5f240c86c467a65aae4fca4cd6c6a88c468b |
crates/oa-desktop/src/app.rs |
f3a61976c1ddbd3a80ae082858dfaa28652156c3af98c6b8fd5d9d6ce294b2b0 |
crates/oa-desktop/src/source_navigation/controller.rs |
7003533fbc3f1d31bf667555faaf81015f6a5d8fffd869e34e15395ce94835fe |
harness/release-text-navigation-design-20260911.md, interaction rule 7, requires an unchanged Back origin to restore its exact selection. Changed origins intentionally use the current retained cursor with a notice, rather than pretending the historical offset is exact. This is separate from the current focus-only Find correction and must not weaken its no-caret-move behavior.
Concrete failure chain
source_symbol_jump stores self.source_editor.cursor() in HistoryEntry; Workspace::switch stores the same representation in Buffer.cursor. source_symbol_back selects that saved cursor for an unchanged origin. File restoration and Back reconstruct Content then queue a reveal that calls move_to.
Iced graphics Editor::cursor maps all Cosmic Normal, Word and Line selections to the same two raw positions. For a double-click word, those raw positions can coincide inside the word even though the effective selected byte range is the whole word. Editor::move_to restores any Some(selection) as Normal. Consequently, the saved representation cannot restore the effective Word/Line selection on unchanged Back or a file round-trip. The equality of raw Cursor structs is not a sufficient test oracle; typing can insert inside the token instead of replacing the original selection. Normal explicit endpoint selections do not suffer this particular loss.
Public API boundary
The cached primary implementations are:
Source under .local/toolchain/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ |
SHA-256 |
|---|---|
iced_widget-0.14.2/src/text_editor.rs |
cecf8686c90aa92066d55075beba545dfb7dd5a44bad0c1519fcb110a56070e1 |
iced_graphics-0.14.0/src/text/editor.rs |
b12c4e645a4db26c79e421a88c2127c8c20b7ff0f5a6fae9a8e050f40bf39567 |
iced_core-0.14.0/src/text/editor.rs |
717158d90d372183ee07483ad950c648fe79acdaa3c61db4d006b924426d0eee |
cosmic-text-0.15.0/src/edit/editor.rs |
9d61a742fea99db9044fce28a253f790f6b48369c24ec9646a3209a6a69fa1cb |
Content::selection() returns copied selected text. The public Editor::selection() returns visual caret/rectangles, not logical bounds or selection mode. Content::clone() recreates text with with_text, losing selection and scroll; it cannot make a selection probe. Cosmic logical selection bounds are inside the graphics editor, whose internal edit state is private. Inferring ranges from painted rectangles or searching copied text in the source is unnecessary and unsound for repeated text.
Small supported candidate: canonical range at committed departure
Use the existing Cursor storage as a canonical effective range, rather than introduce whole-file snapshots or a new history authority. A helper needs mutable Content only at the successful departure boundary:
- Read the original raw cursor and
Content::selection()before modifying selection. If there is no effective nonempty selection, retain the caret representation; do not issue Left on an unselected caret. - Perform public
Action::Move(Motion::Left). GraphicsEditor::perform, lines 315–339, explicitly obtains effectiveselection_bounds, clears selection and sets the caret to its start. It does not edit text. This works for Normal, Word and Line selections. - Read that start from
Content::cursor().position. Derive the end by position arithmetic on the already copied selection, not searching: Cosmiccopy_selection, lines 495–520, joins selected editor lines with one LF regardless of physical line endings. No LF meansend.column = start.column + selected.len()on the same editor line. Otherwiseend.line = start.line + LF count, andend.columnis the UTF-8 byte length after the last LF. These are editor byte columns; inspectContent::lineboundaries if validating. Do not rewrite or normalize the source itself. - Store the resulting endpoint pair as a Normal
Cursor. Preserve forward/reverse direction for unequal original anchor/head positions. When Word/Line raw positions coincide inside an expanded range, choose a documented canonical active endpoint, e.g. the end. Restore this explicit range on the fresh Content used by Back/file return.
This retains only constant-size coordinates and temporarily copies O(selected bytes), not O(file size) when the selection is small. Performing Left followed by Right does not obtain both old boundaries: Left already clears the selection, so Right would move normally. The copied-selection arithmetic is what avoids a second selected editor snapshot.
Capture only after all target, session, snapshot and admission checks have succeeded and immediately before an actual jump or different-file switch. Do not normalize during query capture, pending work, failed/stale completion, invalid file selection, a no-op click on the active file, or focus restoration. Keep raw query-request cursors unchanged for their existing freshness comparisons. Reuse one departure capture for both history and the outgoing buffer cursor. There must be no source edit, Apply, Save or Undo-history mutation from capture.
Explicit semantic boundary: this preserves exact selected bytes and subsequent replacement behavior. It does not preserve the Word/Line gesture-extension mode or the clicked interior caret as a distinct hidden coordinate. If the requirement is exact continued word-drag/line-drag behavior after returning, canonical Normal ranges are insufficient; that stronger requirement needs a richer supported editor API or bounded retained editor state. Do not silently claim that property. For the existing Back rule's selected-byte equivalence, canonical ranges are a proportionate candidate to verify.
Keeping the actual Content object per history entry would retain richer state but can hold whole files/shaping data across 64 entries; it requires an explicit memory policy and is not the smallest change. Tracking guessed selection modes from input, reconstructing words with custom lexical/string-search heuristics, unsafe access and broad dependency vendoring are rejected for this bounded correction.
Required placement and guards before capture
| Boundary | Checks that must precede the mutable helper |
|---|---|
source_symbol_jump |
Preserve caller's query/result/session/interaction/raw-input admission. The target file must exist and nav::exact_cursor must accept the intended target range. Only then capture the outgoing effective selection and use that same cursor for HistoryEntry and Workspace::switch. A valid same-file symbol jump is a real departure and may capture; a failed target must not. |
source_symbol_back |
Existing Back admission first; pop/skip foreign sessions, require the origin file, choose unchanged generation vs changed-current fallback, and validate the eventual destination before normalizing the outgoing editor. Empty history or invalid origin returns without capture. If the changed-origin fallback is the currently active editor, reuse its newly canonical captured range rather than the lossy raw cursor. Preserve the changed-position notice and no-history-push behavior. |
Message::SourceFile(path) |
Preserve existing outer input/modal admission, then explicitly require path != active and loaded membership before helper invocation; those conditions currently live inside Workspace::switch. Moving capture before an unchecked switch would mutate selection on invalid/no-op requests. After checks, capture once, switch, and restore the destination buffer's canonical cursor. |
jump_to_source |
Preserve each caller's authority: current diagnostic publication/snapshot/visit/path/span or the separately authorized model-to-source action. Require a loaded target and establish the destination coordinates before capturing outgoing selection. The old function calls switch before its missing-file failure check; do not insert capture before that check. Keep its existing line-selection/reveal semantics. Do not add a blanket Text-only admission that breaks authorized model-to-source entry from another page. |
The attempt-4 source_navigation_available predicate requires Text page, no rename dialog, no busy/pending/file/delete/workflow/hosted/model-menu/Text-menu/architecture/behavior modal context. Continue checking that predicate at existing lexical navigation entry points. A new helper is not an authority bypass and must not replace captured-message freshness guards. File creation, load/recovery replacement, source edits and pending query snapshots should not acquire departure normalization as a side effect of a broad call-site search/replace.
Required discriminating checks
- Actual mounted Word and Line selections, including word-drag/line-drag if canonical byte-range coverage is intended, must have identical
selection()before departure and after restoration; subsequent typing must replace exactly those bytes. Compare exact final source bytes, not merely cursor structs. - Cover forward/reverse Normal ranges, multiline LF/CRLF/mixed endings, non-ASCII text, repeated identical aliases, trailing newline, empty line and no-selection caret. Verify source bytes remain unchanged by capture and stored history remains bounded.
- Exercise real successful definition/reference jump→Back with unchanged origin and a real different-file round-trip. Retain the existing changed-generation fallback and notice; do not restore stale historical ranges into edited sources.
- Failed query, stale completion, invalid/no-op file selection and Find focus-only round-trips must not normalize or move the current selection. Their subsequent native/mounted replacement behavior remains the original behavior.
- Source/model revisions, complete bundle, text Undo and accepted-model history must be unchanged until an explicit user edit. No filesystem action belongs to capture.
These are proposed evaluators, not executed results. The parent owns the correction decision, implementation partition and serialized gates/native confirmation.