Skip to content
claudewheel.app
Edit
On this page

App class -- TUI event loop for claudewheel.

#claudewheel.app

#claudewheel.app

TUI event loop, keyboard dispatch, and segment interaction.

#KeyContext

Ephemeral snapshot of state relevant to key dispatch decisions.

#Binding

A single keybinding entry in the registry.

#App

TUI application managing the event loop, keyboard handling, and segment interaction.

#run_tui

python
def run_tui(self) -> dict[str, str | None] | None

Enter the TUI loop. Returns selections on launch, None on quit.

#_select_client

python
def _select_client(self) -> bool

Run the Client selection step and drop claude-only segments.

Explicit --client wins and skips the picker (see :func:claudewheel.clients.resolve_client); otherwise the picker fans out the :data:~claudewheel.clients.CLIENT_ADAPTERS registry with the configured default pre-focused. Returns False when the user cancels the picker (Esc/Ctrl-C) so the caller quits cleanly; True otherwise.

When the resolved client is not claude, the version segment is removed from the bar: it selects a claudewheel-managed claude binary, so it is inapplicable to any other client and is skipped entirely.

#_promote_ephemeral

python
def _promote_ephemeral(self) -> None

Promote ephemeral selections to pinned on disk before launch.

#_run_slow_discovery_thread

python
def _run_slow_discovery_thread(self) -> None

Background thread: run slow discovery and store results.

#_apply_slow_discovery

python
def _apply_slow_discovery(self) -> None

Merge slow discovery results into the live segment bar.

Results for the focused segment are deferred to avoid disrupting the user's current interaction. Unfocused segments are updated immediately.

Known limitation: while results are buffered, evaluate_requires() uses the focused segment's pre-discovery options for cross-segment constraint checks. Constraint-based dimming may be briefly stale until defocus.

#_apply_pending_for_segment

python
def _apply_pending_for_segment(self, seg: Segment) -> None

Apply any deferred discovery results for seg and clear pending state.

#_defocus

python
def _defocus(self) -> None

Run deferred-apply housekeeping on the segment about to lose focus.

#_build_context

python
def _build_context(self) -> KeyContext

Build an ephemeral KeyContext from current app state.

#_compute_hints

python
def _compute_hints(self) -> list[str]

Compute visible hint labels from the binding registry for the current state.

#_handle_key

python
def _handle_key(self, key: str) -> str | None

Process a keypress via the binding registry.

#_h_main_left

python
def _h_main_left(self, key: str) -> str | None

Handle left arrow: defocus current segment and move focus left.

#_h_main_right

python
def _h_main_right(self, key: str) -> str | None

Handle right arrow: defocus current segment and move focus right.

#_h_main_up

python
def _h_main_up(self, key: str) -> str | None

Handle up arrow: clear search buffer and cycle selection up.

#_h_main_down

python
def _h_main_down(self, key: str) -> str | None

Handle down arrow: clear search buffer and cycle selection down.

#_h_main_enter

python
def _h_main_enter(self, key: str) -> str | None

Handle enter: launch if valid, or enter creation/install/auth flow.

#_h_main_tab

python
def _h_main_tab(self, key: str) -> str | None

Handle tab: accept search match or advance focus to next segment.

#_h_main_backspace

python
def _h_main_backspace(self, key: str) -> str | None

Handle backspace: enter freeform editing or trim search buffer.

#_h_main_esc

python
def _h_main_esc(self, key: str) -> str | None

Handle escape: clear search buffer and exit freeform editing.

#_h_main_ctrl_c

python
def _h_main_ctrl_c(self, key: str) -> str | None

Handle Ctrl-C: quit the TUI.

#_h_main_delete

python
def _h_main_delete(self, key: str) -> str | None

Handle delete key: initiate profile deletion flow if on profile segment.

#_h_main_question

python
def _h_main_question(self, key: str) -> str | None

Handle '?': toggle provenance overlay visibility.

#_h_main_inspect

python
def _h_main_inspect(self, key: str) -> str | None

Handle 'i': open the profile inspect page for the focused profile.

#_h_main_sessions

python
def _h_main_sessions(self, key: str) -> str | None

Handle 'S': open the machine-wide sessions overview.

#_h_main_freeform_seed

