oc_chat
Loading...
Searching...
No Matches
oc_chat.scripts.tui_spike Namespace Reference

Classes

class  _StreamSimulator

Functions

list[str] _build_sample_blocks ()
"tuple[object, _StreamSimulator]" build_application (*, list[str]|None sample_blocks=None, str initial_status="Ready. Ctrl+S start stream, Ctrl+Q quit, PgUp/PgDn scroll.", object|None input=None, object|None output=None)
int main ()

Variables

int MAX_CHUNKS_PER_SECOND = 80

Detailed Description

Standalone prompt_toolkit TUI feasibility spike (Phase TUI-1).

Purpose
-------
Prove — before committing to a full TUI rewrite of OC Chat — that the
risky parts of a prompt_toolkit-based three-pane terminal application
actually work on Windows Terminal + PowerShell, the project's
declared host. The risky parts (Codex review):

1. Rich-ANSI rendering for Markdown headings, fenced code with syntax
   highlighting, GitHub-style tables, and wide unicode (CJK / combining
   marks) inside a prompt_toolkit pane.
2. Independent pane scrolling via PgUp/PgDn/Home/End on the
   conversation pane only — header and input stay pinned.
3. SIGINT-during-stream behavior. A worker thread appends fake
   streaming chunks; Ctrl+C must cancel the stream and leave the app
   alive (no terminal corruption, no hang).
4. Resize tolerance. SIGWINCH on POSIX; prompt_toolkit's internal
   terminal-size polling on Windows.
5. Alt-screen vs non-alt-screen exit semantics. We use the default
   (non-alt-screen + full_screen=True style with explicit redraw) and
   verify the terminal restores cleanly after Ctrl+Q.

The spike is **deliberately not wired into the OC Chat runtime**. It
is a standalone runnable: ``python -m oc_chat.scripts.tui_spike``. The
companion ``README_TUI_SPIKE.md`` documents how to run it, the exit
criteria that gate Round 2, and a results table for manual smoke.

Responsibilities
----------------
- :func:`main` — bootstrap the prompt_toolkit Application and run it.
- :func:`build_application` — assemble the three-pane layout, key
  bindings, and styled controls. Returns a ready-to-run
  :class:`Application`.
- :class:`_StreamSimulator` — background thread that appends fake
  Rich-ANSI streaming chunks to the conversation buffer at a bounded
  rate. Stop event for SIGINT cancellation.
- :data:`SAMPLE_RICH_BLOCKS` — representative Rich-ANSI samples
  (Markdown heading, fenced code, table, long paragraph, wide
  unicode) used to populate the conversation pane on launch.

Diagnostics
-----------
Domain: OC_CHAT.TUI_SPIKE
Levels:
    L2 — application lifecycle (start / exit)
    L3 — stream simulation events (start, cancel, chunk rate cap)
    L4 — per-chunk trace

Contracts
---------
- The spike must not import any OC Chat runtime modules other than
  :mod:`oc_diagnostics`. It does not touch sessions, providers, the
  agent, or the registry. Deletable later without touching anything
  else.
- Rendering uses ``FormattedTextControl(text=ANSI(...))`` so Rich's
  ANSI output is passed through verbatim — prompt_toolkit does not
  re-parse the escape codes; it just hands them to the terminal.
- Worker-thread → UI handoff goes via
  :meth:`Application.create_background_task` /
  ``app.loop.call_soon_threadsafe``. Direct buffer mutation from the
  worker thread would race with the event loop.
- The simulated chunk rate is capped at 80 chunks/sec, matching the
  observed max from real DeepSeek-v4-pro generations. Above this,
  prompt_toolkit's redraw cadence struggles and the test isn't
  representative.
- SIGINT during streaming sets a ``threading.Event`` that the worker
  polls between chunks; the app stays alive. Ctrl+Q exits via the
  normal key-binding path.

Dependencies
------------
- oc_diagnostics (structured debug logging via _dbg)
- prompt_toolkit (lazy; 3.x Application, layout and key bindings, assumed installed for any developer running this spike)
- rich (lazy; optional ANSI rendering of the sample blocks, falls back to a plain-text sample when missing)

Function Documentation

◆ _build_sample_blocks()

list[str] oc_chat.scripts.tui_spike._build_sample_blocks ( )
protected
Return a list of Rich-rendered ANSI strings.

Tests the four risky rendering paths: Markdown heading, fenced
code with syntax highlighting, table, long paragraph, and wide
unicode. Falls back to plain text if ``rich`` is missing so the
spike still loads on a bare environment.

Definition at line 107 of file tui_spike.py.

Here is the caller graph for this function:

◆ build_application()

"tuple[object, _StreamSimulator]" oc_chat.scripts.tui_spike.build_application ( * ,
list[str] | None sample_blocks = None,
str initial_status = "Ready. Ctrl+S start stream, Ctrl+Q quit, PgUp/PgDn scroll.",
object | None input = None,
object | None output = None )
Construct the prompt_toolkit Application + its stream simulator.

Returns ``(application, stream_simulator)``. The application is not
yet running — call ``application.run()`` to drive it. Splitting the
factory from :func:`main` keeps the wiring inspectable from a
headless test (see :mod:`oc_chat.tests.unit.test_tui_spike`).

Headless test pattern (no real terminal needed)::

    from prompt_toolkit.input import create_pipe_input
    from prompt_toolkit.output import DummyOutput

    with create_pipe_input() as pipe_input:
        app, _ = build_application(input=pipe_input, output=DummyOutput())
        # ... drive app, inspect state ...

Definition at line 237 of file tui_spike.py.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ main()

int oc_chat.scripts.tui_spike.main ( )
Run the spike. Returns a process exit code (0 on clean exit).

Definition at line 405 of file tui_spike.py.

Here is the call graph for this function:
Here is the caller graph for this function:

Variable Documentation

◆ MAX_CHUNKS_PER_SECOND

int oc_chat.scripts.tui_spike.MAX_CHUNKS_PER_SECOND = 80

Definition at line 101 of file tui_spike.py.