@oh-my-pi/pi-tui 18.10.4 → 18.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/CHANGELOG.md +47 -136
  2. package/dist/types/autocomplete.d.ts +2 -0
  3. package/dist/types/chat/figure-markdown.d.ts +7 -2
  4. package/dist/types/components/image.d.ts +2 -0
  5. package/dist/types/components/markdown.d.ts +45 -0
  6. package/dist/types/components/text.d.ts +2 -0
  7. package/dist/types/keybinding-matchers.d.ts +2 -0
  8. package/dist/types/multiplexers/tmux.d.ts +1 -0
  9. package/dist/types/multiplexers/types.d.ts +6 -0
  10. package/dist/types/native/blobs.d.ts +11 -11
  11. package/dist/types/overlays/approval-dialog.d.ts +24 -6
  12. package/dist/types/overlays/job-tree.d.ts +50 -0
  13. package/dist/types/overlays/jobs-panel.d.ts +24 -11
  14. package/dist/types/prompt/custom-editor.d.ts +18 -10
  15. package/dist/types/prompt/image-references.d.ts +22 -0
  16. package/dist/types/render/hanging-wrap.d.ts +5 -0
  17. package/dist/types/render/hyperlink.d.ts +2 -0
  18. package/dist/types/render/utils.d.ts +1 -1
  19. package/dist/types/status-line/context-usage.d.ts +3 -0
  20. package/dist/types/status-line/startup.d.ts +6 -6
  21. package/dist/types/stdin-buffer.d.ts +7 -0
  22. package/dist/types/terminal-capabilities.d.ts +8 -0
  23. package/dist/types/terminal-multiplexer.d.ts +1 -0
  24. package/dist/types/terminal.d.ts +7 -3
  25. package/dist/types/tmux.d.ts +13 -4
  26. package/dist/types/tools/eval.d.ts +5 -0
  27. package/dist/types/tools/grouped-file-output.d.ts +2 -0
  28. package/dist/types/tools/read.d.ts +5 -4
  29. package/dist/types/tui.d.ts +1 -0
  30. package/package.json +9 -9
  31. package/src/autocomplete.ts +14 -19
  32. package/src/bracketed-paste.ts +9 -2
  33. package/src/chat/assistant-message.ts +53 -13
  34. package/src/chat/bash-execution.ts +7 -2
  35. package/src/chat/figure-markdown.ts +10 -3
  36. package/src/chat/image-loading.ts +4 -6
  37. package/src/chat/read-tool-group.ts +6 -1
  38. package/src/chat/svg-figure.ts +3 -1
  39. package/src/chat/svg-source.ts +6 -3
  40. package/src/chat/tool-execution.ts +11 -4
  41. package/src/chat/user-message.ts +22 -15
  42. package/src/components/editor.ts +12 -12
  43. package/src/components/image.ts +23 -6
  44. package/src/components/markdown.ts +387 -6
  45. package/src/components/text.ts +11 -1
  46. package/src/keybinding-matchers.ts +5 -0
  47. package/src/multiplexers/tmux.ts +3 -0
  48. package/src/multiplexers/types.ts +6 -0
  49. package/src/native/blobs.ts +22 -6
  50. package/src/native/reconcile.ts +19 -15
  51. package/src/overlays/approval-dialog.ts +115 -24
  52. package/src/overlays/job-tree.ts +177 -0
  53. package/src/overlays/jobs-panel.ts +107 -49
  54. package/src/prompt/composer.ts +22 -12
  55. package/src/prompt/custom-editor.ts +149 -64
  56. package/src/prompt/image-references.ts +79 -2
  57. package/src/render/code-cell.ts +18 -5
  58. package/src/render/hanging-wrap.ts +31 -0
  59. package/src/render/hyperlink.ts +5 -0
  60. package/src/render/utils.ts +1 -1
  61. package/src/render/width-aware-text.ts +6 -5
  62. package/src/status-line/component.ts +4 -0
  63. package/src/status-line/context-usage.ts +22 -16
  64. package/src/status-line/startup.ts +6 -6
  65. package/src/stdin-buffer.ts +11 -1
  66. package/src/terminal-capabilities.ts +25 -7
  67. package/src/terminal.ts +52 -20
  68. package/src/tmux.ts +42 -16
  69. package/src/tools/ask.ts +20 -9
  70. package/src/tools/ast-grep.ts +2 -2
  71. package/src/tools/edit.ts +20 -2
  72. package/src/tools/eval.ts +11 -4
  73. package/src/tools/grouped-file-output.ts +6 -1
  74. package/src/tools/lsp.ts +9 -2
  75. package/src/tools/read.ts +6 -5
  76. package/src/tools/todo.ts +3 -2
  77. package/src/tools/write.ts +15 -5
  78. package/src/tui.ts +44 -25
  79. package/src/utils.ts +5 -3