python
def _h_main_freeform_seed(self, key: str) -> str | None

Handle first printable key on freeform segment: seed editing from current value.

python
def _h_main_search(self, key: str) -> str | None

Handle printable key on searchable segment: append to search buffer.

#_h_main_quit

python
def _h_main_quit(self, key: str) -> str | None

Handle 'q' on non-searchable segment: quit the TUI.

#_h_theme_switch

python
def _h_theme_switch(self, key: str) -> str | None

Handle Mode 2031 theme-change notification.

#_h_freeform_enter

python
def _h_freeform_enter(self, key: str) -> str | None

Handle enter in freeform mode: submit the typed text as a new value.

#_h_freeform_tab

python
def _h_freeform_tab(self, key: str) -> str | None

Handle tab in freeform mode: accept first matching option.

#_h_freeform_backspace

python
def _h_freeform_backspace(self, key: str) -> str | None

Handle backspace in freeform mode: remove last character from buffer.

#_h_freeform_left

python
def _h_freeform_left(self, key: str) -> str | None

Handle left arrow in freeform mode: cancel editing and move focus left.

#_h_freeform_right

python
def _h_freeform_right(self, key: str) -> str | None

Handle right arrow in freeform mode: cancel editing and move focus right.

#_h_freeform_esc

python
def _h_freeform_esc(self, key: str) -> str | None

Handle escape in freeform mode: cancel editing and clear buffer.

#_h_freeform_ctrl_c

python
def _h_freeform_ctrl_c(self, key: str) -> str | None

Handle Ctrl-C in freeform mode: cancel editing and quit.

#_h_freeform_printable

python
def _h_freeform_printable(self, key: str) -> str | None

Handle printable key in freeform mode: append to search buffer.

#_h_create_enter

python
def _h_create_enter(self, key: str) -> str | None

Handle enter in creating mode: confirm creation of the new option.

#_h_create_esc

python
def _h_create_esc(self, key: str) -> str | None

Handle escape in creating mode: cancel and clear the create buffer.

#_h_create_backspace

python
def _h_create_backspace(self, key: str) -> str | None

Handle backspace in creating mode: remove last character from create buffer.

#_h_create_ctrl_c

python
def _h_create_ctrl_c(self, key: str) -> str | None

Handle Ctrl-C in creating mode: cancel creation and quit.

#_h_create_printable

python
def _h_create_printable(self, key: str) -> str | None

Handle printable key in creating mode: append to create buffer.

#_run_install_flow

python
def _run_install_flow(self, seg: Segment, version: str) -> None

Confirm install via run_selection, download in cooked, show result page.

#_build_bindings

python
def _build_bindings(self) -> list[Binding]

Build the full binding registry from handler methods.

#_h_main_search_or_quit

python
def _h_main_search_or_quit(self, key: str) -> str | None

Handle printable key on searchable segment: search or quit.

#_intercept_unauth

python
def _intercept_unauth(self, seg: Segment) -> str

Prompt auth for an unauthenticated profile before launch.

The app's terminal stays raw: the auth forms render borrowed as pages in the existing alt screen, and the subprocess steps inside the auth flow open their own cooked windows. On "authenticated", "unverified" (a token was saved without validation), and "failed" (credentials may be partially written), re-runs profile discovery and updates auth status. Returns the auth flow outcome: "authenticated", "unverified", "skip", "cancel", or "failed".

#_show_profile_inspect

python
def _show_profile_inspect(self, seg: Segment) -> None

Show a fullscreen inspect page for the focused profile option.

The app's terminal stays raw: show_page renders borrowed in the existing alt screen and the main TUI repaints on return. If the profile has an auth shadow, the hint offers 'f' to fix it.

#_delete_profile_flow

python
def _delete_profile_flow(self, seg: Segment) -> None

Confirm and delete the focused profile from the TUI.

A reserved name is answered first, with its own page and no command to run -- before any inspection, any confirmation and any page about destroying data. Profiles holding REAL data at shared-dir names are hard-blocked with a fullscreen page pointing at the CLI escape hatch -- the TUI offers no override. Otherwise an informed two-option confirm runs (Cancel default-focused). The running check stays CLI/TUI policy; the actual deletion goes through ProfileStore.delete (no force flags). Store refusals raise ValueError, surfaced as a flash.

