Skip to content

Commit c9ec473

Browse files
feat(widgets): add textarea, left-bordered box, and blocks spinner (#109)
* feat(widgets): add textarea, left-bordered box, and block spinner - Add Textarea_widget: multiline text input with Shift+Enter for newlines, cursor navigation, line joining on backspace/delete, scroll support - Add render_left_border_box: opencode-style box with colored left border and background color - Add Block spinner style: animated block cursor ([ ] [▌] [█] [▐]) in a box - Extend Spinner_widget with style parameter (Dots or Block) Co-Authored-By: Claude <noreply@anthropic.com> * feat(widgets): add textarea, left-bordered box, and blocks spinner - Add Textarea_widget: multiline text input with Shift+Enter for newlines, cursor navigation, line joining on backspace/delete, scroll support - Add render_left_border_box: box with colored left border and background, useful for context sections or quoted content - Add Blocks spinner style: size + color gradient progression trail - Lead: ■ (large), trail: ▪ • · (shrinking) - Configurable direction (Left/Right) - Configurable block count Co-Authored-By: Claude <noreply@anthropic.com> * feat(input): add Alt+Enter key parsing for textarea newlines Replace ShiftEnter with AltEnter since most terminals don't send distinct escape sequences for Shift+Enter. Alt+Enter (ESC followed by newline) is universally supported. - Add AltEnter key variant to input_parser - Update textarea widget to use A-Enter/Alt-Enter keys - Update demos and documentation - Bump version to 0.3.1 Co-Authored-By: Claude <noreply@anthropic.com> * feat(widgets): add mouse support (wheel scroll + click) Add comprehensive mouse support to widgets: - New Mouse helper module with event parsing utilities - Pager: wheel scroll, click to position cursor (in cursor mode) - Select: wheel scroll, click to select item - File Browser: wheel scroll, click to select entry - Textbox: click to position cursor - Textarea: wheel scroll, click to position cursor - Tabs: click to select tab - Breadcrumbs: click on crumb to navigate - Button/Link: click to activate/navigate - Checkbox/Radio/Switch: click to toggle Co-Authored-By: Claude <noreply@anthropic.com> * feat(demos): wire mouse events to launcher and widget demos - Launcher: wheel scroll moves selection, click opens demo - Modals (select, poly_select, file_browser, textbox): forward mouse events via handle_modal_key since they don't parse as typed keys - Demos (pager, link, switch, validated_textbox): forward mouse events to their widgets Co-Authored-By: Claude <noreply@anthropic.com> * fix(mouse): improve click handling and add double-click support - Fix click vs selection: single clicks now dispatch to widgets instead of starting text selection. Actual text selection requires dragging. - Add double-click/triple-click event types (DoubleClick:, TripleClick:) which are passed to widgets for handling - Fix Escape key handling in all demos: use Keys.Escape variant instead of Char "Esc" which was never matched - Add modal click coordinate translation: store rendered modal position and translate screen coordinates to widget-relative coordinates - File browser: single click selects, double-click opens folder/file - Select widget: fix click handling to account for scroll offset and top indicator line Co-Authored-By: Claude <noreply@anthropic.com> * fix(matrix): remove unnecessary mark_all_dirty from mouse handlers Remove mark_all_dirty calls from MousePress, Mouse release, and MouseDrag handlers to fix screen flickering on mouse events. The back buffer is cleared and re-rendered every tick (30 TPS), with apply_highlight adding selection styling based on current state. The render domain (60 FPS) diffs front vs back and updates only changed cells. When selection changes or clears, the diff naturally detects and updates affected cells without needing a forced full redraw. Co-Authored-By: Claude <noreply@anthropic.com> * feat(box_widget): add per-side border colors Add border_colors parameter with c_top, c_bottom, c_left, c_right fields to allow different colors for each border side. Useful for indicating multiple states (e.g., selection on top/left, status on bottom/right). Corner colors prefer top/bottom colors when both adjacent sides have colors. When border_colors is provided, it takes precedence over the color parameter. * fix(pager): add ANSI reset and wrap-aware scrolling - Add ANSI reset at end of each rendered line to prevent color spill - Fix ensure_cursor_visible to account for wrapped line heights * fix(mouse): write enable sequence to /dev/tty for consistency The enable_mouse function was writing to stdout while all other matrix driver terminal operations write to /dev/tty. This inconsistency could cause buffering mismatches and race conditions leading to screen flicker. Now enable_mouse writes to tty_out_fd first (with tcdrain to ensure delivery), then to stdout as fallback, matching the pattern used by disable_mouse for reliability. Co-Authored-By: Claude <noreply@anthropic.com> * feat(signals): add install_signals' with optional SIGINT handling Add install_signals' variant that accepts ~handle_sigint:bool parameter. When false, SIGINT is not intercepted, allowing apps to receive Ctrl+C as a key event instead of immediate exit. The original install_signals is preserved unchanged (delegates to install_signals' with ~handle_sigint:true). * fix(matrix): remove screen clear from periodic scrub to prevent flicker The periodic scrub was clearing the entire screen with ESC[2J before redrawing, causing visible flicker. Instead, just mark all cells dirty and let the diff-based renderer overwrite them naturally. * feat(config): add handle_sigint option to control Ctrl+C behavior Add handle_sigint field to Matrix_config.t (default: true). When false, SIGINT is not intercepted, allowing apps to receive Ctrl+C as a key event ("C-c") instead of immediate exit. Propagates through Matrix_input.create and Matrix_driver. * feat(runner): add handle_sigint option to Runner_tui.run Allows apps to disable SIGINT handling so Ctrl+C is received as a key event ("C-c") instead of immediately exiting. * fix(terminal): disable c_isig in raw mode and ignore SIGINT when not handling Two changes to make Ctrl+C receivable as a key event: 1. Set c_isig=false in raw mode to prevent terminal from generating SIGINT on ^C - this allows the character (ASCII 3) to be read 2. When handle_sigint=false, explicitly ignore SIGINT to prevent default termination behavior Together these allow apps to receive Ctrl+C as "C-c" key event. * docs(changelog): document new widgets and signal updates Add changelog entries for signal handling options, Box_widget per-side border colors, and recent driver/mouse/pager fixes on the new-widgets branch. * chore(release): bump new-widgets to 0.3.2 * fix: copyright year, comment typo, spinner glyph and docs - Fix copyright year 2025 → 2026 in mouse.ml/mli - Fix comment "Shift+Enter" → "Alt+Enter" in textarea_widget.ml - Fix spinner_widget.mli docs: Blocks renders a single line, not 3 - Implement glyph parameter in render_blocks_glyph (was ignored) Circle: ● ○ • · and Dot: • ∙ · · variants now work --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent cd5db5e commit c9ec473

File tree

93 files changed

+2065
-279
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

93 files changed

+2065
-279
lines changed

CHANGELOG.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,41 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.3.2] - Unreleased
9+
10+
### Added
11+
12+
- **Textarea widget** (`miaou_widgets_input.Textarea_widget`): multiline text input with cursor navigation, line joining, and scroll support. Use Alt+Enter to insert newlines.
13+
- **Left-bordered box** (`Widgets.render_left_border_box`): display helper for context/quote blocks with colored left border and optional background.
14+
- **Blocks spinner style** (`Spinner_widget.Blocks`): animated spinner with size+color gradient progression trail, configurable direction and block count.
15+
- **Alt+Enter key parsing** (`Input_parser.AltEnter`): universally-supported newline insertion key for textarea widgets.
16+
- **Mouse helper module** (`Miaou_helpers.Mouse`): utilities for parsing mouse events (clicks, drags, wheel) in widgets.
17+
- **Mouse support for widgets**: wheel scrolling and click handling added to:
18+
- Pager: wheel scroll, click to position cursor (in cursor mode)
19+
- Select: wheel scroll, click to select item
20+
- File Browser: wheel scroll, click to select entry
21+
- Textbox: click to position cursor
22+
- Textarea: wheel scroll, click to position cursor
23+
- Tabs: click to select tab
24+
- Breadcrumbs: click on crumb to navigate
25+
- Button: click to activate
26+
- Link: click to navigate
27+
- Checkbox/Radio/Switch: click to toggle
28+
29+
- **Signal handling control**: optional SIGINT handling via `install_signals'` and `Runner_tui.run` `handle_sigint` option.
30+
- **Per-side border colors** for `Box_widget` to style each edge independently.
31+
32+
### Changed
33+
34+
- **Input parser**: added `AltEnter` key variant for Alt+Enter detection (ESC followed by newline).
35+
36+
### Fixed
37+
38+
- **Matrix driver scrub**: avoid screen clear during periodic scrub to reduce flicker.
39+
- **Terminal raw mode**: disable `c_isig` and ignore SIGINT when not handling it.
40+
- **Mouse interactions**: consistent enable sequence via `/dev/tty`, improved click handling, and double-click support.
41+
- **Pager**: add ANSI reset and wrap-aware scrolling.
42+
843
## [0.3.0] - Unreleased
944

1045
### Breaking Changes

dune-project

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
(lang dune 3.15)
22
(name miaou)
3-
(version 0.3.0)
3+
(version 0.3.2)
44
(generate_opam_files true)
55
(license MIT)
66

example/demos/bar_chart/page.ml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,7 @@ module Inner = struct
6464

6565
let handle_key s key_str ~size:_ =
6666
match Miaou.Core.Keys.of_string key_str with
67-
| Some (Miaou.Core.Keys.Char "Esc") | Some (Miaou.Core.Keys.Char "Escape")
68-
->
69-
go_back s
67+
| Some Miaou.Core.Keys.Escape -> go_back s
7068
| Some (Miaou.Core.Keys.Char " ") -> randomize_data s
7169
| _ -> s
7270

example/demos/box_widget/page.ml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,18 @@ module Inner = struct
5555
let ascii_box =
5656
BW.render ~title:"ASCII" ~style:Ascii ~width:box_w "Fallback style"
5757
in
58+
(* Left-bordered box *)
59+
let left_border_box =
60+
W.render_left_border_box
61+
~border_color:75
62+
~bg_color:236
63+
~cols:box_w
64+
"Left-bordered box\nWith colored bg\nFor context/quotes"
65+
in
5866
let left_col = single_box ^ "\n\n" ^ double_box ^ "\n\n" ^ ascii_box in
59-
let right_col = rounded_box ^ "\n\n" ^ nested_box in
67+
let right_col =
68+
rounded_box ^ "\n\n" ^ nested_box ^ "\n\n" ^ left_border_box
69+
in
6070
let left_child =
6171
{
6272
Flex.render = (fun ~size:_ -> left_col);

example/demos/braille/page.ml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,7 @@ module Inner = struct
120120

121121
let handle_key s key_str ~size:_ =
122122
match Miaou.Core.Keys.of_string key_str with
123-
| Some (Miaou.Core.Keys.Char "Esc") | Some (Miaou.Core.Keys.Char "Escape")
124-
->
125-
go_back s
123+
| Some Miaou.Core.Keys.Escape -> go_back s
126124
| Some (Miaou.Core.Keys.Char k) when String.lowercase_ascii k = "b" ->
127125
let mode = match s.mode with ASCII -> Braille | Braille -> ASCII in
128126
{s with mode}

example/demos/breadcrumbs/page.ml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,7 @@ module Inner = struct
6060

6161
let handle_key s key_str ~size:_ =
6262
match Miaou.Core.Keys.of_string key_str with
63-
| Some (Miaou.Core.Keys.Char "Esc") | Some (Miaou.Core.Keys.Char "Escape")
64-
->
65-
go_back s
63+
| Some Miaou.Core.Keys.Escape -> go_back s
6664
| _ ->
6765
let trail, handled =
6866
Breadcrumbs.handle_event ~bubble_unhandled:true s.trail ~key:key_str

example/demos/button/page.ml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,7 @@ module Inner = struct
4545
let button, fired = Button.handle_key s.button ~key:key_str in
4646
let clicks = if fired then s.clicks + 1 else s.clicks in
4747
match Miaou.Core.Keys.of_string key_str with
48-
| Some (Miaou.Core.Keys.Char "Esc") | Some (Miaou.Core.Keys.Char "Escape")
49-
->
50-
go_back {s with button; clicks}
48+
| Some Miaou.Core.Keys.Escape -> go_back {s with button; clicks}
5149
| _ -> {s with button; clicks}
5250

5351
let move s _ = s

example/demos/canvas/page.ml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,9 +244,7 @@ module Inner = struct
244244

245245
let handle_key s key_str ~size:_ =
246246
match Miaou.Core.Keys.of_string key_str with
247-
| Some (Miaou.Core.Keys.Char "Esc") | Some (Miaou.Core.Keys.Char "Escape")
248-
->
249-
go_back s
247+
| Some Miaou.Core.Keys.Escape -> go_back s
250248
| Some (Miaou.Core.Keys.Char k) when String.lowercase_ascii k = "b" ->
251249
let border_idx = (s.border_idx + 1) mod Array.length border_styles in
252250
{s with border_idx}

example/demos/card_sidebar/page.ml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,7 @@ module Inner = struct
5454

5555
let handle_key s key_str ~size:_ =
5656
match Miaou.Core.Keys.of_string key_str with
57-
| Some (Miaou.Core.Keys.Char "Esc") | Some (Miaou.Core.Keys.Char "Escape")
58-
->
59-
go_back s
57+
| Some Miaou.Core.Keys.Escape -> go_back s
6058
| Some Miaou.Core.Keys.Tab
6159
| Some (Miaou.Core.Keys.Char "Tab")
6260
| Some (Miaou.Core.Keys.Char "NextPage") ->

example/demos/checkbox/page.ml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,7 @@ module Inner = struct
7979

8080
let handle_key s key_str ~size:_ =
8181
match Miaou.Core.Keys.of_string key_str with
82-
| Some (Miaou.Core.Keys.Char "Esc") | Some (Miaou.Core.Keys.Char "Escape")
83-
->
84-
go_back s
82+
| Some Miaou.Core.Keys.Escape -> go_back s
8583
| Some Miaou.Core.Keys.Tab | Some (Miaou.Core.Keys.Char "Tab") ->
8684
let focus, _ = Focus_chain.handle_key s.focus ~key:"Tab" in
8785
{s with focus}

0 commit comments

Comments
 (0)