Skip to content

Latest commit

 

History

History
577 lines (507 loc) · 36.3 KB

File metadata and controls

577 lines (507 loc) · 36.3 KB

Data and editor model

This describes the implementation as of 2026-09-05. It is a reference for working on the code, not an attribution of design intent to the owner. Earlier models and unverified rationale are preserved in historical notes. Open work is listed separately in deferred work.

Data and resolution

GID defines Value as cell references, blobs, lists, and records. A document has an optional root and a CellId -> Value table. An identity with no table entry is a bare cell. Record labels are cell identities; text, names, numbers, absence reasons, and domain data are library conventions.

A cell may accidentally occur in several sources, for example when editing a document that also exists as a loaded library. Normal lookup selects the document definition first, otherwise the first loaded library definition. Names, structural display, and cell evaluation use that value. Definitions are neither merged nor implicitly composed. Duplicate indicators and inspection UI are deferred.

Each library has one definition per cell: either an ordinary value or a native definition pairing a descriptive value with its Rust implementation. Reads and calls use the same lookup. Reading a native definition uses its description; calling it invokes its implementation. An absent or non-callable result does not try another source. Library descriptions (currently name records) live in that same table under the library identities, not in a separate metadata store. Replacing a loaded library replaces all its contributions in place, including projections and completions, before those are composed. Explicit source-qualified paths still reach a particular definition; loaded library values remain read-only. See Sources, Library and Libraries, and Grap evaluation.

The text bridge is a temporary import/export representation. Its binders and spelling do not add GID semantics. Native binary storage is not implemented; see the bridge format.

Occurrences, definitions, and views

A Path is a sequence of GID steps interpreted in a supplied document and library context:

  • Key(cell) enters a record field.
  • Element(position) enters a list element at its stable position.
  • Follow(Document | Library(id)) crosses a cell into a particular definition.

The empty path is the document root; it need not be a cell. Library sources use stable identities, so changing load order cannot silently retarget a Follow. List positions preserve an occurrence across edits that retain that position; they are not content identities or globally meaningful addresses.

Selection also names its owning workspace view. Two panes can show the same stored path while keeping separate focus, scroll, and folds. A workspace Root supplies this session identity; it is not a GID cell.

SourceTrace serves source-linked widgets, drawing, and highlighting. It is either a stored path or a cell, definition source, and path relative to that definition. Normalizing to the nearest followed definition lets different occurrences highlight the same source without conflating two libraries' definitions of one cell. Both forms still need the caller's resolution context. Neither is an execution stack or global address.

Computed values use at at their own displayed occurrences. They and their children can be selected, copied, and folded, but have no document destination for edits. Selecting a result does not select its producing expression. Computed output uses the same read-only ground as external definitions. Nested read-only occurrences do not stack washes; an explicit jump to writable source restores the paper ground. This decoration does not change edit permissions: those still follow the occurrence's source/conject. A reference to an external definition can itself still be replaced or removed from a writable document.

Selection and editing

Selection owns a workspace root, path, GID payload, and optional Rust editor. Its stages distinguish an existing edge, a pending value, and a pending record label. Annotation records live in a path-keyed Annotations trie owned by the view; folding is one convention in those records.

Selection also retains a native editing scope. Ordinary document projection uses its allocation-free identity case. A scope installs a conject at an occurrence: an ordinary function of the remaining projection path and a document path, returning Option<DocumentPath>. Reads and writes use the same function; there is no independently configurable read/write pair or inverse mapping. Several occurrences may conject to one source. Selection, navigation, and annotations stay occurrence-local, while source-linked hover uses the conject. Line editing, gestures, completion insertion, structural paste/deletion, and history retain that interpretation. Undo restores the scope with the selected path; scopes contain no document snapshot or borrow of the editor. The scope stores a resolver function from occurrence path to optional document path. The standard boundary constructor captures the occurrence prefix, document starting path, conject, and enclosing fallback in that function; there is no separately interpreted route record. Identity scopes borrow their input path without allocating. If a projection changes what a selected occurrence means, its caller is responsible for clearing the selection when necessary; the editor does not try to repair arbitrary changes of interpretation.