#_show_sessions_overview

python
def _show_sessions_overview(self) -> None

Show every Claude Code session on this machine.

Machine-wide, so no segment decides anything: the table covers every profile the workspace discovers plus every session the lifecycle store recorded, and the key opens it from anywhere on the bar.

The app's terminal stays raw -- the overview renders borrowed in the existing alt screen, like every other fullscreen surface here -- and the main TUI repaints on return.

#_resolve_archiver

python
def _resolve_archiver(self, name: str) -> 'Saferm | None'

The archiving tool this deletion will use, or None to abort.

Deletion delegates to saferm so it can be undone, so "is saferm here, and does it ship what the delegation uses" is a precondition of the operation. When the answer is no, the screen says which of the three it is, states what deleting without it would cost, and offers to install it -- there is a person here to ask. Declining aborts the deletion, a failed install aborts it, and a freshly installed binary that still does not answer the probe aborts it. None of those is an option that deletes the profile anyway.

A preview is the one case where having somebody to ask is not enough, and it is the trap: under --dry-run there is still a person at the keyboard who can accept an offer, and accepting it would make a run that promised to change nothing download and install a program. So the offer is not made at all -- the same refusal the scripted door in :func:claudewheel.cli._resolve_archiver makes, for the same reason.

#_run_deletion_checklist

python
def _run_deletion_checklist(self, name: str) -> 'ChecklistOutcome'

Show what holds name and stop whatever the user ticks.

The daemon and its workers arrive pre-ticked; everything else is listed untouched. Returns the screen's outcome -- whether the deletion was confirmed, and what is still holding the profile afterwards.

#_refresh_profile_segment

python
def _refresh_profile_segment(self, seg: Segment) -> None

Re-run profile discovery and update the segment's auth status.

#_launch_profile_wizard

python
def _launch_profile_wizard(self, seg: Segment) -> str | None

Run the create-profile flow as one continuous alt-screen session.

The app's terminal stays raw throughout: the wizard form, the auth forms, and the creation summary page all render borrowed in the existing alt screen (subprocess steps open cooked windows inside the auth flow). The main TUI repaints on return.

#_confirm_create

python
def _confirm_create(self, seg: Segment) -> None

Confirm creation of a new option.

More tools from this site

  • claudestream Drive Claude Code from Python: run it as a subprocess and read its output as typed events, with async and sync sessions, sandbox policies, and tools you define in Python
  • dirstat Fast, single-binary directory statistics CLI: every file under a tree grouped by format, with counts, sizes, and lines of code, as a colored terminal table or as JSON
  • fastware A batteries-included ASGI framework: msgspec JSON, a managed Granian server, dependency injection, SSE, WebSockets, auth, and a test client
  • go-toml-edit Zero-dep TOML editing library for Go with comment preservation
  • howmuchleft The fastest Claude Code statusline: context window, 5-hour, and weekly limit usage as three customizable gradient bars, rendering in about 6 ms
  • orxtra
  • pgdesign
  • predraw Declarative rendering pipeline: describe a scene in JSON and get SVG, PNG and WebP out, with light and dark style tokens, reusable components and text converted to path outlines
  • reposummary Turn a git repository's history into a Markdown journal: pick a time window or revision range and get a readable digest of what changed, optionally narrated by an LLM
  • rlsbl Release orchestration and project scaffolding CLI that bumps versions, validates a structured JSONL changelog, tags only the commit CI verified, and publishes to npm, PyPI, Go and more
  • safegit git wrapper CLI that gives each commit its own temporary index and retries ref updates on conflict, so concurrent agents share one repository
  • saferm Command-line replacement for rm that archives every deletion with a mandatory reason and the context it ran in, so deleted files can be listed, inspected and restored
  • selfdoc Static Site Generator that builds a project's documentation site directly from its source code, so the docs can never drift from the code they describe, with SEO/AEO, first-class blog, search, and cross-project linking built in
  • strictcli
  • stricttest An always-on test-isolation floor: a pytest plugin and a Go env-hygiene module that make a test suite structurally unable to reach real credentials, the real HOME, the network, or the development repository.
  • wesktop A Python framework that turns an ASGI web app into a desktop application, serving it from a local Granian server and displaying it in a native OS window via pywebview
Search