Cleared the terminal and aborted the interactive form cleanly on Ctrl-C.#60
Conversation
📝 WalkthroughWalkthroughCtrl-C is parsed as a named interrupt, handled across the panel loop and banner stage, surfaced through ChangesCtrl-C interrupt flow
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Terminal
participant KeyParser
participant PanelController
participant Tui
participant PlaygroundBootstrap
Terminal->>KeyParser: provide Ctrl-C byte
KeyParser->>PanelController: return KeyName::Interrupt
PanelController->>Tui: expose interrupted result
Tui->>Tui: throw InterruptException
PlaygroundBootstrap->>PlaygroundBootstrap: handle exception and exit 130
Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
This comment has been minimized.
This comment has been minimized.
|
🚀 Deployed on https://6a587f9a4ec4d45f672b989f--tui-docs.netlify.app |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #60 +/- ##
==========================================
+ Coverage 97.48% 97.49% +0.01%
==========================================
Files 97 97
Lines 2858 2876 +18
==========================================
+ Hits 2786 2804 +18
Misses 72 72 ☔ View full report in Codecov by Harness. |
This comment has been minimized.
This comment has been minimized.
Raw mode now disables 'isig', so Ctrl-C is delivered as byte 0x03 and parsed as a new 'KeyName::Interrupt' rather than raising SIGINT, which previously killed the process before the teardown ran and left the alternate screen and raw mode behind. The panel loop catches it above the field handlers - so it aborts from anywhere, including mid-widget and at the start banner - through a shared 'consumeInterrupt()' helper, then always restores and clears the screen, even when 'clearOnExit' is off. 'isInterrupted()' is exposed on the controller and the 'TuiTester' harness, and the interrupt renders as 'ctrl-c' in key hints.
c59a3c9 to
f729aa7
Compare
This comment has been minimized.
This comment has been minimized.
…or a submit. 'Tui::run()' and 'interact()' now throw a new 'InterruptException' when the user aborts the interactive session with Ctrl-C, rather than returning the partial answers a caller would then print as a completed form. Each interactive playground demo catches it inline and exits 130, so every script stays self-contained with no shared bootstrap. The abort is documented in the installation guide, and the self-contained-scripts rule is recorded in 'AGENTS.md'.
f729aa7 to
1905d47
Compare
|
Summary
Pressing Ctrl-C partway through the interactive panel TUI used to leave a broken terminal - the alternate screen stayed up, the cursor stayed hidden, the shell stayed in raw mode - and, once that was fixed, the collected answers were still printed as if the form had been submitted. This makes Ctrl-C a clean abort end to end: the screen is cleared and the terminal restored, and the abort is surfaced to the caller as a distinct
InterruptExceptionso partial answers are never mistaken for a completed form.Previously
Terminal::setup()leftisigenabled, so Ctrl-C raisedSIGINTand killed the process before thetry/finallyteardown could run. Now raw mode disablesisig, Ctrl-C arrives as byte0x03, and it is parsed as a first-classKeyName::Interrupt.Changes
Interrupt handling (core)
src/Render/Terminal.php-setup()adds-isigso Ctrl-C is delivered as input rather than a signal;stty saneinrestore()re-enables it on exit.src/Input/KeyName.php,src/Input/KeyParser.php,src/Testing/KeyEncoder.php- a newKeyName::Interruptcase, with0x03parsed to it and the encoder round-trip kept complete.src/Render/PanelController.php- a sharedconsumeInterrupt()catches the interrupt above the field/widget handlers (so it aborts from anywhere, including mid-widget and at the start banner), breaks the read loop, and always restores and clears the screen even whenclearOnExitis off. Adds anisInterrupted()accessor.src/Testing/TuiTester.php- exposesisInterrupted()for the harness.src/Theme/DefaultTheme.php,translations/tui.php- the interrupt renders asctrl-cin key hints, with its catalog entry.Abort signalling (facade + demos)
src/InterruptException.php- a new exception representing a user abort.src/Tui.php-run()andinteract()throwInterruptExceptionwhen the run was interrupted, instead of returning the partial answers; docblocks document both this and the existingEngineException.playground/*- every interactive demo catchesInterruptExceptioninline and exits130(the conventional SIGINT code): atry/catchsibling for the numbered demos, a wrap around theechofor the widget one-liners. Each script stays fully self-contained - it requires the autoloader directly, with no shared bootstrap or helper. The headless demos are untouched.Docs and conventions
docs/content/installation.mdx- an "Aborting with Ctrl-C" section showing thecatch (InterruptException)pattern;docs/cspell.jsongainsCtrlandSIGINT.AGENTS.md- records that each playground script must be self-contained.Tests
clearOnExitis off, and aborts at the banner), harness reporting, and a facade test assertinginteract()throws on Ctrl-C. Changed classes stay fully covered.Screenshots
N/A - headless library change, nothing rendered.
Before / After