descend extends the current occurrence, jump gives a new occurrence an actual document source, and at projects a supplied value without a source. None means no document location, not a missing value: Some(path) can name an absent field and still offer editing. A library source is present but read-only. Ordinary descendants of at stay detached, including cell follows; an explicit jump can establish a source again. Outline list entries project their referenced fields through jumps: the heading is the real list element, and its body has an occurrence beneath that element. Repeating a field yields independent folds and selection locations while both bodies edit the same source field.

Copy and fold do not resolve an occurrence through conject. The shared projection boundary installs handlers that copy the displayed Value and change the occurrence's fold annotation using the default already determined during projection. This also works for source-less at values. Inner widget handlers take precedence (for example, copying a text selection). Structural cut copies that same value, then attempts deletion through the selection's conject; a source-less value can be copied but not deleted.

Ordinary selection does not initialize editing state. Its role is derived from the current location: a writable missing value uses the pending picker, while an existing value is selected normally. An explicit pending or label payload can still request those modes intentionally, but no caller needs one as setup. The picker renders an empty query by default without writing selection state and materializes its editor only on input; query, caret, choice, scroll, and expansion state are optional. This applies to empty roots, bare-cell definitions, missing record fields, and inserted list positions, without a special selection callback for each. Read-only locations do not enter a picker.

The live LineEditState owns text, caret, IME, and text-drag state. A projection at the selected location receives a GID description derived from that state. A capability replacement decodes it once into the live editor; there is no second retained editor representation to synchronize. The path library encodes typed paths as ordinary GID lists: {key: cell}, {element: {indexable: blob}}, and {follow: document} or {follow: {library: cell}}. Decoding checks each step and the canonical list-position bytes. Encoding a position does not extend its lifetime: loading a document still regenerates its list positions.

Generic Grap handlers receive GID events and caller-supplied capabilities. Site and selection access are scoped foreign calls. site path returns the projected site's document path; selection get reads the selection payload there. selection set takes an explicit path and value, interpreted in the supplied document and view, so it can move selection to another location. An absent value clears selection only at the specified path. Reads observe staged writes; an explicitly declined or halted handler commits no effects. Ordinary absent results keep preceding effects. Setters return {} on successful set or clear; absent remains an input convention for clearing, not its return value. Site and selection getters return their own missing-state reasons when empty. A function must decline before performing effects, including effects in its arguments or nested calls. Declining afterward halts the evaluation and prints an error. Only the complete editor operation is staged, with no per-call snapshots or rollback. Tests can replace these foreign functions with a recording interpreter.

The projection supplies a line's spelling, presentation, and conversion callback. The current line handler owns the callback; selection retains neither a Rust callback nor a Grap callable. The line is the stock native widget function carried by Layout::widget; layout has no line-editor constructor or interpreter arm. The editor supplies current state and scoped editing capabilities, then incorporates the widget's render closures, handlers, hover claim, and navigation transition. Progred's line control runs an editing operation, then converts only when the accepted operation changed the text. The callback receives the live value and spelling; None declines a write. An equal result does not rewrite the document. Invalid intermediate text can remain in the editor while the last valid value remains in the document. Loaded library values decline writes. Projections decide how unrelated fields survive an edit. Native atomic libraries use native conversion functions; the line library's grap adapter calls a Grap conversion with text and current value as data, mapping absent to None.

The line control groups the first document write into undo and coalesces later writes in that editing run. The shell no longer runs a general post-event conversion step. Query editing similarly resets its completion choice and scroll at the editing operation, not after unrelated events.