package/CHANGELOG.md CHANGED
@@ -2,6 +2,52 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.11.1] - 2026-10-15
6
+
7
+ ### Added
8
+
9
+ - Added a tree layout to the background jobs sheet: subagents' jobs nest under the task job that runs them, busiest branches first, and Cancel appears only on the viewing agent's own jobs ([#15854](https://github.com/can1357/oh-my-pi/pull/15854) by [@H4vC](https://github.com/H4vC))
10
+
11
+ ### Fixed
12
+
13
+ - Fixed transcript images not opening their file: image placeholders and native image nodes now link to the source file or a stored copy ([#15872](https://github.com/can1357/oh-my-pi/pull/15872) by [@H4vC](https://github.com/H4vC))
14
+ - A collapsing live block no longer leaves its slack as a band of blank rows between the retired history and the live rows: the slack falls below the transcript, so a streamed reply stays contiguous and the input keeps the bottom row ([#15499](https://github.com/can1357/oh-my-pi/pull/15499) by [@iacore](https://github.com/iacore))
15
+ - Fixed width-aware text on native (TSP) terminals truncating content two columns early ([#15806](https://github.com/can1357/oh-my-pi/pull/15806) by [@yuzu-octopus](https://github.com/yuzu-octopus))
16
+ - Fixed Tab/right-arrow completion silently dismissing a selected `@"quoted path"` file suggestion when the path contains spaces ([#15698](https://github.com/can1357/oh-my-pi/pull/15698) by [@drawde-nadroj](https://github.com/drawde-nadroj))
17
+ - Bracketed pastes are now capped by their UTF-8 size, so a large multibyte paste no longer buffers past the byte limit ([#15803](https://github.com/can1357/oh-my-pi/pull/15803) by [@yuzu-octopus](https://github.com/yuzu-octopus))
18
+ - Fixed OSC 66 text-sized spans bypassing truncation when their UTF-16 length was short enough to fit the fast-path guard ([#15802](https://github.com/can1357/oh-my-pi/pull/15802) by [@yuzu-octopus](https://github.com/yuzu-octopus))
19
+ - Updated the `ast_grep` result view's truncated-match note to suggest `skip` pagination instead of the schema's nonexistent `increase limit` flag ([#15532](https://github.com/can1357/oh-my-pi/issues/15532), [#15543](https://github.com/can1357/oh-my-pi/pull/15543) by [@peda-cos](https://github.com/peda-cos)).
20
+ - Fixed a streaming Edit preview with long wrapped lines growing past its row cap, because its tail was budgeted without the diff gutter its continuation rows hang under ([#15679](https://github.com/can1357/oh-my-pi/pull/15679))
21
+ - Fixed malformed base64 padding being accepted for chat image attachments before native image decoding ([#15765](https://github.com/can1357/oh-my-pi/pull/15765) by [@anatoli-tsinovoy](https://github.com/anatoli-tsinovoy)).
22
+ - Fixed Shift+Enter submitting and Ctrl+Enter inserting a newline in Windows Terminal 1.25–1.27 after closing any fullscreen view such as `/settings`; the terminal dropped omp's keyboard mode on leaving the alternate screen ([microsoft/terminal#20763](https://github.com/microsoft/terminal/issues/20763)), so omp now re-applies it ([#15868](https://github.com/can1357/oh-my-pi/pull/15868) by [@H4vC](https://github.com/H4vC)).
23
+ - Long lines in native (Tern) `read`, `write`, and code-cell source now wrap instead of scrolling each block sideways ([#15823](https://github.com/can1357/oh-my-pi/issues/15823)).
24
+ - Fixed long text pastes taking seconds to land in terminals that deliver a paste one key at a time, such as JetBrains' terminal on Windows; omp no longer writes to the terminal for every pasted key ([#15757](https://github.com/can1357/oh-my-pi/issues/15757))
25
+ - Fixed Shift+Enter submitting the prompt and Ctrl+Enter inserting a newline in WSL under Windows Terminal 1.24 and earlier, conhost, and other ConPTY hosts without kitty keyboard support; Shift+Enter now inserts a newline and Ctrl+Enter sends a follow-up, as on native Windows.
26
+ - Fixed streamed Markdown tables jumping around as each row arrives: the terminal UI fills the row being written in under the existing column widths, and native (TSP) terminals receive each row once its line is complete ([#15856](https://github.com/can1357/oh-my-pi/pull/15856) by [@H4vC](https://github.com/H4vC)).
27
+ - Fixed streamed links and inline math showing raw `[text](url` or LaTeX and then rewrapping the paragraph when they close, in the terminal UI and native (TSP) terminals: they now render in their closed form while they stream ([#15864](https://github.com/can1357/oh-my-pi/pull/15864) by [@H4vC](https://github.com/H4vC)).
28
+ - Paint-only overlays no longer swallow typing intended for the focused input beneath them ([#15793](https://github.com/can1357/oh-my-pi/issues/15793)).
29
+
30
+ ## [18.11.0] - 2026-10-14
31
+
32
+ ### Added
33
+
34
+ - Added native composer microphone controls with visible starting, recording and transcription states; a start still loading its model can be cancelled, and Enter and Send wait while dictation is recording or transcribing into the draft ([#15759](https://github.com/can1357/oh-my-pi/pull/15759) by [@daaximus](https://github.com/daaximus)).
35
+ - `ApprovalDialogComponent` takes a `draft` option that routes typing to the composer beneath it, so a message can be written while a tool call waits ([#15642](https://github.com/can1357/oh-my-pi/pull/15642) by [@H4vC](https://github.com/H4vC))
36
+ - `ApprovalDialogComponent` takes a `fullPrompt` option that the expand key (`app.tools.expand`) or a Details button swaps in for the summarized prompt; it replaces `wrapDetails` ([#15642](https://github.com/can1357/oh-my-pi/pull/15642) by [@H4vC](https://github.com/H4vC))
37
+
38
+ ### Fixed
39
+
40
+ - SVG fences in CRLF or CR-only assistant Markdown now render as figures instead of source code ([#15663](https://github.com/can1357/oh-my-pi/issues/15663)).
41
+ - Fixed clipboard-image and image-path pastes being dropped while the composer draft is edited beneath an ask or approval dialog ([#15642](https://github.com/can1357/oh-my-pi/pull/15642) by [@H4vC](https://github.com/H4vC))
42
+ - Fixed inline images inside tmux assuming Kitty graphics from the pane's `TERM`; the attached client is identified from tmux's terminal-type reply or its `TERM` (e.g. `xterm-kitty`), and unrecognized clients go to the SIXEL probe ([#15524](https://github.com/can1357/oh-my-pi/issues/15524)).
43
+ - Fixed ```svg fences rendering as their source code instead of a drawing when no theme was installed yet, e.g. in an extension rendering a message that opens with the fence ([#15660](https://github.com/can1357/oh-my-pi/issues/15660))
44
+ - Fixed pastes inside tmux 3.5a and older submitting line by line after another program turned bracketed paste off on the pane; omp now re-enables it on the next render or keypress ([#15596](https://github.com/can1357/oh-my-pi/issues/15596))
45
+ - Fixed assistant replies containing a table (on image-capable terminals with table charts on) publishing still-streaming rows to native scrollback, which froze the block's append-only retirement and could leave overwritten reply rows in scrollback ([#15693](https://github.com/can1357/oh-my-pi/issues/15693))
46
+ - Fixed streamed thinking publishing a line to native scrollback just before a code fence or `<!-- -->` sentinel split across deltas rewrote it with the prose-fold ellipsis ([#15693](https://github.com/can1357/oh-my-pi/issues/15693))
47
+ - Fixed streamed assistant text or thinking containing a tab stopping all later rows of the reply from retiring into native scrollback until the turn ended ([#15693](https://github.com/can1357/oh-my-pi/issues/15693))
48
+ - Fixed wrapped lines in Write, Read, Edit, Todo, Ask and LSP tool cards losing their alignment under line numbers, tree connectors, and option or code rails ([#15677](https://github.com/can1357/oh-my-pi/issues/15677))
49
+ - Fixed SIXEL images leaving their previous raster on screen, drawn again over itself, when the transcript moves them within the viewport ([#15657](https://github.com/can1357/oh-my-pi/issues/15657))
50
+
5
51
  ## [18.10.4] - 2026-10-13
6
52
 
7
53
  ### Added
@@ -2622,139 +2668,4 @@
2622
2668
  - Added `cursorOverride` and `cursorOverrideWidth` properties to customize the end-of-text cursor glyph with ANSI-styled strings
2623
2669
  - Added `getUseTerminalCursor()` method to query the terminal cursor mode setting
2624
2670
 
2625
- ## [11.10.0] - 2026-02-10
2626
-
2627
- ### Added
2628
-
2629
- - Added `hint` property to autocomplete items to display dim ghost text after cursor when item is selected
2630
- - Added `getInlineHint()` method to `SlashCommand` interface for providing inline hint text based on argument state
2631
- - Added `getInlineHint()` method to `AutocompleteProvider` interface for displaying dim ghost text after cursor
2632
- - Added `hintStyle` theme option to customize styling of inline hint/ghost text in editor
2633
-
2634
- ### Changed
2635
-
2636
- - Updated editor to render inline hint text as dim ghost text after cursor when autocomplete suggestions are active or provider supplies hints
2637
-
2638
- ## [11.8.0] - 2026-02-10
2639
-
2640
- ### Added
2641
-
2642
- - Added Alt+Y keybinding to cycle through kill ring entries (yank-pop)
2643
- - Added undo support to Input component with Ctrl+Z keybinding
2644
- - Added kill ring support to Input component for Emacs-style kill/yank operations
2645
- - Added yank (Ctrl+Y) and yank-pop (Alt+Y) support to Input component
2646
-
2647
- ### Changed
2648
-
2649
- - Changed Editor kill ring implementation to use dedicated KillRing class for better state management
2650
- - Changed Editor undo stack to use generic UndoStack class with automatic state cloning
2651
- - Changed kill/yank behavior to properly accumulate consecutive kill operations
2652
- - Changed Input component deletion methods to record killed text in kill ring
2653
- - Changed undo coalescing in Input component to group consecutive word typing into single undo units
2654
-
2655
- ## [11.4.1] - 2026-02-06
2656
-
2657
- ### Fixed
2658
-
2659
- - Fixed terminal scrolling when displaying overlays after rendering large content, preventing hundreds of blank lines from being output
2660
-
2661
- ## [11.3.0] - 2026-02-06
2662
-
2663
- ### Breaking Changes
2664
-
2665
- - Removed `getCursorPosition()` method from Component interface and implementations, eliminating hardware cursor positioning support
2666
-
2667
- ### Added
2668
-
2669
- - Added sticky column behavior for vertical cursor movement, preserving target column when navigating through lines of varying lengths
2670
- - Added `drainInput()` method to Terminal interface to prevent Kitty key release events from leaking to parent shell over slow SSH connections
2671
- - Added `setClearOnShrink()` method to control whether full re-render occurs when content shrinks below working area
2672
- - Added support for hidden paths (e.g., `.pi`, `.github`) in autocomplete while excluding `.git` directories
2673
-
2674
- ### Changed
2675
-
2676
- - Changed default value of `PI_HARDWARE_CURSOR` environment variable from implicit true to explicit `"1"` for clarity
2677
- - Changed default value of `PI_CLEAR_ON_SHRINK` environment variable from implicit false to explicit `"0"` for clarity
2678
- - Changed TUI to clear screen on startup to prevent shell prompts and status messages from bleeding into the first rendered frame
2679
- - Refactored full-render logic into reusable helper function to reduce code duplication across multiple render paths
2680
- - Changed autocomplete to include hidden paths but filter out `.git` and its contents
2681
- - Changed Input component to properly handle surrogate pairs in Unicode text, preventing cursor display corruption with emoji and multi-byte characters
2682
- - Changed Editor to use `setCursorCol()` for all cursor column updates, enabling sticky column tracking
2683
- - Changed Editor's vertical navigation to implement sticky column logic via `moveToVisualLine()` and `computeVerticalMoveColumn()`
2684
- - Changed Editor's Enter key handling to extract submit logic into `submitValue()` method for better code organization
2685
- - Changed SettingsList to truncate long lines to viewport width, preventing text overflow
2686
- - Changed Terminal's `stop()` method to drain stdin before restoring raw mode, fixing race condition where Ctrl+D could close parent shell over SSH
2687
- - Changed TUI rendering to add `clearOnShrink` option (controlled by `PI_CLEAR_ON_SHRINK` env var) for reducing redraws on slower terminals
2688
- - Changed TUI rendering to detect when extra lines exceed viewport height and trigger full re-render instead of incremental updates
2689
-
2690
- ### Fixed
2691
-
2692
- - Fixed rendering of extra blank lines when content shrinks by improving cursor positioning logic during line deletion
2693
- - Fixed cursor display position in Input component when scrolling horizontally through long text
2694
- - Fixed Kitty keyboard protocol disable sequence to use safe write method, preventing potential output buffering issues
2695
- - Fixed unnecessary full-screen redraws when changes occur in out-of-view components (e.g., spinners), reducing terminal scroll events and improving performance on slower connections
2696
- - Fixed scrollback clearing behavior to only clear screen instead of scrollback when resizing or shrinking content, preventing loss of terminal history
2697
- - Fixed `.git` directory appearing in autocomplete suggestions when filtering by prefix
2698
- - Fixed cursor position corruption in Input component when displaying text with emoji and combining characters
2699
- - Fixed `.git` directory appearing in autocomplete suggestions
2700
- - Fixed race condition where Kitty key release events could leak to parent shell after TUI exit over slow SSH connections
2701
- - Fixed Editor's word movement (Ctrl+Left/Right) to properly reset sticky column for subsequent vertical navigation
2702
- - Fixed Editor's undo operation to reset sticky column state when restoring cursor position
2703
- - Fixed Editor's right arrow key at end of last line to set sticky column for subsequent up/down navigation
2704
- - Fixed TUI rendering to correctly detect viewport changes and avoid false full-redraws after content shrinks
2705
- - Fixed Kitty protocol key parsing to prefer codepoint over base layout for Latin letters and symbols, fixing keyboard layout issues (e.g., Dvorak)
2706
-
2707
- ## [11.0.0] - 2026-02-05
2708
-
2709
- ### Added
2710
-
2711
- - Introduced `terminal-capabilities.ts` module consolidating terminal detection and image protocol support
2712
- - Added `TerminalInfo` class with methods for detecting image lines and formatting notifications
2713
- - Added `NotifyProtocol` enum supporting Bell, OSC 99, and OSC 9 notification protocols
2714
- - Added `isNotificationSuppressed()` function to check `OMP_NOTIFICATIONS` environment variable
2715
- - Added `TERMINAL` constant providing detected terminal capabilities at runtime
2716
-
2717
- ### Changed
2718
-
2719
- - Changed notification suppression environment variable from `OMP_NOTIFICATIONS` to `PI_NOTIFICATIONS`
2720
- - Changed TUI write log environment variable from `OMP_TUI_WRITE_LOG` to `PI_TUI_WRITE_LOG`
2721
- - Changed hardware cursor environment variable from `OMP_HARDWARE_CURSOR` to `PI_HARDWARE_CURSOR`
2722
- - Updated environment variable access to use `getEnv()` utility function from `@oh-my-pi/pi-utils` for consistent handling
2723
- - Renamed `TERMINAL_INFO` export to `TERMINAL` for clearer API semantics
2724
- - Reorganized terminal image exports from `terminal-image` to `terminal-capabilities` module
2725
- - Updated all internal references to use `TERMINAL` instead of `TERMINAL_INFO`
2726
-
2727
- ### Removed
2728
-
2729
- - Removed `terminal-image` module exports from public API (functionality migrated to `terminal-capabilities`)
2730
-
2731
- ## [10.5.0] - 2026-02-04
2732
-
2733
- ### Fixed
2734
-
2735
- - Treated inline image lines with cursor-move prefixes as image sequences to prevent width overflow crashes
2736
-
2737
- ## [9.8.0] - 2026-02-01
2738
-
2739
- ### Changed
2740
-
2741
- - Moved `wrapTextWithAnsi` export to `@oh-my-pi/pi-natives` package
2742
-
2743
- ### Fixed
2744
-
2745
- - Improved Kitty terminal key sequence parsing to correctly handle text field codepoints in CSI-u sequences
2746
- - Fixed handling of private use Unicode codepoints (U+E000 to U+F8FF) in Kitty key decoding to prevent invalid character interpretation
2747
-
2748
- ## [9.7.0] - 2026-02-01
2749
-
2750
- ### Breaking Changes
2751
-
2752
- - Removed `Key` helper object from public API; use string literals like `"ctrl+c"` instead of `Key.ctrl("c")`
2753
- - Removed `KeyEventType` export from public API
2754
-
2755
- ### Changed
2756
-
2757
- - Migrated key parsing and matching logic to native implementation for improved performance
2758
- - Simplified `isKeyRelease()` and `isKeyRepeat()` to use regex pattern matching instead of string inclusion checks
2759
-
2760
- Older entries are archived in [packages/tui/CHANGELOG.md@e6b8074bbe4b](https://github.com/can1357/oh-my-pi/blob/e6b8074bbe4b0d459aa812948822c2a634c8e62d/packages/tui/CHANGELOG.md).
2671
+ Older entries are archived in [packages/tui/CHANGELOG.md@090e359aa972](https://github.com/can1357/oh-my-pi/blob/090e359aa97267de88bd365054685c8234843cd7/packages/tui/CHANGELOG.md).
@@ -6,6 +6,8 @@
6
6
  */
7
7
  export declare function findLeadingSlashCommandStart(text: string): number | null;
8
8
  export declare function findTrailingSlashCommandStart(text: string): number | null;
9
+ /** Extract the live whitespace-delimited or double-quoted `@` file token ending at the cursor. */
10
+ export declare function extractAtFilePrefix(text: string): string | null;
9
11
  /**
10
12
  * Whether an autocomplete value represents a directory: trailing slash or
11
13
  * backslash, optionally followed by a closing quote for quoted paths.
@@ -1,5 +1,5 @@
1
1
  import type { ImageBudget } from "../components/image.js";
2
- import type { Markdown } from "../components/markdown.js";
2
+ import { type Markdown } from "../components/markdown.js";
3
3
  import { Container } from "../tui.js";
4
4
  export interface FigureMarkdownOptions {
5
5
  /** Markdown for one prose run (and a failed figure's code fallback); FigureMarkdown drives its transience. */
@@ -32,7 +32,12 @@ export declare class FigureMarkdown extends Container {
32
32
  /**
33
33
  * The trimmed prose ahead of the first figure, or "" when the block opens
34
34
  * with one. These are the only rows plain Markdown reproduces byte for
35
- * byte, so they are all a transcript may publish ahead of the figure.
35
+ * byte, so they are all a transcript may publish ahead of the figure. While
36
+ * that prose is still the streaming tail (no figure follows it yet), only
37
+ * its settled prefix ({@link Markdown.getLastRenderStableText}) qualifies:
38
+ * the rest is still being written. Both forms are in
39
+ * {@link streamingMarkdownSource} form, so the settled prefix stays a byte
40
+ * prefix of the whole run once a figure splits it off.
36
41
  */
37
42
  get leadingProse(): string;
38
43
  /** Whether a figure's final source still waits for its raster, or a chart for its pick or raster. */
@@ -8,6 +8,8 @@ export interface ImageOptions {
8
8
  maxWidthCells?: number;
9
9
  maxHeightCells?: number;
10
10
  filename?: string;
11
+ /** File the image was read from; its link target. Images without one link to a blob copy of their bytes. */
12
+ sourcePath?: string;
11
13
  /** Shared budget that caps how many inline images render as live graphics. */
12
14
  budget?: ImageBudget;
13
15
  /**
@@ -2,6 +2,51 @@ import { type TokensList } from "@oh-my-pi/pi-utils/marked";
2
2
  import type { DescribeContext, NativeNode } from "../native/node.js";
3
3
  import type { SymbolTheme } from "../symbols.js";
4
4
  import type { Component } from "../tui.js";
5
+ /**
6
+ * `text` as the streaming renderer lexes it: OSC 8 terminators unified and
7
+ * tabs expanded. {@link Markdown.getLastRenderStableText} is a prefix of this
8
+ * form, not of the raw source, so append-only publishers that mix settled
9
+ * prefixes with whole blocks must compare both in this form. Rendering it is
10
+ * byte-identical to rendering `text`.
11
+ */
12
+ export declare function streamingMarkdownSource(text: string): string;
13
+ /**
14
+ * Streaming `md` source for a native terminal, minus a still-growing table row:
15
+ * the unterminated last line when a GFM table (a run of pipe lines holding a
16
+ * header and its delimiter row, outer pipes optional) sits right above it,
17
+ * outside any open code fence. GFM continues a table on any non-blank line, so
18
+ * that line is a row whatever it holds so far. The terminal sizes columns by
19
+ * every row it gets, so a half-streamed row (an unclosed link shows its whole
20
+ * URL) would rewrap the table each frame, and TSP cannot ask it to skip one.
21
+ * The result keeps the newline before the held line, so it is a prefix of
22
+ * `text` and of the result for every append to it: streaming stays
23
+ * append-only. Pass untrimmed source: trimming first drops the newline that
24
+ * marks the last row finished, so that row would be held too.
25
+ */
26
+ export declare function holdPartialTableRow(text: string): string;
27
+ /**
28
+ * `text`, one inline run whose end is still streaming, with a trailing open
29
+ * link or math span closed the way the finished text most likely closes it,
30
+ * or `undefined` when nothing at its end is open. A link gains its missing
31
+ * `)` (one per open parenthesis in its destination), so it renders as the
32
+ * link it becomes instead of literal brackets and URL. A math body is cut back
33
+ * to its last valid form (a half-typed command, dangling `^`/`_`, or the first
34
+ * `$` of a `$$` closer dropped, open braces closed) and closed, so the
35
+ * rendered math grows in place instead of raw LaTeX turning into symbols at
36
+ * the closer. A `$` body needs a LaTeX character, so a price (`$5 per`) is
37
+ * never speculated. Only prose is: escaped openers and anything inside a code
38
+ * span, closed or still being written, stay literal.
39
+ */
40
+ export declare function speculativeInlineClose(text: string): string | undefined;
41
+ /**
42
+ * Streaming `md` source for a native terminal with the open link or math span
43
+ * at its end speculatively closed ({@link speculativeInlineClose}), as the TUI
44
+ * renders it. Only the last block is read (a span cannot cross a blank line),
45
+ * and source inside an open code fence is left as it is. Each frame rewrites
46
+ * only the guessed closers at the end, which the reconciler sends as a small
47
+ * `splice`.
48
+ */
49
+ export declare function speculativeStreamingSource(text: string): string;
5
50
  /** @internal exported for tests — the grown-line block-kind gate. */
6
51
  export declare function fastLineStartHazard(grownLine: string): boolean;
7
52
  /**
@@ -12,6 +12,8 @@ import type { Component } from "../tui.js";
12
12
  export declare class Text implements Component {
13
13
  #private;
14
14
  setIgnoreTight(ignore: boolean): this;
15
+ /** Override row wrapping when the source carries structural prefixes. */
16
+ setWrapLine(wrapLine: (line: string, width: number) => string[]): this;
15
17
  constructor(text?: string, paddingX?: number, paddingY?: number, customBgFn?: (text: string) => string);
16
18
  /** Return bounded text and layout state for debug inspection. */
17
19
  debugState(): Record<string, unknown>;
@@ -9,6 +9,8 @@ import type { KeyId } from "./app-keybindings.js";
9
9
  export declare function matchesAppInterrupt(data: string): boolean;
10
10
  /** Match `app.tools.expand`, falling back to Ctrl+O when the action is unbound. */
11
11
  export declare function matchesAppToolsExpand(data: string): boolean;
12
+ /** Effective tools-expand key: the first bound key, or Ctrl+O when unbound. */
13
+ export declare function appToolsExpandKey(): KeyId;
12
14
  /** Match the generic selector cancel keybinding. */
13
15
  export declare function matchesSelectCancel(data: string): boolean;
14
16
  /** Match the generic selector up-navigation keybinding. */
@@ -22,6 +22,7 @@ export declare const tmuxMultiplexer: {
22
22
  readonly passthrough: typeof wrapTmuxPassthrough;
23
23
  readonly settleMs: 100;
24
24
  };
25
+ readonly gatesBracketedPaste: true;
25
26
  readonly expandsRepPadding: true;
26
27
  readonly expiresSynchronizedOutput: true;
27
28
  readonly altRestoreEndsSynchronizedOutput: true;
@@ -73,6 +73,12 @@ export interface TerminalMultiplexerModule<Id extends string = string> {
73
73
  * session, and an unrecognized report does not turn it off.
74
74
  */
75
75
  readonly honorsSynchronizedOutput?: boolean;
76
+ /**
77
+ * Tracks DEC 2004 per pane and forwards paste markers only while it is set,
78
+ * yet may leave DECRQM 2004 unanswered, so bracketed paste is reasserted
79
+ * without waiting for a DECRPM confirmation.
80
+ */
81
+ readonly gatesBracketedPaste?: boolean;
76
82
  /**
77
83
  * The session does not reveal whether its client renders graphics while
78
84
  * outer-terminal identity variables leak in, so images and Kitty
@@ -1,12 +1,3 @@
1
- /**
2
- * Content-addressed binary blobs (images) for `image` nodes.
3
- *
4
- * `describe()` registers the bytes and puts the returned id in `image.p.blob`;
5
- * the native backend delivers each referenced blob once per terminal
6
- * connection (asking the terminal first which it holds, or through its blob
7
- * cache), else uploads it with verb `b` before the frame that first
8
- * references it.
9
- */
10
1
  import type { TspProps } from "@oh-my-pi/pi-wire";
11
2
  import type { NativeChild, NativeNode } from "./node.js";
12
3
  export interface NativeBlob {
@@ -26,10 +17,19 @@ export declare function nativeImageNode(bytes: Uint8Array, mime: string, p?: Omi
26
17
  * so callers cache the node per payload ({@link NativeImageCache}).
27
18
  */
28
19
  export declare function base64ImageNode(data: string, mimeType: string, p?: Omit<TspProps<"image">, "blob" | "builtin" | "w" | "h">, key?: string): NativeNode;
29
- /** `image` nodes for base64 payloads, one per slot key, rebuilt only when the slot's payload changes. */
20
+ /**
21
+ * Props that make an `image` node open the file at `filePath` (as a `file://` URL, the TSP
22
+ * open-target form): a click zooms the image in the terminal, and the file opens from its
23
+ * context menu. Empty without a file.
24
+ */
25
+ export declare function openableImageProps(filePath: string | undefined): Pick<TspProps<"image">, "href" | "actions">;
26
+ /**
27
+ * `image` nodes for base64 payloads, one per slot key, rebuilt only when the slot's payload
28
+ * or the link policy changes. Each opens its file (`sourcePath`, else a blob copy).
29
+ */
30
30
  export declare class NativeImageCache {
31
31
  #private;
32
- get(key: string, data: string, mimeType: string): NativeNode;
32
+ get(key: string, data: string, mimeType: string, sourcePath?: string): NativeNode;
33
33
  }
34
34
  /** The disk file a tool result's image was read from (`read` details `mediaPath`), when it has one. */
35
35
  export declare function readMediaPath(details: unknown): string | undefined;
@@ -5,7 +5,11 @@
5
5
  * so an Enter typed for the composer as the prompt appears never approves a
6
6
  * call, and the chord itself only arms {@link APPROVE_ARM_MS} after mount: it
7
7
  * is also the follow-up key, pressed mid-run exactly when prompts appear.
8
- * Esc denies.
8
+ * Esc denies. With an {@link ApprovalDialogDraft}, every other key edits the
9
+ * composer mounted beneath the dialog, so a message can steer the turn while
10
+ * the call waits; an empty composer ignores Enter and arrows scroll the details.
11
+ * The prompt stays a few rows so the composer keeps its room; with a full
12
+ * prompt, the expand key (`app.tools.expand`) swaps it in, wrapped.
9
13
  */
10
14
  import { type Component, type TUI } from "../index.js";
11
15
  import type { NativeNode, NativeUiEvent } from "../native/node.js";
@@ -15,10 +19,16 @@ interface ApprovalDialogCallbacks {
15
19
  onApprove(): void;
16
20
  onDeny(): void;
17
21
  }
22
+ /** The composer mounted beneath the dialog: it receives every key the dialog does not own. */
23
+ export interface ApprovalDialogDraft {
24
+ /** True while the composer holds a draft — text, attachments, or a paste still landing: keys then edit and send it. */
25
+ hasDraft(): boolean;
26
+ handleInput(keyData: string): void;
27
+ }
18
28
  interface ApprovalDialogOptions {
19
29
  tui?: TUI;
20
- /** Wrap long details instead of clipping them to a row: set when the dialog is the only place they show. */
21
- wrapDetails?: boolean;
30
+ /** The prompt with every detail in full, which the expand key swaps in (wrapped) for the summarized one. */
31
+ fullPrompt?: string;
22
32
  /** Deny once this many ms pass without input (any key restarts the countdown). */
23
33
  timeout?: number;
24
34
  /** Called when {@link timeout} expires, before the dialog denies. */
@@ -27,6 +37,8 @@ interface ApprovalDialogOptions {
27
37
  onTimeoutStart?: () => void;
28
38
  /** Called each time a key restarts the countdown. */
29
39
  onTimeoutReset?: () => void;
40
+ /** Route typing to the composer instead of dropping it. */
41
+ draft?: ApprovalDialogDraft;
30
42
  }
31
43
  export declare class ApprovalDialogComponent implements Component {
32
44
  #private;
@@ -37,15 +49,21 @@ export declare class ApprovalDialogComponent implements Component {
37
49
  invalidate(): void;
38
50
  dispose(): void;
39
51
  handleInput(keyData: string): void;
52
+ /**
53
+ * Swap the summarized prompt for every detail, wrapped, or back, as the expand key does here;
54
+ * for hosts that intercept that key first. Restarts the countdown like any key. False (nothing
55
+ * changes) without a full prompt, so the host's own expand handling (tool previews) can run.
56
+ */
57
+ toggleDetails(): boolean;
40
58
  render(width: number): readonly string[];
41
59
  /**
42
60
  * In the dock, in the composer's place and framed as the composer is (an
43
61
  * `omp.editor.*` root role), like the ask dialog, rather than a card over
44
- * the transcript: the title, the details, then Deny and the primary
45
- * Approve, whose keys show in their tooltips.
62
+ * the transcript: the title, the details, then Details (with a full prompt),
63
+ * Deny, and the primary Approve, whose keys show in their tooltips.
46
64
  */
47
65
  describe(): NativeNode;
48
- /** Deny and Approve run exactly what Esc and the approve key run. */
66
+ /** Deny, Approve, and Details run exactly what Esc, the approve key, and the expand key run. */
49
67
  handleNativeEvent(event: NativeUiEvent): void;
50
68
  }
51
69
  export {};
@@ -0,0 +1,50 @@
1
+ import type { Theme } from "../theme/theme.js";
2
+ /** What the tree reads off a job. */
3
+ export interface JobTreeJob {
4
+ readonly id: string;
5
+ readonly status: "running" | "completed" | "failed" | "cancelled";
6
+ readonly startTime: number;
7
+ /** Registry id of the agent that owns the job; absent or the root id: a top-level job. */
8
+ readonly ownerId?: string;
9
+ /** Registry id of the subagent a task job runs: the job heads that agent's subtree. */
10
+ readonly agentId?: string;
11
+ }
12
+ /** A subagent in the tree, by registry id: an owner of listed jobs, or an ancestor linking one to the root. */
13
+ export interface JobTreeAgent {
14
+ readonly id: string;
15
+ readonly parentId?: string;
16
+ }
17
+ /** Where a row sits: its depth and the rails drawn left of it. */
18
+ export interface JobTreePlace {
19
+ /** 0 for the top level. */
20
+ readonly depth: number;
21
+ /** Per enclosing level below the top, whether that level's rail continues past this row. */
22
+ readonly rails: readonly boolean[];
23
+ /** Whether this row is the last of its siblings. */
24
+ readonly last: boolean;
25
+ /** Whether rows nest under this one. */
26
+ readonly parent: boolean;
27
+ }
28
+ /** One tree row: a job (a task job also heads its subagent), or a header for a subagent with no listed task job. */
29
+ export type JobTreeRow<J extends JobTreeJob> = (JobTreePlace & {
30
+ readonly kind: "job";
31
+ readonly job: J;
32
+ readonly agent?: JobTreeAgent;
33
+ }) | (JobTreePlace & {
34
+ readonly kind: "agent";
35
+ readonly agent: JobTreeAgent;
36
+ });
37
+ /**
38
+ * `jobs` as tree rows in display order. `rootId` is the viewing agent: its
39
+ * jobs, ownerless jobs and jobs of owners outside `agents` sit at the top
40
+ * level, as do agents whose parent is the root or unknown.
41
+ */
42
+ export declare function buildJobTree<J extends JobTreeJob>(jobs: readonly J[], agents: readonly JobTreeAgent[], rootId: string | undefined): JobTreeRow<J>[];
43
+ /** The connector drawn before a row's head: nothing at the top level, else rails then a branch. */
44
+ export declare function jobTreeGuide(row: JobTreePlace, theme: Theme): string;
45
+ /**
46
+ * The prefix of lines under a row's head (its label, a wrapped command): the
47
+ * rails that continue past it, then the rail down to its children or blank,
48
+ * two columns wide so a top-level leaf's lines indent by two.
49
+ */
50
+ export declare function jobTreeIndent(row: JobTreePlace, theme: Theme): string;
@@ -1,5 +1,6 @@
1
1
  import type { Component } from "../tui.js";
2
2
  import type { DescribeContext, NativeNode, NativeUiEvent } from "../native/node.js";
3
+ import { type JobTreeAgent } from "./job-tree.js";
3
4
  /** One async job as the session snapshots it. */
4
5
  export interface JobsPanelJob {
5
6
  readonly id: string;
@@ -8,13 +9,19 @@ export interface JobsPanelJob {
8
9
  readonly label: string;
9
10
  readonly startTime: number;
10
11
  readonly endTime?: number;
12
+ /** Registry id of the agent that owns the job. */
13
+ readonly ownerId?: string;
11
14
  /** Registry id of the subagent a task job runs. */
12
15
  readonly agentId?: string;
13
16
  }
14
- /** Running and recently settled jobs. */
17
+ /** Running and recently settled jobs of an agent and its subagents. */
15
18
  export interface JobsPanelSnapshot {
16
19
  readonly running: readonly JobsPanelJob[];
17
20
  readonly recent: readonly JobsPanelJob[];
21
+ /** The agent the sheet is for: its jobs sit at the top level, and only they can be cancelled. */
22
+ readonly rootId?: string;
23
+ /** Subagents owning listed jobs, with the ancestors linking them to the root. */
24
+ readonly agents?: readonly JobTreeAgent[];
18
25
  }
19
26
  /** What the sheet shows about the selected job beyond its row. */
20
27
  export interface JobsPanelDetail {
@@ -35,20 +42,26 @@ export interface JobsSheetSource {
35
42
  load(): JobsPanelSnapshot;
36
43
  /** The selected job's detail; runs once per frame, undefined once the job is gone. */
37
44
  inspect(id: string): JobsPanelDetail | undefined;
38
- /** Cancel a running job. */
39
- cancel(id: string): void;
45
+ /**
46
+ * Cancel a running job. Omitted by a read-only sheet (a focused subagent
47
+ * view, which only watches that agent): it lists the jobs and offers no way
48
+ * to stop them.
49
+ */
50
+ cancel?(id: string): void;
40
51
  /** Dismiss the sheet. */
41
52
  close(): void;
42
53
  }
43
54
  /**
44
55
  * The jobs pill's sheet: a centred `lg` glass sheet titled Background jobs.
45
- * A master–detail split (it stacks in a narrow pane): the selectable job list,
46
- * grouped Running / Recent, beside the selected job's detail — a status pill
47
- * with live elapsed and Cancel job, its command or task, facts (agent, cwd,
48
- * live pids, exit code, artifact) and a tail-following output pane. With no
49
- * jobs it shows an empty state. It re-reads the session every frame, so the
50
- * caller re-renders it on a timer while it is open. ↑/↓ select, X cancels the
51
- * selected running job, Esc or Close dismisses; nothing enters the transcript.
56
+ * A master–detail split (it stacks in a narrow pane): the selectable job tree
57
+ * beside the selected job's detail — a status pill with live elapsed and
58
+ * Cancel job, its command or task, facts (agent, cwd, live pids, exit code,
59
+ * artifact) and a tail-following output pane. With no jobs it shows an empty
60
+ * state. It re-reads the session every frame, so the caller re-renders it on
61
+ * a timer while it is open. ↑/↓ select, X cancels the selected running job,
62
+ * Esc or Close dismisses; nothing enters the transcript. A source without
63
+ * `cancel` renders read-only, and a subagent's job never offers Cancel: only
64
+ * the root's own jobs can be stopped here.
52
65
  */
53
66
  export declare class JobsSheet implements Component {
54
67
  #private;
@@ -60,7 +73,7 @@ export declare class JobsSheet implements Component {
60
73
  };
61
74
  constructor(source: JobsSheetSource);
62
75
  describe(_cx: DescribeContext): NativeNode;
63
- /** Rows for a terminal without the native surface: one per job, then the selected job's facts and output tail. */
76
+ /** Rows for a terminal without the native surface: the job tree, then the selected job's facts and output tail. */
64
77
  render(width: number): readonly string[];
65
78
  invalidate(): void;
66
79
  handleInput(data: string): void;
@@ -24,6 +24,8 @@ export interface ComposerNativeState {
24
24
  readonly rate?: number;
25
25
  /** A turn is running: the send keycap becomes a Stop button. */
26
26
  readonly running: boolean;
27
+ /** Native dictation control; undefined hides it for hosts without speech-to-text. */
28
+ readonly dictation?: "idle" | "starting" | "recording" | "transcribing";
27
29
  /** The viewed subagent's lineage, outermost first and the viewed agent last; undefined on the main session. */
28
30
  readonly viewing?: readonly string[];
29
31
  /** The session's title, quoted in italics as the TSP placeholder; undefined until it has one. */
@@ -220,7 +222,8 @@ export declare class CustomEditor extends Editor {
220
222
  * once after construction (and again after `setEditorComponent` swaps the
221
223
  * editor). Passing `undefined` clears any pending frame. */
222
224
  setShimmerRepaintHandler(handler: (() => void) | undefined): void;
223
- /** Editing is available during bootstrap; atomic sends wait until submission is wired and enabled. */
225
+ /** Editing is available during bootstrap; atomic sends wait until submission is wired and enabled
226
+ * and no dictation is still writing into the draft. */
224
227
  protected get nativeSendable(): boolean;
225
228
  /** Viewing a subagent, the draft goes to it: the placeholder names it; else the session's title, quoted in italics. */
226
229
  describePlaceholder: () => TspText;
@@ -238,17 +241,17 @@ export declare class CustomEditor extends Editor {
238
241
  * subagent is focused, the attachment chips, a `line` row of the
239
242
  * shell-mode chip and the input, and the `bar`: model chip, effort chip
240
243
  * (or the effort glyph as the model chip's icon), the generation rate,
241
- * the other status facts, usage, then send (Stop while a turn runs).
242
- * Clicks come back as `status.model`, `thinking.cycle`, `submit`,
243
- * `interrupt` and `focus:<id>` actions.
244
+ * the other status facts, usage, dictation, then send (Stop while a turn runs).
245
+ * Clicks come back as `status.model`, `thinking.cycle`, `dictation.toggle`,
246
+ * `submit`, `interrupt` and `focus:<id>` actions.
244
247
  */
245
248
  describeLayout: (input: NativeNode, cx: DescribeContext) => NativeEditorLayout;
246
249
  /**
247
- * Clicks on the composer's controls take the same paths as their keys: ⇧⇥,
248
- * ⏎ and Esc; the viewing header's links (`focus:<id>`) go to the host,
249
- * the status facts' clicks (`status.*`) to their source. Selection edits
250
- * go to the buffer; `send` submits its own prompt after saving the old draft
251
- * for recall and waiting for in-flight clipboard work.
250
+ * Dictation clicks go directly to the host, independent of keybindings.
251
+ * Other controls take the same paths as their keys; the viewing header's
252
+ * links (`focus:<id>`) go to the host, the status facts' clicks to their source.
253
+ * Selection edits go to the buffer; `send` submits its own prompt after
254
+ * saving the old draft for recall and waiting for in-flight clipboard work.
252
255
  */
253
256
  handleNativeEvent(event: NativeUiEvent): void;
254
257
  onEscape?: () => void;
@@ -275,6 +278,8 @@ export declare class CustomEditor extends Editor {
275
278
  onRetry?: () => void;
276
279
  /** Called when Caps Lock is pressed. */
277
280
  onCapsLock?: () => void;
281
+ /** Called directly by the native microphone/stop control, without synthesizing a keypress. */
282
+ onToggleDictation?: () => void;
278
283
  /** Called when left-arrow is pressed while the editor is empty (cursor necessarily at start). */
279
284
  onLeftAtStart?: () => void;
280
285
  /** Called when the viewing header asks to view agent `id` ({@link MAIN_AGENT_ID}: the main session). */
@@ -296,6 +301,8 @@ export declare class CustomEditor extends Editor {
296
301
  * Clear all custom key handlers.
297
302
  */
298
303
  clearCustomKeyHandlers(): void;
304
+ /** True while a clipboard-image or image-path paste is still landing; later keys queue behind it. */
305
+ get pasteInFlight(): boolean;
299
306
  capturesInput(data: string): boolean;
300
307
  handleInput(data: string): void;
301
308
  /**
@@ -305,7 +312,8 @@ export declare class CustomEditor extends Editor {
305
312
  * an Ask dialog opened over a non-empty prompt — so finishing or submitting
306
313
  * the draft cannot fire an editor-slot shortcut that clears
307
314
  * `editorContainer` and orphans the overlay. Only text editing, cursor
308
- * movement, submission, and the clear action reach the buffer.
315
+ * movement, submission, the clear action, and pastes (clipboard images and
316
+ * image paths attach as on the composer) reach the buffer.
309
317
  */
310
318
  handleDraftEdit(data: string): void;
311
319
  }