xdog-tui
Terminal UI library with differential rendering.
A small terminal UI toolkit that redraws owned rows in the main terminal buffer while preserving native scrollback. No curses dependency.
InputPanel supplies shared Unicode editing, undo/redo, history and atomic
paste. InlineLayout budgets the editor and prioritized auxiliary panels from
terminal height. The renderer uses cursor reports at startup, resize and resume.
Ctrl+L repaints visible owned rows without clearing native scrollback or focus.
Focused editor markers position and show the terminal cursor; panels without a
marker hide it. Overlays restore base focus and composite by display cells.
Clearing a scrolled transcript re-anchors the owned tail without erasing history.
Confirmation panels reject unshown/clipped actions and unpainted resize states.
EventQueue.drain() processes bounded FIFO batches so sustained producer output
does not monopolize a UI tick (defaults: 64 events or 8 ms between callbacks).
Standalone panels are available from xdog.tui.components:
| Component | Responsibility |
|---|---|
PermissionPanel |
Summary scrolling, choices, selection and cancellation callbacks |
DetailsPanel |
Full detail records, entry navigation, scrolling and explicit output following |
InputPanel |
Multiline editing, history, completion and submit/cancel callbacks |
StatusLine |
One bounded row of plain status or prioritized activity/context/model fields |
Panels accept data, theme functions and callbacks; they do not import Coding or
Claw policy. Applications own permission decisions and lifecycle state, while
InlineLayout only positions panels and allocates height. PromptEditor and
BoundedDetails remain compatible aliases at their original import paths.
See terminal stability and verification for scope, test commands, and platform limitations. Fullscreen applications and remote coding are outside this stability work.
Part of xdog
This package is one piece of xdog, a
local-first toolkit for building, running and scheduling LLM workflows. The
centrepiece is xdog-flow — a typed
workflow format and compiler.
Documentation: https://xdog.942295.xyz
Licence
Copyright (c) 2026 HugeMan <942295.xyz>
GNU Affero General Public License v3.0 or later — see
LICENSE. Output compiled
by xdog-flow is exempt; see the
Generated Output Exception.
Non-blocking prompt path completion
InputPanel.set_autocomplete_provider(provider) now connects live prompt
completion. Call poll_autocomplete() from the host's UI tick and request a
repaint only when it returns True. Providers must return without blocking;
WorkspaceFileCompletionProvider(root) provides a coalescing filesystem worker
for fuzzy @ references and explicit paths. Close owned providers during host
shutdown. Results never mutate the editor from a worker thread.
Selecting a file replaces only its cursor-local token and never submits the
draft; slash commands retain their existing Enter behavior. Focused details and
permissions suppress background suggestions. max_entries/scan_seconds bound
worker batches, not total discovery. Names are held in a private SQLite index;
root search and large directory scans resume until complete. Git supplies ignore
semantics for repositories and non-repositories through private metadata; no
workspace Git state or hooks are changed/executed. Missing/failing Git is a
visible basic-exclusion fallback. CompletionStatusProvider optionally supplies
non-blocking progress text; InputPanel clamps it to the existing menu budget and
never treats status as a selectable path. See docs/agent-ui-gap-plan.md for the
full application goal and verification.
Prompt keymaps and foreground terminal handoff
PromptKeymap is the immutable, validated map used by the live Coding/Claw
editors and host action handlers. InputPanel.set_keymap() remaps actions without
falling back to replaced chords; dialog confirmation remains independent.
help_text() and label() expose effective bindings for help and hints.
TUI.run_outside_terminal(callback) schedules one foreground callback after
protocol cleanup and raw-mode restoration. The callback runs on the UI thread;
polling/painting pauses, while external workers may keep publishing events.
Modes are preserved around the callback in case its process leaves them changed.
Return re-enters negotiation and appends a new owned transient tail without
replaying durable history. Pending handoffs and stopped/suspending TUIs reject
new handoffs; coalesced input after a handoff request is not executed.
edit_draft() provides the standard private-file external-editor round trip.
Only user environment preferences select its executable; returned text must be
applied by the host, not submitted automatically. Helpers are exported from
xdog.tui as well as their modules. Approval policies remain application-owned.
Queue control vocabulary
The shared queue_controls module supplies immutable queue views, stable local
ID bookkeeping and /queue command parsing; it does not route agent work or
choose application permissions. Hosts show bounded previews and own queue locks,
worker-loop acknowledgements, transport generations and draft recovery. The live
prompt keymap exposes steer (Alt+S), follow_up (Alt+F) and queue (Ctrl+Q).
Repository review
RepositoryReview supplies immutable ReviewSnapshot / ReviewFile objects
from one owned read-only Git worker. Patch rows are stored in a private SQLite
cache and fetched as display-cell-clipped pages; all hunk positions remain
available. Requests coalesce, stale results and partial document caches are
fenced, and close cancels/reaps the child and removes private storage. No agent
or application permission policy is imported into this standalone helper.
ReviewPanel composes a bounded file/filter/patch surface in the main-buffer
transient tail. Hosts poll snapshots, restore focus around higher-priority
permissions, and close the backend on exit/workspace change. The panel owns
keyboard/paste while focused so Enter cannot submit the underlying draft.
Library exports are available from xdog.tui and xdog.tui.components.
Session/branch navigation surfaces
NavigationEntry/NavigationSnapshot, NavigationWorker and NavigationFeed
provide immutable, paginated local-worker and remote-reply views. Search applies
to the full metadata set before pagination. Requests coalesce and stale/closed
replies are fenced; changed searches/scopes do not leave stale rows selectable.
NavigationPanel owns search, paste, navigation and focus in the bounded native
transient tail. Application callbacks own activation, idle gates, permission
isolation, storage and gateway context synchronization; the library does not
import agent/application policy or run model calls.
Explicit image composer and clipboard reads
Attachment is immutable raw image data plus stable identity/metadata.
attachment()/read_attachment() validate PNG/JPEG/WebP headers and bounded
size/dimensions; file reads use nonblocking open/regular-file checks to avoid FIFO
hangs, not to establish a sandbox. AttachmentReader owns one off-thread read
and its bounded clipboard subprocess, publishing immutable events to the host.
Wayland/X11 image helpers and the native macOS image/text adapter (text-only
pbpaste fallback only if osascript is absent) are explicit reads, never background
polling. The macOS adapter is contract-tested on Linux; native desktop validation
remains separate.
AttachmentComposer owns add/list/preview/remove/clear/take/restore on the UI
thread. Late reads after take stay editable; explicit clear fences prior reads.
Recovery retains bytes even above ordinary admission caps, leaving host validation
to block oversized sends. InputPanel.set_attachment_summary() reserves a bounded
summary row and enables empty-text submission only while attachments are present.
AttachmentPanel owns focused preview input, caps image height, and falls back to
metadata on unsupported/tiny terminals. Types/helpers are exported from xdog.tui,
the panel from xdog.tui.components. Provider capability, permissions, typed SDK
conversion, queue ownership, persistence and transport remain application policy.
Full-source transcript browsing and temporary alternate screens
Transcript.transcript_records() projects immutable, stable-ID source strings
from retained message/tool components without rendering/truncation. User/assistant
source is retained before Markdown transformations. TranscriptBrowser owns one
coalescing worker: full source indexing, literal Unicode-casefold search over every
occurrence, source-position preservation on refresh, horizontal clipping and
bounded viewport snapshots. Per-line cumulative counts avoid allocating one
object for each repeated occurrence on a huge line. Old/closed results cannot
be published over newer requests. Source extraction for explicit copy/export is
separate and should run off the UI thread.
TranscriptPanel owns query paste/editing, occurrence and source-entry navigation,
vertical/horizontal scrolling, whole-line/range/entry selection and refresh/latest
controls. The host owns source/session boundaries, permissions and fullscreen
policy. Inline panels are capped at 12 rows; temporary fullscreen takes the
host-provided terminal-height budget. Searching/selection has no application
mutation, agent, provider or clipboard dependency.
TUI.request_fullscreen(enabled) schedules an owned terminal-loop boundary
transition rather than changing modes inside an input callback. A temporary
alternate painter retains the native main-buffer painter; return requests CPR to
handle hidden main-buffer reflow, then repaints only owned visible rows. Coalesced
keys cannot submit through a pending transition. InlineLayout.fullscreen_view
lets hosts provide a height-bounded alternate surface; no normal transcript/editor
is painted there. Hosts must leave fullscreen for approvals/other dialogs.
Stop/failure cleans temporary screen ownership; suspend retains it for re-entry.
The default remains native scrollback, with no main-buffer clear/replay. Helpers
are exported from xdog.tui and TranscriptPanel from xdog.tui.components.
Hosts with asynchronous remote sources can supply TranscriptPanel(on_refresh=...),
set a visible loading/error status, and replace immutable records only after complete
validation. Selection is disabled during loading; pending query/End intent survives
old viewport and geometry receipts. Whole-entry selection is prioritized in the
header even when a source-status label is present. Large sanitized ASCII lines use
cell-equivalent direct slicing/panning instead of allocating a grapheme object for
every offscreen character; Unicode retains grapheme-safe clipping.
Captured transcript output actions
transcript_transfer supplies immutable parsed options/prepared artifacts, source
scope extraction, HTML escaping/heuristic share redaction, exclusive private atomic
file creation and explicit OS clipboard writes with a visible private-file fallback.
TransferWorker owns one off-thread operation; it does not replay/coalesce writes.
TranscriptActions adapts immutable worker receipts into a context-fenced UI review
lifecycle. Hosts retain approval, source completeness, workspace/session and focus
policy. OutputPanel owns preview/path input, forbids confirmation until source and
full metadata/destination are rendered, and keeps paste/modified Enter/tiny resize
from saving. It imports no agent policy and performs no upload. TranscriptPanel
exposes optional output callbacks for y/e/s with stable selection/entry IDs.
Child-work presentation
ChildPanel is a policy-free bounded roster/form/transcript component used by both
clients. Hosts provide validated summaries, immutable source records, operation
callbacks and ownership fences. It never executes agents or grants tool permission.
Child-specific start/steer/follow-up/cancel/close confirmations require a painted
visible action; paste/repeat/release/tiny-frame input cannot confirm. ChildSourceWorker
coalesces local conversion; child JSON conversion preserves complete retained text,
redacts sensitive fields heuristically and omits binary bodies/signatures. Search,
selection, fullscreen and explicit reviewed outputs reuse TranscriptPanel and
TranscriptActions. Child dialogs/configuration/tool-progress policy stays in hosts.
OAuthPanel is a policy-free bounded full-page consent/status surface composed by
McpPanel. Hosts validate endpoints/URIs and own network/view leases. Every field
must paint before confirmation; layout/content/visibility changes invalidate prior
review. Cancel outranks polling; URL copy is explicit after full review. Wrapper
budget/invalidation reaches the actual form, including zero rows. The component
never launches browsers, connects tools or interprets OAuth as execution permission.
Refresh reviews use the same full-page confirmation and resize invalidation as
initial consent. Their footer exposes paging until all fields paint, then Enter
confirmation; pending refresh shows c cancellation, which remains available
while an owner request is busy. Components dispatch auth_refresh_prepare and
auth_refresh; only application hosts manage credentials and token rotation.
Child task submission can prepare a local review while a roster request is pending, without losing the input draft. Final confirmation remains blocked until that request settles; review preparation itself never starts a child or grants access.
clipboard_macos.read_macos_clipboard() supplies a static JXA/AppKit program to the
reader's existing bounded subprocess runner: PNG/JPEG/WebP first, TIFF-to-PNG
conversion with pixel/byte limits, then Unicode text. A change-count guard refuses
mixed clipboard snapshots. Strict bounded JSON/base64 decoding rejects helper
failure without reflecting contents or retrying as text. No clipboard URL/file
reads, browser/app automation, clipboard polling or input submission are added.
Node-based contract tests exercise the exact shipped program with an independent
AppKit interface; they are not validation of Apple's real JXA bridge/pasteboard.
The script-contract tests explicitly skip if Node is unavailable; Node is not a
runtime dependency of the clipboard adapter or either client.
Inert MCP completion forms
McpCompletionPanel composes an explicit prefix editor, advertised variable
navigation, ranked bounded suggestions and visible selection. Ctrl+N/P selects a
variable, Tab requests and Enter accepts into the parent's editable draft only;
Escape cancels. Typing invalidates/cancels pending suggestions and scope/prefix/
context checks reject late or mismatched replies. Layout/visibility changes fence
unpainted acceptance; control/newline text is sanitized for terminal output without
coercing the selected original value. No network client or application policy lives
in this component. McpPanel hosts own request/cancel callbacks and preserve JSON/
URI drafts on invalid/busy submissions.
scalar_template.expand() is a bounded inert RFC 6570 string-value expander for
all operators, prefix modifiers and scalar explode. It omits undefined variables,
percent-encodes values appropriately and accepts no list/map values. Expansion is
editable URI data only; neither helper resolves or fetches URLs/files.
Opaque resource completion forms
McpPanel exposes v on server/resource rows for a JSON uri/argument/value/context
form. mcp_resource.resource_request() validates bounded named-string data without
interpreting/fetching URIs or guessing server schemas. Ctrl+Space opens the existing
picker with operator-specified names; acceptance updates the JSON draft only. A
separate Enter dispatches local resource_completion_preview, not resources/read.
Hosts label that selected/edited data and retain normal quoted preview insertion.
JSON argument forms use up to three editor rows within their existing budget;
soft-wrap does not truncate or silently discard the reference/value/context.
Existing resize, repeat-Enter, invalid/busy draft retention and stale-result fences
remain in force. Components still perform no network or application permission work.
Typed extension form component
ExtensionDialog delegates kind form to ExtensionForm, with no dependency on
agent application policy. It edits named scalar fields, preserves invalid drafts,
shows title/help/constraints and implements whole-form paged visible confirmation.
Tab/Shift+Tab fields, Space/Left/Right choices, Enter/Ctrl+Enter review, PgUp/PgDn
help/review, Ctrl+E edit and Esc/Ctrl+C cancel are contextual. Multiline Enter adds
a line; pasted newlines/repeat Enter never submit. Budget/visibility invalidation
propagates through the wrapper, including zero rows. Hosts independently validate
declarations/answers and own deadlines/leases/cancellation; returning a dict is not
permission or execution. Flat typed forms do not claim arbitrary custom rendering
or nested-schema UI parity.
Read-only extension command display
ExtensionCommand displays a host-validated artifact's text and bounded image
components, with complete detail_body and separately retained typed result data.
Its transcript_role is system, not tool/model input. Existing detail expansion and
transcript source ownership apply; narrow fallback text is clipped without altering
graphics payloads. Hosts alone decode/gate artifacts, preserve drafts/session scope
and prevent prompt submission. The component does not execute returned text/URIs,
write files, grant approval or pretend textual export contains the image bytes.
Explicit typed snapshot output
TranscriptActions.start_snapshot(options, root, prepare)/TransferWorker.start_snapshot
run a host-supplied pure bounded snapshot recipe off-thread and ALWAYS show the
existing review, including explicit paths. Hosts supply PreparedTransfer data/text/
digest/context; the TUI adds no agent dependency or artifact-policy authority.
Artifact scope is JSON-only export, not binary copy/share or textual record coercion.
OutputPanel exposes no-redaction/sensitive-original-byte warnings; existing
visible-path/resize/context/cancel/private-no-overwrite rules remain. Command display
components carry a host-assigned owner token so clients can select only current
session/connection data for lossless export.
Policy-free editor compare-and-set receipts
EditorActions(snapshot, replace) captures text/cursor/attachment identities and
issues a random single-use receipt tied to a host-provided owner key. Changed state,
wrong owner/revision or invalidated receipt refuses replacement; the host callback
validates replacement text before mutation. Snapshot and draft handling do not send
input, touch attachments or change application policy. Coding/Claw enforce command-
only authority, session/connection context and dialog precedence outside the library.
Exact-target extension navigation review
ExtensionNavigation composes ExtensionForm for default-deny, whole-page review of
current owner and exact action/session/branch/zero-based checkpoint data. It returns
only bool queued consent; it cannot mutate a runtime or grant tool permissions.
All target fields/warnings must paint after layout changes, paste/repeat confirmation
is inert and zero-row invalidation reaches the actual form. Hosts queue/validate/
dispatch the intent only after the original command settles successfully under
unchanged command/session/connection ownership and existing idle/navigation policy.
Effective extension command bindings
ExtensionShortcuts(rows, keymap) resolves host-validated command contributions
against effective built-in/user actions, reports disabled collisions and dispatches
only PRESS events. A namespaced ext-ID-command keybindings override replaces that
registration's defaults or explicitly unbinds it; unknown/inactive registrations
cannot become callbacks from configuration alone. Conflicting extension keys are
all disabled, never arbitrary winner-takes-input. Context/activation/command authority
belongs to the hosts. The TUI performs no plugin loading or model/tool calls.
Canonical pre-admission input display
Shared UserMessage.set_text/Transcript.transform_user_input updates only a matching current user echo, preserving image-count labels and Markdown invalidation without terminal command execution. Hosts carry input-transformed/refused ownership receipts, update actual consumed queued text and restore owned original drafts/attachments on pre-admission refusal. The TUI itself runs no extension filter, model or permission policy. Native scrollback and existing source-role/selection semantics remain.
The live TUI now guards painting against geometry changing BETWEEN its input-loop size check and tick callbacks. A raced frame is deferred while remaining dirty, so the next loop still queries/re-anchors the cursor before repainting. Pure/offline rendering remains unchanged. A deterministic lifecycle test and the physical output review cases preserve existing native-scrollback and visible-confirmation assertions.
Handled input remains host-owned policy. Current clients show explicit system/extension feedback and retain original composer attachments without pretending the request was admitted as a model message. Child source inspection accepts separately bounded input_feedback metadata and presents it as system data, never assistant prose. No custom renderer/model/tool/permission authority is introduced by these displays.
Inline custom extension component
components.extension_custom.ExtensionCustom consumes bounded viewport-tagged data
and forwards keys/atomic paste through a host-supplied enqueue-only callback.
render() performs no worker/network I/O; poll() handles initial/resize requests.
The component invalidates painted revisions on blur, hidden rows, geometry changes
and view invalidation, tagging requests with a generation to reject delayed frames
from the same-size old view. It exposes busy/refusal feedback without replay.
Only vetted SGR styles/colors survive line sanitization. OSC/APC/DCS, clipboard,
links, graphics, cursor/erase/alternate-screen controls and private cursor markers
cannot pass through. Style resets isolate host components; optional carets are
placed at whole-grapheme boundaries. Host cancellation/redraw/suspend/exit keys are
reserved. TUI.requires_current_viewport_input fences EVERY forwarded key and paste
during a physical resize race. Reasserting the same focus owner is idempotent;
normal polling is not a blur/focus transition. Hosts still own permissions, leases,
original completion futures, modal precedence and headless/older-viewer negotiation.
ExtensionCustomPresentation manages a temporary fullscreen view using the host's
visibility/policy decision. InlineLayout supplies the actual terminal height to
that view; inline and fullscreen remain separate, epoch-fenced presentations.
The component supports sanitized metadata paging (F1 view/details, page/home/end
navigation) and explicitly requested repeat/release events, including release
subscription at the real TUI parser. Default press-only payloads keep the v1 wire
shape. Screen ownership, permission decisions and protocol capability negotiation
remain host concerns. Alternate-screen resize resets/re-homes only the active
alternate painter; it does not clear the saved main renderer or native scrollback.
ExtensionDialogStack preserves FIFO among unrelated roots while selecting the
active descendant of an owned custom parent. It caches parent components across
child input/form/custom questions and validates supplied parent linkage/scope;
hosts still enforce their actual runtime and transport leases. Reusing a parent
component restores its draft/state but invalidates old focus/viewport proof. No
worker or socket code runs in this dependency-light selection/cache layer.
Owned custom-view overlay presentation
ExtensionCustomPresentation can attach a validated custom panel to a capturing
TUI overlay handle. The existing host overlay options supply percentage/absolute
width and height, anchors, offsets, positions and margins. The panel receives the
actual allocated viewport and uses the same sanitized frame/cursor/input protocol.
Only the owning handle is removed on nested questions, preemption or completion;
other host overlays keep their focus. has_overlay(exclude=component) lets a host
check competing visible overlays without treating its own panel as a blocker.
Main-buffer overlay composition anchors the visible tail after a physical resize, including while an asynchronous custom frame has no cursor. The actual CPR row still drives cursor moves. No alternate-screen or whole-screen/scrollback erase is used, and historical rows above the visible viewport are never replayed. Extension hosts retain approval policy, visibility and focus decisions; component code does not gain a permission grant or direct terminal access.
Worker-side custom-view composition
xdog.tui.components.custom_layout provides bounded custom-view composition:
CustomText, CustomSpacer, CustomChild, CustomRow, CustomColumn,
CustomPadding, CustomBox, CustomViewport, CustomComponent, and custom_frame.
from xdog.tui.components.custom_layout import (
CustomBox, CustomChild, CustomColumn, CustomRow, CustomText, custom_frame,
)
layout = CustomColumn((
CustomChild(CustomText("Review"), size=1),
CustomChild(CustomRow((
CustomChild(CustomBox(CustomText("Main content", wrap=True), title="Main"), weight=2),
CustomChild(CustomBox(CustomText("Status"), title="Side"), size=18),
), gap=1)),
), gap=1)
def render(width, height):
return custom_frame(layout, width, height)
size reserves fixed cells along the parent axis; weight distributes remaining
space with deterministic integer largest-remainder allocation. Containers handle
narrow/zero-size viewports, clip at whole graphemes, and isolate SGR styles between
children. Padding uses explicit top/right/bottom/left cells. Box titles are safely
clipped. Text supports wrapping and left/center/right alignment.
CustomViewport(content, width, height, left=0, top=0) adds a scrollable virtual
canvas inside any box/row/column or another viewport. The extension owns scroll
state: rebuild the frozen viewport with new left/top offsets in its existing
input callback. Offsets are display cells/rows, not string bytes; cropping preserves
SGR and complete graphemes, including styled combining/ZWJ sequences. On resize,
offsets clamp to the last full visible canvas. A cursor is translated only while
visible; scrolling offscreen hides it without moving focus to another component.
from xdog.tui.components.custom_layout import CustomViewport
# In render(width, height); update left/top in your api.custom input callback.
return custom_frame(
CustomViewport(CustomText(document), width=120, height=320, left=left, top=top),
width, height,
)
Virtual dimensions are explicit and bounded: at most 8,192 columns, 4,096 rows, 128 Ki display cells per canvas, 512 Ki cells across all viewport nodes, and 512 KiB retained virtual-frame/component text. This is bounded canvas rendering, not an unbounded lazy document API; larger documents should use worker-side paging. Only the cropped host-sized frame crosses the existing protocol. No extra socket capability, host scrolling callback, direct terminal control or model call is added.
CustomComponent(component) adapts an existing worker-owned TUI Component.
The extension retains its state/focus and dispatches its own input; it should
provide the component's render budget (for example an InputPanel's
set_render_budget) when that component does not implement set_height.
The adapter extracts one cursor coordinate and strips the private marker before
serialization. Multiple focused cursors are rejected, not selected arbitrarily.
Limits: 256 rendered nodes, depth 12, 32 children per container, 128 KiB text/frame, 200 viewport rows, and 8,192 UTF-8 bytes per output line. Invalid/cyclic layouts or excessive component output fail visibly through the existing custom-view lifecycle. Zero-size viewports do not invoke components. Layouts are frozen; component state remains owned by the extension worker.
Use these helpers inside an extension's existing api.custom render callback.
Only ordinary sanitized lines/cursor frame data reaches the client; no layout
object or callback runs on the UI thread or writes terminal commands. Agent does
not gain a TUI dependency. Coding/Claw already install TUI; standalone extension
environments using this optional helper must install xdog-tui themselves.
Existing broker revisions, viewport generations, nested dialog leases, fullscreen
ownership and permission gates remain authoritative.
Explicit Windows clipboard snapshots
AttachmentReader now selects a dedicated helper on win32, invoked through the
current Python interpreter rather than a shell, PowerShell, clipboard history or
an application automation target. It reads the current Win32 clipboard only when
the composer requests paste. The existing owned process deadline, cancellation,
output bounds and composer epochs still apply; a read never submits a model turn.
The helper prefers PNG/JPEG/WebP data, then CF_DIBV5/CF_DIB (including Windows' synthesized bitmap formats), then bounded CF_UNICODETEXT. Image errors do not fall back to text. Clipboard handles remain clipboard-owned: they are locked/copied/ unlocked, never freed. The desktop clipboard is closed before raster conversion, with sequence checks before and after conversion. Encoded image framing trims native allocation padding rather than copying unused memory into an attachment.
The dependency-free DIB codec supports CORE/INFO/V4/V5 headers, top-down/bottom-up RGB, palette, 16/32-bit bitfields with declared alpha, RLE4/RLE8, embedded JPEG/PNG and bounded embedded ICC profiles. Source raster/pixel and final image limits are separate: an uncompressed desktop bitmap may be larger than its encoded PNG. Linked profiles are not followed. Self-contained calibrated RGB now becomes a bounded inline ICC matrix/TRC profile, retaining channel gamma, chromatic adaptation and the original RGBA samples. Singular/invalid calibration fails instead of being silently relabeled sRGB. This is not a declaration that every Windows bitmap, external profile reference or native desktop is verified.
Linux tests execute the helper/ABI and image conversion contracts with owned peers, and both real clients exercise the composer transport in private tmux. Native Windows clipboard/desktop/terminal validation remains unverified; this adapter is not a claim of complete Windows client or platform parity.
Reviewed Windows clipboard copy
The existing response/selection/transcript copy flow now chooses an owned native Windows helper before looking for Unix clipboard tools. Reviewed text is passed as UTF-8 on stdin, never in a shell command or argv, and published as terminated CF_UNICODETEXT. Unsupported embedded NUL and inputs above 32 MiB fail explicitly before native work; they are not silently truncated. Export remains available.
The helper creates an invisible message-only owner window (not a NULL clipboard owner), prepares movable memory and privacy-marker formats before emptying the clipboard, then transfers handles to the OS. It frees only handles that were not transferred and closes/destroys owned resources on failures. It requests exclusion from clipboard monitoring/history/cloud processing; these markers are not a confidentiality guarantee against other applications or platform behavior.
Pre-canceled operations do not launch a helper. Cancellation or failure after a write attempt reports an uncertain outcome, reaps the helper and does not retry or automatically substitute a file write. Existing unavailable-helper fallback, review panels, session/context fencing and local export/share behavior remain. Linux owned-peer/private-tmux checks cover both clients' production transport; native Windows desktop, privacy-flag behavior and overall client parity are still unverified.
Calibrated-color profile tests compare generated ICC transforms against an independently generated LittleCMS profile when that system library is available. It is an optional verification dependency, not a runtime package. A missing library causes an explicit skip and cannot be counted as color-conformance evidence. The fixed profile creation date/identity is deterministic and does not disclose the clipboard capture time or user. Conversion happens after releasing the clipboard; its sequence, pixel/profile/output budgets and owned helper cancellation still apply.
Paged output review on narrow terminals
Export/share/artifact review no longer requires an unusually wide terminal for a long destination. When metadata, warnings and the full target do not fit together, the output panel pages them with PgUp/PgDn/Home/End. Every displayed metadata/path row must be painted under the active focus owner before Enter can save. At least 24 columns and four panel rows are required; narrower/hidden views cannot confirm. Resize, focus loss and path editing revoke that proof and restart field review.
v opens the captured payload preview without enabling save, then returns to the
metadata/path review. Normal roomy layouts retain the combined preview. Tab edits
the path; repeated Tab/Enter or pasted confirmation text cannot approve an export.
A long Unicode path is reviewed without losing grapheme boundaries or replacing
its source value. Existing private atomic writes, no-overwrite, owner/context
fencing, no-upload and share-redaction policy remain authoritative.
In-view custom layers
CustomStack(background, layers) and frozen CustomLayer(content, left=0, top=0, width=None, height=None) compose opaque rectangles inside an extension's own
custom frame. Use them for a popup/menu/tooltip without asking the host for another
overlay or focus handle. Coordinates and sizes are display cells/rows; omitted
sizes use the remaining viewport, and rectangles clip to actual available space.
Each rectangle blanks its entire footprint, including rows with no foreground text. Clipping preserves SGR and full combining/ZWJ graphemes; cut background glyphs become overlapping blank cells rather than partial glyphs. A covered background cursor disappears, a layer cursor is translated, and multiple still-visible focused cursors are rejected. The worker owns focus/input state and must defocus its old editor when another visible editor should own the cursor.
Invisible/zero-size layers do not render components. A stack allows at most 32 layers and shares the existing node/depth/text/frame/virtual-canvas limits. Only sanitized ordinary lines and one cursor coordinate cross the current protocol; no layout object, callback or terminal-control command reaches a UI host. These helpers are distinct from the separately negotiated positioned host-overlay API.
Metadata
Release files for xdog-tui 2.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| xdog_tui-2.3.0.tar.gz | 368.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| xdog_tui-2.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 675.4 kB
Release files / xdog_tui-2.3.0.tar.gz
| Download URL | xdog_tui-2.3.0.tar.gz |
|---|---|
| Size | 368.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cbbde38f0b290c828209b8af95aa6f2ed2d166e9a2be7bd1d77ece65ecd29b2a
|
|
BLAKE2b-256 checksum How to use checksums |
a9cfdcd218246ee5b92722ed0ea4b7c66ecf92cdc616e1c25087921ab17bd3d3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.
Transparency logRelease files / xdog_tui-2.3.0-py3-none-any.whl
| Download URL | xdog_tui-2.3.0-py3-none-any.whl |
|---|---|
| Size | 306.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8b7e650d472d3c105b203d359cf0134398b438e32826485f60f57638ca57de93
|
|
BLAKE2b-256 checksum How to use checksums |
c64b2fc649ab5a62eeccea79847823b6f38aa8bde739595280630b9684eebaed
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.
Transparency log