In the normal projection, blobs use a monospace hex line with a fixed 0x prefix. The buffer contains the full hex digits, including for blobs longer than the structural summary. Edits accept complete bytes in either case; empty hex denotes an empty blob. Query entry and editing share the blob library's parser. Raw retains its compact structural blob display.

A selected, writable line with no editing state uses its current projected spelling with the caret at the end. Rendering and input use the same default; projection does not store it just because the line is selected. On an editing interaction, the line control materializes state from that default. Its current handler supplies conversion. Existing caret, in-progress spelling, and IME state take precedence. Raw and read-only views do not acquire an editor from a plain selection.

Ordinary selection therefore needs only a location, including after completion, paste, deletion, and undo. Navigation targets receive an optional arrival direction. The line widget places the caret at the beginning when moving Right into it and at the end when moving Left; ordinary and vertical arrivals keep the default. This policy belongs to the widget, not navigation dispatch. Pointer placement remains an explicit interaction. The shell does not inspect values or the render tree to infer editability. See navigation.

Unhandled arrows, regardless of modifiers, become Event::Navigate(direction) after focused controls and editing handlers decline the raw key. These semantic events run through the ordinary handler chain. A frame contains only the handlers for its selected occurrence's available directions, not a complete navigation graph or special destination table. With no selection, a root navigation handler selects the document's root occurrence. Select All remains an explicit command.

Projections declare selectable stops; the chosen layout determines their order. During placement, ordinary columns concatenate logical lines and rows align their children's logical baselines. No pixel coordinates, measured heights, or distance thresholds choose destinations. Padding and inert decorations add no stops or lines. Only placed alternatives contribute.

display::projection::group(content) adds the whole-value stop before its contents. Multiline contents put that stop on an additional leading logical line; single-line contents keep it on the same line. The added entry line does not itself make enclosing single-line contents multiline. Cells, lists, records, and custom forms use this same rule. group_hug combines a selectable whole with inline/indented head-body alternatives; ordinary row and col need no navigation-specific counterpart.

Left/Right traverse all stops in logical reading order, wrapping between lines. Up/Down select the first stop of the adjacent logical line. Thus Down enters a multiline cell or list before its first content line, while passing over a single-line container to the next line. Different painted heights do not change this logical order. Center-aligned rows currently combine top-first. No retained column, directional history, geometric tie-breaking, or Tab routing is present.

A call groups its function and argument fields together. Its arguments use record_fragment_with, arranging fields without another whole-call stop. An outline groups its heading and body in a column, then composes sections and extras vertically. Extras use record_fragment so they do not add a second root-record stop. Empty lists stop once; empty cells offer both the reference and the missing contents. A leaf facet can supply its widget-specific selection callback at the same occurrence as an enclosing whole-value stop without adding another navigation stop. Punctuation and other inert decoration do not declare separate stops.

Each pane builds its own temporary collection of stops and logical lines. After placement, it retains only the current selection's four destinations in an ordinary navigation handler and drops the collection. Destinations preserve the existing occurrence path and editing/conject context: jumps remain editable, and computed children remain read-only. Navigation never resolves document paths to choose a destination. Landmarks separately provide geometry for revealing a landing, pointer/source selection, and Select All. See layout navigation for examples and the checkpoint measurements.

Line editing uses the host-supplied keyboard convention: Mac Command+Left/Right moves to the content's ends and Option+Left/Right moves by words. Control-based hosts use Control+Left/Right for words. Shift extends the selection in each case. These controls handle movement before structural navigation; affixes remain outside the editable span.

Ordinary hover, selection, and related-occurrence highlights share one padded outline. Each occurrence paints at most one mark: selection takes precedence, then related selection, direct hover, and related hover. Pending text frames keep their own tighter outline through focus and editing; this widget styling does not determine the geometry of ordinary value highlights.

