• src/syncterm/Wren.adoc sr

    From Deuc¿@VERT to Git commit to main/sbbs/m on Sun Jul 19 09:24:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/86562f55ac003f4ac4256fee
    Modified Files:
    src/syncterm/Wren.adoc src/syncterm/scripts/menu_bbs_editor.wren menu_settings_ui.wren menu_sort_profiles.wren menu_ui.wren ui_popup_test.wren
    Log Message:
    wren: keep owning menus behind nested dialogs

    MenuUi choice helpers dismissed their pane before returning a selected
    value. Repeating editors therefore opened prompts and submenus over the
    main screen instead of over the menu that owned the selected action.

    Add callback forms that run the selected action while the choice pane
    remains on the modal stack, then dismiss it after nested UI returns.
    Use them throughout settings, web-list, font, palette, logging, and sort profile editors so each parent remains visible in its inactive state.

    Document the stacking behavior and cover the callback lifetime with a modal-stack regression test.

    Co-Authored-By: OpenAI Codex <noreply@openai.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Deuc¿@VERT to Git commit to main/sbbs/m on Sun Jul 19 09:24:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/514458c7a94fc3970b174f26
    Modified Files:
    src/syncterm/Wren.adoc src/syncterm/scripts/auto/menu/main_menu.wren Log Message:
    wren: update main menu hints with focus

    The Wren main menu rendered directory editing commands in the footer
    regardless of which pane owned focus. SyncTERM Settings consequently
    advertised F2, copy, paste, insert, and delete operations which do not
    apply to that menu.

    Track the active hint set in the classic footer. Retain the directory
    commands and conditional paste hint for the directory, while showing
    only Help and Exit for SyncTERM Settings, matching UIFC's flag-driven
    bottom line.

    Preserve the current menu's hint set while editing the comment and
    restore the destination menu's hints when comment editing ends.

    Co-Authored-By: OpenAI Codex <noreply@openai.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Deuc¿@VERT to Git commit to main/sbbs/m on Sun Jul 19 09:24:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/a4abcbd3d70efae9752a948d
    Modified Files:
    src/syncterm/Wren.adoc src/syncterm/scripts/auto/menu/main_menu.wren Log Message:
    wren: repaint comment editor on focus changes

    The comment input selected its centered inactive or left-aligned edit
    rendering from the parent footer's focus. The footer did not gate its
    child's activity, so a focus change did not invalidate the cached input surface. Leaving an unchanged comment could therefore leave the edit
    lightbar visible until an unrelated update forced a repaint.

    Make the footer a focus gate so its input repaints immediately when the
    footer gains or loses focus. Restore the established full-row comment
    colors and the normal-background margins around the active input.

    Use codepoint count when centering comments so multibyte text agrees
    with the input cursor and the cell-oriented painter.

    Co-Authored-By: OpenAI Codex <noreply@openai.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Deuc¿@VERT to Git commit to main/sbbs/m on Sun Jul 19 09:24:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/a51e13fee68a1ca689b960d6
    Modified Files:
    src/syncterm/Wren.adoc src/syncterm/scripts/auto/menu/main_menu.wren src/syncterm/scripts/ui_input.wren ui_input_test.wren
    Log Message:
    wren: restore comment replace-all entry

    UIFC entered an existing comment through K_EDIT with its current text
    in an initial replace-all state. Typing or deleting replaced the
    complete value, while cursor movement or mouse positioning preserved it
    and continued ordinary editing. The Wren editor always appended at the
    end and kept the whole field in input colors.

    Add an explicit transient select-all state to TextInput and activate it
    when the main-menu comment receives focus. Keep replacement edits to one onChange callback and dismiss the state through navigation, clipboard,
    help, and focus-traversal paths.

    Highlight only the existing comment while replacement is pending, then
    restore normal menu colors after the cursor is positioned. Document the
    new TextInput contract and cover replacement, deletion, movement, mouse positioning, and value reassignment.

    Co-Authored-By: OpenAI Codex <noreply@openai.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Deuc¿@VERT to Git commit to main/sbbs/m on Sun Jul 19 09:24:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/efc8c15aa023d5de7bec3e7a
    Modified Files:
    src/syncterm/Wren.adoc src/syncterm/scripts/auto/menu/main_menu.wren src/syncterm/scripts/menu_bbs_editor.wren ui.wren ui_input.wren ui_input_test.wren ui_popup.wren ui_popup_test.wren
    Log Message:
    wren: restore K_EDIT prompt behavior

    The UIFC menu passed K_EDIT for 34 existing-value inputs. The initial
    Wren migration only assigned those values to TextInput, causing typing
    to append at the end. This affected directory fields, program settings,
    sort profiles, web lists, font names, palette components, and repeated
    Find queries.

    Add SelectOnFocusInput to preserve normal TextInput assignment while
    providing UIFC-style existing-value editing. Select the value whenever
    the widget gains focus, highlight only selected text, and return to
    ordinary colors after cursor movement. Use it for Prompt, Find, the
    palette component editor, and the main-menu comment editor.

    Reselect palette values after a percent reset, export the new input and
    Find through the public UI module, and update the Wren documentation.
    Replace the popup test that expected append-at-end behavior with
    replacement and movement coverage.

    Co-Authored-By: OpenAI Codex <noreply@openai.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Deuc¿@VERT to Git commit to main/sbbs/m on Sun Jul 19 09:24:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/18566cfa9a000da6a7c06113
    Modified Files:
    src/syncterm/Wren.adoc src/syncterm/scripts/auto/connected/font_pick.wren src/syncterm/scripts/menu_bbs_editor.wren menu_settings_ui.wren ui_draw_test.wren ui_list.wren ui_pane.wren ui_picker.wren
    Log Message:
    ui: centralize shadow-aware pane sizing

    The connection settings editor retained a nearly full-screen
    rectangle after its UIFC list was replaced. Fixed-size Help panes
    also failed to reserve the two columns needed by the Wren shadow
    renderer.

    Make Pane own constrained content layout. Measure children again
    after assigning their viewport so ListView scrollbar overhead is
    included, provide shared screen-fitting operations for natural and
    fixed sizes, and leave ListView responsible only for its natural
    dimensions.

    Use the shared layout for the directory editor, generic and font
    list pickers, File Locations, and Build Options. Add pane geometry
    tests and document the current sizing contracts.

    Co-Authored-By: OpenAI Codex <noreply@openai.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Deuc¿@VERT to Git commit to main/sbbs/m on Sun Jul 19 09:24:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/c97924540b81e05079330cb5
    Modified Files:
    src/syncterm/Wren.adoc src/syncterm/scripts/ui_input.wren ui_input_test.wren ui_popup_test.wren
    Log Message:
    ui: show the end of long prompt values

    Prompt initializes a TextInput value before assigning its bounds. The
    cursor was placed at the end, but the horizontal viewport remained at
    zero because no field width existed yet. This displayed the value prefix
    while clamping the visible cursor into the field, then jumped on the
    first cursor movement.

    Recalculate the TextInput viewport whenever its width is assigned or
    changed. Start from the unscrolled position before ensuring visibility
    so long values consistently show their trailing characters and a cell
    for the insertion cursor.

    Cover both direct TextInput layout and the long initial-value Prompt
    path, and document the viewport contract.

    Co-Authored-By: OpenAI Codex <noreply@openai.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Deuc¿@VERT to Git commit to main/sbbs/m on Sun Jul 19 09:24:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/11d04f312ebf17c9954dcb16
    Modified Files:
    src/syncterm/Wren.adoc src/syncterm/scripts/menu_ui.wren ui_popup.wren ui_popup_test.wren
    Log Message:
    ui: size prompts for their input fields

    MenuUi gave every prompt a fixed 34-column minimum and otherwise
    sized it only from the label. This left URI, path, password, modem,
    and other long-capacity fields unnecessarily narrow. UIFC instead
    included the maximum input length and capped the result to the screen.

    Add Prompt.sizeForInput and have the MenuUi wrappers use maxLen as
    the requested field width. Keep the compact minimum for short fields,
    size direct Prompt helpers from their initial values, and correct popup
    bounds so an oversized request cannot exceed the standard margins.

    Add coverage for compact and full-width menu prompts, input layout,
    and right-shadow clearance. Document the shared sizing contract.

    Co-Authored-By: OpenAI Codex <noreply@openai.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Deuc¿@VERT to Git commit to main/sbbs/m on Sun Jul 19 09:24:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/81ab6d872552f58b9e865a20
    Modified Files:
    src/syncterm/Wren.adoc src/syncterm/scripts/menu_settings_ui.wren src/syncterm/uifcinit.c uifcinit.h wren_bind_fs.c src/uifc/filepick.c filepick.h
    Log Message:
    ui: select the current font in the file picker

    Font Details always opened the native file picker at the process
    current directory. Replacing a configured font therefore discarded the
    useful location and selection already stored in the font record.

    Allow the single-file picker initial path to name an existing file. It
    opens the containing directory and initializes the file-list cursor and
    scroll position to the file basename. Directory initial paths and the multi-file picker retain their existing behavior.

    Pass the configured font path from the Wren menu, falling back to the
    current directory only for an unset font slot, and document the expanded Host.pickFile contract.

    Co-Authored-By: OpenAI Codex <noreply@openai.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Deuc¿@VERT to Git commit to main/sbbs/m on Sun Jul 19 13:28:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/6c09071ae20ae8d08f241919
    Modified Files:
    src/syncterm/Wren.adoc src/syncterm/scripts/auto/picker/file_picker.wren src/syncterm/scripts/ui_list.wren ui_list_test.wren
    Log Message:
    wren: fix picker list focus and positioning

    Keep both sides of the file picker in the normal active palette while
    showing the selection lightbar only in the list that owns focus. Preserve
    the existing always-visible selection default for other ListView callers.

    When a selection is restored before list bounds are known, center it during initial layout if it falls outside the first viewport. Continue using the existing minimal scrolling behavior for subsequent keyboard navigation.

    Document both ListView controls and cover focused rendering and initial viewport positioning in the Wren UI tests.

    Co-Authored-By: OpenAI Codex <noreply@openai.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Deuc¿@VERT to Git commit to main/sbbs/m on Sun Jul 19 13:28:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/73886f9d8d8507216cdf7aed
    Modified Files:
    src/syncterm/Wren.adoc src/syncterm/scripts/ui_help.wren ui_help_test.wren ui_list.wren ui_list_test.wren ui_logview.wren ui_logview_test.wren
    Log Message:
    wren: improve scrollbar mouse navigation

    Make ListView track clicks jump to the proportional viewport position
    instead of consuming the click without moving. Shift its selection with
    the viewport so the focused lightbar remains on the same screen row.

    Treat wheel events over a scrollbar as page navigation in ListView, Help,
    and LogView while retaining their existing item or line increments over content. Leave button drag handling unchanged so each control preserves
    its current text-selection or thumb-drag behavior.

    Document the mouse rules and add coverage for track clicks and page-wheel navigation across all three scrollbar consumers.

    Co-Authored-By: OpenAI Codex <noreply@openai.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Deuc¿@VERT to Git commit to main/sbbs/m on Sun Jul 19 13:28:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/f56a1ff1d1f3b79c4cd61c96
    Modified Files:
    src/syncterm/Wren.adoc src/syncterm/scripts/auto/picker/file_picker.wren src/syncterm/scripts/ui_draw.wren ui_draw_test.wren ui_list.wren ui_list_test.wren
    Log Message:
    wren: correct scrollbar click handling

    Map scrollbar track clicks through the inverse of the thumb placement calculation so a one-cell thumb lands on the clicked row instead of the
    row above it.

    Treat the separator column between list content and the scrollbar as
    inert. The picker keeps its inactive-pane focus behavior while routing
    only actual scrollbar clicks through the scrollbar handler.

    Document the interaction and cover both cases with Wren tests.

    Co-Authored-By: OpenAI Codex <noreply@openai.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Deuc¿@VERT to Git commit to main/sbbs/m on Sun Jul 19 13:28:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/b252c42d8aeb661e76a3954e
    Modified Files:
    src/syncterm/Wren.adoc src/syncterm/scripts/auto/menu/menu_host_ui.wren Log Message:
    wren: center host progress text

    Host progress panes painted every status line at the first interior
    column. This left connection messages visibly off-center even though
    the pane itself was centered.

    Wrap status text to the padded interior width, size the pane from the
    wrapped row count, and center every resulting line. The retained App
    repaints its full-screen backdrop before each update, so replacement
    panes can resize without leaving the previous frame behind.

    Document the progress-pane layout for menu VM implementations.

    Co-Authored-By: OpenAI Codex <noreply@openai.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Deuc¿@VERT to Git commit to main/sbbs/m on Sun Jul 19 13:28:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/6d5032ddff4500878c4e210f
    Modified Files:
    src/syncterm/Wren.adoc src/syncterm/scripts/auto/connected/scrollback_view.wren src/syncterm/scripts/wrentest.wren
    Log Message:
    wren: isolate scrollback mouse events

    The connected scrollback viewer added its events to the active CTerm
    mask. When a BBS enabled mouse tracking, raw motion kept waking and
    repainting the viewer, while drag move/end events could be absent and
    strand text selection.

    Replace the mask while the viewer is active with exactly its drag and
    wheel events, including the complete drag sequence. Restore the exact
    caller mask on exit instead of reconstructing it from CTerm state. This
    also preserves subscriptions belonging to a surrounding Wren App.

    Document and test the viewer mask.

    Fixes ticket 270

    Co-Authored-By: OpenAI Codex <noreply@openai.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Deuc¿@VERT to Git commit to main/sbbs/m on Mon Jul 20 11:51:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/f99bbe4180ffe4156ce8b815
    Modified Files:
    src/syncterm/Wren.adoc src/syncterm/scripts/auto/menu/main_menu.wren Log Message:
    wren: initialize the startup menu backdrop

    Startup web-cache progress runs after the menu VM is initialized but
    before MainMenu.run() creates the interactive application. The status
    pane therefore saved and overlaid the uninitialized text-mode screen.

    Have the built-in main_menu module paint only its full-screen backdrop
    and title bar when the module loads. Startup alerts and progress panes
    then save and restore initialized menu chrome without constructing the
    footer or loading the directory.

    Document the startup rendering order.

    Co-Authored-By: OpenAI Codex <noreply@openai.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Deuc¿@VERT to Git commit to main/sbbs/m on Mon Jul 20 11:51:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/793574a598a0d1140900f72c
    Modified Files:
    src/syncterm/Wren.adoc src/syncterm/scripts/auto/picker/file_picker.wren src/syncterm/scripts/sftp_app.wren ui_list.wren ui_list_test.wren
    Log Message:
    wren: preserve list view across refreshes

    ListView.items= reset the selection and scrollTop whenever a caller
    rebuilt its row labels. Callers then restored the selected index, causing ensureVisible_() to place that row at the bottom of the viewport.

    Preserve the selected index and viewport when replacing a populated
    list, clamping only when the replacement is shorter. Add resetItems() for actual collection navigation and use it when changing file-picker and
    SFTP directories.

    Document the two replacement modes and test preservation across both an
    item refresh and subsequent layout.

    Co-Authored-By: OpenAI Codex <noreply@openai.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Deuc¿@VERT to Git commit to main/sbbs/m on Mon Jul 20 11:51:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/e90c365ca2b71fd852f1bc21
    Modified Files:
    src/syncterm/Wren.adoc src/syncterm/scripts/auto/connected/transfer_app.wren transfer_pick.wren src/syncterm/scripts/menu_ui.wren ui.wren ui_popup.wren ui_popup_test.wren
    Log Message:
    wren: restore compact text entry dialogs

    Ordinary menu text entry used the general Prompt popup, which repeated
    matching title and field labels, added padded message rows, and exposed
    OK and Cancel buttons despite Enter and Escape already submitting and cancelling the field.

    Add a UIFC-style LinePrompt with the label and input sharing one interior
    row. Use it for menu editing and transfer filename entry, while retaining Prompt for host dialogs that carry a separate explanatory message.

    Center the custom layout without Pane's single-child fitting helper so
    its calculated input bounds are not replaced with the full pane interior. Document the widget and cover its geometry and dismissal behavior.

    Co-Authored-By: OpenAI Codex <noreply@openai.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Deuc¿@VERT to Git commit to main/sbbs/m on Mon Jul 20 11:51:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/7d66cd8aec7cdab6c6f4a2e4
    Modified Files:
    src/syncterm/Wren.adoc src/syncterm/scripts/auto/menu/main_menu.wren src/syncterm/scripts/auto/picker/file_picker.wren src/syncterm/scripts/classic_theme.wren syncterm.wren syncterm_picker.wren ui_app.wren ui_widget_test.wren src/syncterm/wren_bind.c wren_bind_fs.c wren_bind_picker.c wren_bind_picker.h wren_menu_host.c wren_picker_host.c
    Log Message:
    wren: apply the configured theme to every App

    Apps outside the main menu previously inherited Theme.default, while
    the menu and picker wired ClassicTheme independently. This gave
    connected and standalone dialogs different selected-button colors from
    the rest of SyncTERM.

    Expose the six configured palette indexes as read-only Host.themeColors
    in all three Wren VMs. Have App.new() build ClassicTheme.current from
    them. Remove the menu and picker-specific theme injection and picker
    request color copy while retaining explicit app.theme overrides and live settings updates.

    Document the program-wide default and test that a new App receives the configured Classic Theme.

    Co-Authored-By: OpenAI Codex <noreply@openai.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Deuc¿@VERT to Git commit to main/sbbs/m on Mon Jul 20 11:51:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/dbd75c66a2f45238f716fe04
    Modified Files:
    src/syncterm/Wren.adoc src/syncterm/scripts/auto/menu/menu_host_ui.wren src/syncterm/scripts/auto/picker/file_picker.wren src/syncterm/scripts/ui_draw.wren ui_draw_test.wren ui_help.wren ui_list.wren ui_logview.wren ui_pane.wren ui_popup.wren ui_popup_test.wren ui_style.wren ui_style_test.wren ui_widget_test.wren
    Log Message:
    wren: classify pane frames by purpose

    Frame selection was exposed as single versus double, and Popup forced
    every dialog into the single-line family. This made alerts, prompts, and standalone choices diverge from UIFC, where control-bearing lists and
    inputs use double-line borders while Help and status displays use
    single-line borders.

    Replace framePreset and the visual glyph prefixes with the semantic
    frameKind values control and display. Default Pane and Popup to control,
    then select display explicitly for Help, transient status overlays, and
    startup progress. Reject unknown kinds instead of silently treating them
    as single-line frames.

    Give scrollbar separators their own glyph identifier and update the file picker's structural dividers to use the control family. Document the new
    theme contract and test the registry, frame rendering, validation, and
    widget assignments.

    Co-Authored-By: OpenAI Codex <noreply@openai.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Deuc¿@VERT to Git commit to main/sbbs/m on Mon Jul 20 11:51:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/f59f9a6274e8b80389e490b6
    Modified Files:
    src/syncterm/Wren.adoc src/syncterm/scripts/auto/menu/menu_host_ui.wren src/syncterm/scripts/ui_progress.wren ui_progress_test.wren
    Log Message:
    wren: preserve web progress column alignment

    The web-cache formatter pads each list name to a 20-cell field, but the
    menu host centered every complete status row independently and resized
    the pane from the longest current row. Different state and counter
    lengths therefore moved the name and colon columns between rows and
    updates.

    Restore the fixed 74-column progress pane used by the UIFC presentation,
    capped to the normal screen margins. Render every formatted and wrapped
    row from one padded left edge so the C formatter's columns remain
    stable.

    Move the reusable text body into the existing progress module, document
    the geometry, and test unequal status rows with aligned name columns.

    Co-Authored-By: OpenAI Codex <noreply@openai.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Deuc¿@VERT to Git commit to main/sbbs/m on Mon Jul 20 16:56:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/efef12fb850785555fea36d3
    Modified Files:
    src/syncterm/Wren.adoc src/syncterm/scripts/ui_app.wren ui_widget.wren ui_widget_test.wren
    Log Message:
    Redraw complete UI trees after theme changes

    Replace top-level dirty marks with non-bubbling recursive widget-tree invalidation. Theme previews previously repainted modal and root containers without invalidating their cached descendants, leaving inactive widgets in
    the previous theme until another state change dirtied them.

    Include transient status overlays in complete redraws and parent them to the App so they inherit its selected theme. Document the repaint contract and
    cover descendants in both the background root and modal stack.

    Co-Authored-By: OpenAI Codex <noreply@openai.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Deuc¿@VERT to Git commit to main/sbbs/m on Tue Jul 21 09:28:00 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/5651d5592e5a5ffb7b320a04
    Modified Files:
    src/syncterm/Wren.adoc src/syncterm/scripts/ui_app.wren ui_widget_test.wren
    Log Message:
    Discard modals left by failed handlers

    Snapshot the App modal stack before each synchronous event dispatch.
    When a handler aborts after pushing a dialog, remove frames outside the pre-dispatch common prefix so an orphaned pane cannot consume the next
    Escape or Backspace.

    Keep modals that the failed handler successfully dismissed, preserving completed close operations and the existing Key.quit recovery behavior. Document the error-boundary contract and add regression coverage for the
    next Escape after an abort.

    Co-Authored-By: OpenAI Codex <noreply@openai.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net