The editor owns an explicit Palette input, used by text, selections, library grounds, menus, completion cards, and popovers. Theme::Light uses white paper, blue names, violet field labels, and orange literals. Theme::Dark supplies brighter corresponding colors on navy paper. The macOS shell follows the system appearance at startup and on changes. Its native View menu offers Follow System Appearance, Light Mode, and Dark Mode; this application-wide override lasts for the session, including newly opened windows, and also updates window chrome. Other hosts default to Light; the browser host can explicitly choose either theme. Read-only projection boundaries use a faint tint; returning to writable source restores the same opaque paper used by the window. Changing the palette rebuilds the ordinary complete frame without replacing the document, selection, or undo history. Authored drawing colors remain document data, not theme colors. These styles change presentation, not projection recognition or source/selection policy.

Cmd+A (Ctrl+A on non-Mac hosts) selects the current view's root through its landmark's direct-selection callback. With no selection, it targets the document root. Focused text fields handle the shortcut first and select their own text.

Completion

The projection rendering a pending value or label explicitly requests completion and may supply a lazy vocabulary. Completion is an ordinary native widget function; its explicit kind and provider arguments go to a scoped app adapter returning the same measured widget output as other projections. No provider is hidden in traversal context. Only the active picker asks the provider for offers. Each request includes the query, field/value kind, suggestion/Everything scope, Raw mode (so providers can omit interactions whose presentation needs a library projection), source-qualified path, and read-only path and cell lookups, plus lazy enumeration of defined cells. Cell lookup exposes the selected definition's value, source, and whether it has a native implementation, without evaluating it. The path names a missing value or the record receiving a new label. A local projection provider takes precedence; otherwise library providers contribute in library order. None leaves the vocabulary unspecified, while Some([]) means an empty narrow list with the … escape. If no provider specifies a vocabulary, the editor uses its universal offers directly.

Completion display text and detail can be literal text or a named cell reference. Library, constructor, parameter, and numeric-type labels use references; the picker resolves their current names before filtering each frame. Renaming a definition therefore updates even retained offers without changing their activation handler. An explicit empty name stays empty; a missing name uses the usual short cell identity. Typed values remain literal text. Cell search entries show their source name as a right-aligned note, without appending the cell identity. Identities are low-level inspection information, not routine disambiguation labels; Raw still shows them as the primary spelling.

The completion library's labels helper turns cell identities into label offers; combine concatenates applicable lazy providers in order, preserving an explicitly empty vocabulary. Grap's parameter_labels reads an inline or stored lambda's declared parameters when asked. The same metadata reader supplies call field order and call_completion's initial pending parameter. It follows cell aliases, declines cycles and computed callables, and does not interpret native descriptions as lambdas. Filtering names and excluding existing record labels remain picker responsibilities.

For nonempty value searches, Grap also offers calls to named stored lambdas, closures, and native functions discovered through those definitions. These are ordinary call_completion offers, distinguished by a call detail; choosing one inserts {function: cell} and opens its first declared argument when known. Call suggestions put document-defined functions before library functions, sorting each group alphabetically by display name, ignoring case. Typed-query matching still takes precedence; this order breaks equally ranked matches. The bare reference remains a separate choice. Discovery does not evaluate code or guess the result of computed callables. Function/FFI reference slots and label pickers do not receive these call offers.

Grap expression slots supply a focused provider: statically visible lambda parameters, preceding let/where bindings and the current match case's pattern bindings, plus named function calls, library literal/constructor offers, text, lists, and records. This is an explicit local projection, including the items of do, not a global change to data completion. Existing domain-specific suggestions are tried first. Binding suggestions put the nearest enclosing lexical scope first, then outer scopes, sorting names alphabetically within each scope. Rebinding a cell gives it the nearer scope's rank; scrolling and layout have no effect on this order. Typed-query match quality still ranks ahead of proximity. The projection environment exposes the loaded library provider for composition; Grap reuses those offers rather than duplicating numeric conventions. Scope discovery reads source structure only; quoted template declarations do not introduce bindings into unquotes. Computed binding lists and runtime callable types are not inferred. … restores all cell references, including function values, named constants, and library vocabulary.

Root templates and root field suggestions are ordinary library providers checking the path, not separate editor hooks. The name library offers the query as text at a name field, including an empty string and optional surrounding quotes. A missing-name marker, such as an anonymous lambda's, only selects that name location; the name library's offer supplies its text. Expanding the picker still allows other values. Fidget uses the same interface for shape expressions, parameter labels, and f32 parameter values, including through cell references, source-list items, and existing Grap constructor calls. Shape templates open their first missing parameter without inventing a value. Label offers omit fields already in the record. Fidget field-expression suggestions put shapes before scalar constants; numeric parameter slots still lead with their f32 offer, including zero for an empty query. Circle and sphere are ordinary named Grap lambdas that use quote/unquote to return Fidget arithmetic; the Fidget parser has no circle or sphere forms. Their parameter suggestions and initial focus come from their current definitions; the native Fidget forms retain explicit domain schemas. Their completions insert calls only in evaluated positions (the domain's source entries and Fidget constructor arguments), not inside inert Fidget records.

Libraries can also contribute query-dependent value offers to the universal vocabulary. The numeric libraries offer f32, f64, and u64 interpretations when the query parses, showing the representation and the actual stored number. An empty or whitespace-only query offers zero in each available representation; it remains a suggestion until committed. Other invalid numeric queries offer no number. Universal offers use a general ordering: exact nonempty display-name matches from library interpretations, strong named cell and constructor matches, remaining library-provided interpretations in library order, plain text (or a new label), then weak fuzzy and unnamed references. The editor does not distinguish particular numeric representations for ranking. With an empty query, the zero interpretations therefore follow named cells and constructors, just before the empty string. Explicit quoted text and blob syntax lead instead. Numeric providers decline label requests. A projection's narrow vocabulary still takes precedence until the user expands it.

Universal constructor offers accept delimiter aliases: [ for new list, ( for new cell, and { for new record. In an empty completion query, those keys activate the constructors directly, including in a provider's narrow list. A field label permits only the cell constructor. Nonempty queries and IME composition keep ordinary text input; quoted punctuation leads with literal text. Shortcuts use the same insertion callbacks as the constructor offers.

A library completion consists of presentation metadata and an activation handler receiving &mut Editor. Clicking or pressing Enter runs it; highlighting or moving between suggestions does not. Handlers may edit, change selection, or start an interaction without inserting data. An optional advertised value supplies source/filter metadata only; it never determines activation behavior.

The completion library's insert and generated helpers build ordinary insertion handlers. They accept an optional on_commit continuation, run at the committed location against the staged selection and annotation state. site::grap adapts a Grap callable to that continuation interface. Insertion and continuation effects are prepared together and installed only if the continuation accepts; Grap explicitly declining or halting declines the whole operation. Ordinary absent results do not veto it. Selection changes are effectful calls, not a special return-value format. Insertion itself does not change selection: the continuation receives the existing selection, and only its explicit effects replace or clear it. A low-level offer with no continuation leaves that selection unchanged, including an active pending query. Stock offer combinators supply the policy: completion::select selects the inserted value, and completion::label opens the label's missing value. Text, numbers, and blobs simply select their location; their line controls supply default editing when projected. Completions do not copy editor text, caret, or write-back rules into the selection. Query caret positions are not translated across parsing. These same offers work in Raw without an override, since that projection contains no atomic line control. Enter commits only through the placed completion control; the shell has no fallback which inserts query text after its offers decline.

The color library's new color offer changes selection to picker state at the same missing location. No color is inserted and no undo entry is made until the first picker interaction. List insertion positions remain stable through that transition. The existing point-control gesture writes the selected color and groups the drag into one undo step. Escape before choosing returns to completion; leaving the location discards the picker without an edit. The seed color belongs only to the picker. Raw mode omits this offer because it does not render the color library's picker; ordinary value-inserting offers remain available.

Root grap and fidget offers create a fresh bare cell shared by their domain field and a left pane. Fidget's pane applies preview 3d; Grap's pane renders the evaluated result. The continuation opens the cell's pending document definition through the domain field, without inventing a placeholder value. Offers that mint identities construct their value on activation, so reusing an offer creates independent cells. panes remains an independent root field suggestion.

The placed frame retains the exact visible offers, so hover and commit never reconstruct a different list. Each offer has an activation callback plus explicit text, styling, matching spans, and source attribution. The reusable puri-widgets completion widget shapes and draws rows. Progred's native card widget composes navigation, interaction, and the shared column and scroll container; the row widget has no scroll viewport of its own. Its caller supplies state and activation callbacks; the widget knows nothing about paths, insertion, or Grap. The editor owns document operations, query state, and popup placement. See offer construction and document adaptation. The card meets the query's painted frame, accounting for the frame outline and both border widths. Placement uses those same drawing parameters above or below the query, and includes the card's stroke when keeping it within the viewport.

A provider starts with its narrow vocabulary. The trailing … participates in row navigation but activates expansion rather than committing a value. Expansion also happens when pressing Down on the selected … row. The expanded list keeps the provider's offers first and adds universal atoms, constructors, and cell search; the … row disappears. Expansion keeps the selected index, clamped to the available rows. Changing the query filters the expanded list and resets selection and scroll; expansion lasts for that picker. Returning to an older query does not restore an old choice. A new picker starts with its provider's narrow vocabulary. Pointer and keyboard activation use the same callbacks. Keyboard navigation and unpressed mouse motion over a visible row update the same chosen-row state. Only that row is highlighted, and Enter activates it. Hover remains available for pointer activation but does not paint a second highlight or overwrite the choice during a redraw. Touch motion and active mouse drags do not choose rows, so scrolling and text selection can keep their gestures. Activating a visible offer consumes the input even if its continuation declines; Enter must not then fall through to inserting the raw query.

Panes

workspace recognizes a direct, coherent root record convention:

{panes: {left: [...values...], right: [...values...]}, ...}

The lists determine side and order; entries are ordinary values. Deleting a pane's root removes that list element. Opening and moving panes edit these lists, so they save and undo with the document. A malformed pane container or nonrecord root is not overwritten to make the operation possible.

Pane identity, projection mode, folds, scroll, and requested size are session state. The document view starts with the contents of panes folded; individual pane entries have no initial fold. Expanding the field is preserved when panes are added, removed, or moved. Surviving declarations retain their view state. Moving an entry assigns a new list position and explicitly retargets selection; its size, scroll, and projection mode carry across, while path-keyed folds reset.

{value: source, projection: function} is a presentation-library convention, not workspace configuration. The document's normal projection shows this declaration as editable data. At pane entry, including through cell definitions, the view tries the presentation library's declaration interpreter first. Nested values, computed results, and the source shown after an absent result use the normal projection, so declarations inside them remain data. This pane-entry following is separate from explicit local or scoped projection composition. Raw shows the structural data in either view. See projection composition.

An assigned-size pane uses {value: source, viewport: function} instead. The function receives value, width, and height (logical display units) and returns ordinary display content, including handlers. Its pane has no automatic padding or document scrolling; the assigned rectangle clips its output. Pane splitting determines that rectangle before the function runs, so there is no feedback from content measurement to pane sizing. View annotations and selection keep their existing per-view ownership. Raw returns to ordinary scrolling source display. The viewport convention applies only at pane entry, including through cell aliases; the main document and nested declarations remain editable data.

The Fidget example and template use this contract. Preview image dimensions are explicit arguments, separate from the Fidget field and camera volume. Raster resolution follows the display scale; rectangular 3D views preserve square pixels rather than stretching the geometry. Ordinary preview calls without size arguments retain the 256-point default.

preview 3d accepts either one Fidget field (white) or a scene value: {scene: [{field: sdf, color: rgb}, ...]}. A scene groups separately colored objects; it is not a CSG operator. Geometry is depth-merged before lighting, and equal depths keep the earlier object. Colors use the existing color library's RGB/RGBA values; omitted colors are white and alpha must be opaque. An empty scene is transparent. Fidget's 16-bit object index limits one scene to 65,536 objects. Scene data is inert like other records: Grap code constructs it with ordinary quote/unquote, not implicit evaluation inside the scene. The native renderer uses Fidget's GPU color pass; the CPU/web renderer shades the winning geometry with the same object's color. Their lighting differs.

The IoP example uses an inline Grap viewport function to construct a drawing program with the assigned width and height. Those dimensions also reach tree scene; its drawing units and editable tree parameters are not rescaled.

History, gestures, and persistence

The drawn menu contributes its keyboard handler along with its bar and popups. Frame composition places that handler ahead of content handlers; omitting the menu also omits its shortcuts and keyboard navigation. Undo/redo have a separate editing handler, so embedded tutorials retain history without inheriting Raw, example switching, or pane commands. Native menus keep their own command routing.

Examples and replace-in-place New Document are development/demo conveniences, not the intended production File menu. Cmd+N (Ctrl+N on non-Mac hosts) and the example shortcuts replace the current document after confirming any unsaved changes. With no desktop window, they create one. Cmd+Shift+N / Ctrl+Shift+N is New Window; Open also continues to create a separate window.

Replacement keeps the window, surface, geometry, fonts, text-shaping cache, clipboard, library stack, physical pointer/modifier input, and debug-display preference. It resets document/saved identity, history, selection and text/IME state, all pane identities and annotations (including camera and folds), scroll, projection modes, divider state, binders, menus, gestures, queued input, retained handlers/paint, and hover attribution. The successor frame is built immediately. On macOS the old file's frame-autosave name is detached without deleting its saved geometry; the represented file, title, and edited flag are refreshed. These development commands are currently present in optimized make dev builds too; optimization level is not a distribution feature flag.

History is a generic pair of snapshot stacks; recording a new branch clears redo. The editor's snapshot contains its shared document, view-qualified selection, and per-view folds. Collapse and expand record steps without editing the document. Undo restores fold overrides and their view identities, including when a deleted pane returns; it leaves current scroll positions, projection modes, and unrelated annotations alone in surviving views. Selection paths include the owning view rather than borrowing whichever pane happens to be selected when Undo is invoked.

The editor holds its current and saved documents as Rc<Document>. Dirty state is pointer inequality, constant-time at any document size. Successful writes use copy-on-write; rejected writes do not detach the snapshot. Undoing to the saved snapshot clears the dirty flag, while manually recreating equal contents does not. Folding never changes document identity, and branch depth has no bearing on saved state. The GID document and its on-disk representation remain unchanged.

A line's first write records its undo step; subsequent writes in that edit run coalesce. Saving or recording a fold breaks the run. Projection gestures have one caller-owned slot in gesture. An ordinary native widget's accepting handler supplies the continuation; the slot does not distinguish number, camera, or color controls. Widgets process pointer samples, closing over document-edit runs or view-annotation setters supplied by the editor. An edit run groups its successful writes into one undo step. Creating the run, moving the caret, and writing an equal value do not write the document. Release, cancellation, replacing/restoring the document, saving, or recording a fold ends the gesture.

The platform supplies persistence and clipboard capabilities. macOS uses its native atomic write API. Linux writes a unique sibling temporary file, synchronizes it, renames it over the destination, and synchronizes the parent directory. Browser capabilities differ; see persistence and platform notes.

Frame construction, dispatch order, hover, and clipping are described in Puri and the editor frame.