import { CustomEditor } from '@earendil-works/pi-coding-agent'; import { type EditorOptions, type EditorTheme, type TUI } from '@earendil-works/pi-tui'; /** Keep inline command disclosure compact and bounded before wrapping; the * inspector remains the unbounded, exact-prompt surface. */ export declare function compactLoadedCommandPreview(content: string, width: number): string[]; /** Thinking levels pi cycles through (shift+tab), lowest → highest budget. */ export type ThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max'; /** Build a title-chip styler for a `/color` token (a tmux colour name, `#rrggbb`, * or `colourN`): the colour as the chip BACKGROUND with contrast-picked bold * text, matching the thinking-chip treatment. `undefined` for an unrenderable * token (e.g. `default`) — the caller keeps its fallback style. */ export declare function colorChipStyle(token: string): ((s: string) => string) | undefined; /** The default (thinking `off`) title chip: reverse video + a space of padding * each side, so the name reads as a label sitting on the border rule. Used as * the fallback when no thinking color applies. */ export declare const defaultTitleStyle: (s: string) => string; /** Resolve the editor border colorizer for a thinking level. Unknown / `off` → * the supplied `fallback` (the theme's default border color). */ export declare function thinkingBorderColor(level: string | undefined, fallback: (s: string) => string): (s: string) => string; /** Resolve the title-chip styler for a thinking level: the level's color as the * chip BACKGROUND with bold white text (a space of padding each side), so the * session name reads as a solid label in the same hue as the border. Unknown / * `off` → the supplied `fallback` (the reverse-video default chip). */ export declare function thinkingTitleStyle(level: string | undefined, fallback: (s: string) => string): (s: string) => string; /** Compose the replacement top-border line: solid title chip + border rule + * info chip, never wider than `width` (pi-tui hard-crashes on an over-wide * line). The info chip yields entirely when the chip leaves it almost no room, * and truncates when it only partially fits. Exported pure for the overflow * regression test. */ export declare function composeTopBorder(width: number, title: string, info: string, titleStyle: (s: string) => string, borderColor: (s: string) => string): string; export declare function outlineCursorLine(line: string): string | undefined; /** Recognize a single input chunk as a filesystem path to an image file — how a * terminal delivers a file DRAGGED into the pane (the escaped path pasted in * bulk, optionally wrapped in bracketed-paste markers). Returns the resolved * path, or null when the chunk isn't a lone image path (normal typing, multi- * line text, a non-image path, or a path that doesn't point at a real file). * Conservative on purpose: it must never transform ordinary typed input. */ export declare function detectDroppedImagePath(data: string): string | null; /** One projected layout entry — a faithful port of pi-tui's private * `Editor.prototype.layoutText`, extended with the logical line each entry * came from (the base algorithm processes one editor at a time and never * needs to report this, since it already owns `this.state.lines`). */ export interface RefLayoutChunk { logicalLine: number; startIndex: number; endIndex: number; text: string; hasCursor: boolean; cursorPos: number; } /** Recompute the exact layout pi-tui's `Editor.render` will produce for * `lines` at `layoutWidth`, faithfully porting the private `layoutText` * algorithm (empty-editor special case; per-line whole-fit vs `wordWrapLine` * branch; cursor-in-chunk assignment for wrapped lines). `wordWrapLine` here * is called WITHOUT the base editor's private paste-marker-aware * presegmentation, so a wrapped line containing a paste marker can * theoretically diverge from the real render's chunk boundaries — harmless, * because `decorateRow`'s verify-before-splice check means any divergence * simply drops that row's decoration rather than corrupting it. */ export declare function buildRefLayoutChunks(lines: readonly string[], layoutWidth: number, cursorLine: number, cursorCol: number): RefLayoutChunk[]; /** Splice underline/dim styling around every resolved-ref span that survives * chrome-safety verification. `rowStr` is one already-rendered content row * (plain, or carrying exactly the cursor's own cell markup); `chunk` is the * layout entry it was built from; `spans` are candidate resolved-ref spans * already filtered to lie within `chunk`'s logical range; `line` is the full * logical line text (used only to verify a span's characters still match at * the row offset we computed — never spliced in directly). Exported pure for * direct testing. */ export declare function decorateRow(rowStr: string, chunk: RefLayoutChunk, spans: ReadonlyArray<{ start: number; end: number; }>, line: string, paddingX: number): string; export declare class TitledEditor extends CustomEditor { /** crtr's OWN keybindings manager — the same one CustomEditor matches `app.*` * against. We keep a reference so `handleInput` can resolve the newline chord * here, independent of the editor's (possibly separate) pi-tui module global. */ private readonly km; /** The raw (untrimmed) text `submitValue()` is ABOUT to trim away, captured by * the `submitValue` monkeypatch just below it runs, and consumed exactly once * by the `onSubmit` accessor wrapper. See the constructor for why. */ private pendingRawSubmitText; /** The REAL caller-supplied `onSubmit` handler (what `InputController` wires * via `this.editor.onSubmit = ...`) — stored here because `onSubmit` itself is * converted to an accessor below whose getter always returns our substitution * wrapper, not this value directly. */ private realOnSubmit; constructor(tui: TUI, theme: EditorTheme, keybindings: ConstructorParameters[2], options?: EditorOptions); /** Insert a newline on the newLine chord (Alt+Enter / Shift+Enter) using crtr's * OWN keybindings manager, then defer everything else to the stock editor. * * The base `Editor` resolves newLine against `getKeybindings()` on ITS pi-tui * instance, which in a non-deduped install is a DIFFERENT module than the one * carrying crtr's `alt+enter` override — so the override can silently never * reach it and Alt+Enter falls through to submit (issue #11). crtr's `km` (the * same manager CustomEditor matches `app.*` against) always carries the * override, so matching the chord here makes the newline behavior independent * of cross-instance keybinding mirroring. Skipped while the autocomplete popup * is open so Enter-to-confirm keeps working. */ /** Set by the input controller: called when a bulk input chunk is recognized * as a filesystem path to an image file (a file DRAGGED into the pane, which * the terminal delivers as bracketed-paste TEXT, not an image event). Returns * true when it consumed the drop, so the raw path is never inserted as text. */ onPasteImagePath?: (path: string) => boolean; /** Width of an `[Image #N]` token sitting immediately before the cursor on the * current line, or 0 when the cursor isn't at the end of such a token — used to * delete/skip the token atomically. The token's separating space is a normal, * reachable character, deliberately NOT part of the atom. */ private imageTokenWidthBeforeCursor; /** Width of an `[Image #N]` token sitting immediately after the cursor on the * current line, or 0 when the cursor isn't at the start of such a token — used * to skip the token with the right arrow. */ private imageTokenWidthAfterCursor; handleInput(data: string): void; /** The base editor auto-triggers autocomplete for a LEADING slash context * (`isInSlashCommandContext`, `insertCharacter`) whenever the text before * the cursor — from the very start of the line — begins with `/`. A * non-leading ref elsewhere in ordinary text (`hey check /dev`) never * satisfies that check, so it would otherwise require an explicit Tab. * This bridges exactly that gap: after an ordinary single-character * insertion, if the caret now sits in a non-leading ref token * (`findRefCompletionContext` — T1's shared caret classifier, not * reimplemented here), nudge the same private trigger the base editor uses * for its own leading case. Detected purely by before/after cursor and * text-length diffing, so it's mechanism-agnostic (typed key or decoded * printable key alike) and naturally excludes multi-character paste * (`handlePaste` already suppresses autocomplete itself, and the exact +1 * length-delta check here would reject it anyway). Leading context is * deliberately left untouched — triggering it again here would double the * base editor's own native trigger for that case. */ private maybeTriggerNonLeadingRefAutocomplete; /** Called after native ↓ navigation proves the editor and prompt history are * both exhausted. Attach uses this boundary handoff to enter its node roster. */ onCursorDownAtBottom?: () => void; /** Fully-expanded prompt template currently loaded by the editor's leading * slash token. It renders inside the editor outline before submission, so the * user can inspect the exact payload while arguments are still editable. */ loadedCommand: { name: string; content: string; inspectHint: string; } | undefined; /** Session-name chip painted into the LEFT of the top border. Empty → plain. */ title: string; /** Pre-styled context string painted into the RIGHT of the top border (cwd / * branch / git status). Already colorized by the caller; empty → omitted. */ info: string; /** Editor-mode badge painted into the bottom-right of the outline. Empty → omitted. */ mode: string; /** Node and workspace badge painted at the far bottom-right of the outline. */ contextBadge: string; /** Paint the chip solid so the name reads as a label sitting on the border * rule. Defaults to the reverse-video chip; viewer.ts swaps in a * thinking-colored background (bold white text) on each state update. */ titleStyle: (s: string) => string; /** Editor-mode badge colorizer, matched to the current mode type. */ modeStyle: (s: string) => string; /** Node and workspace badge colorizer, matched to the editor frame. */ contextBadgeStyle: (s: string) => string; /** Whether the hosting tmux pane / terminal window has FOCUS (terminal focus * reporting, DECSET 1004 — viewer.ts flips this on `\x1b[I` / `\x1b[O`). * Distinct from TUI component focus: the editor keeps component focus while * its pane is blurred. False → the fake cursor renders as an outline. */ paneFocused: boolean; /** Canonical names the broker currently resolves — the exact-membership set * a candidate ref token must be in to render as a resolved highlight. * Empty by default (no decoration) until the caller (T3, out of scope for * this wave) supplies the broker-supplied inventory. */ private resolvedRefNames; /** Command names the VIEWER dispatches ENTIRELY locally (never reach the * engine as prose) — checked against the TRIMMED leading-name rule * (`extractLeadingCommandName`, which mirrors `slash/dispatch.ts`'s own * `text.trim()`-based dispatch parse). Exactly * `locallyConsumedCommandNames()` from `slash/dispatch.ts` — independent * of `remote` and of the broker's `get_commands` reply (see that * function's doc comment). Deliberately NEVER the merged display/ * completion list (review Major 6 — `slashCommandList` is a different, * remote-filtered, broker-augmented list, not a dispatch predicate). */ private localCommandNames; /** Leading tokens the ENGINE (broker) independently consumes as a leading * command/skill/template dispatch — the broker's own * `engineLeadingCommandNames` (design: inline memory references, review * Major 6), checked against the UNTRIMMED leading-name rule * (`matchesEngineLeadingCommand`, mirroring the broker's own * `isLeadingEngineCommand` — review Minor 10). Populated from each * `list_memory_refs` reply, alongside `resolvedRefNames`. */ private engineCommandNames; setResolvedRefNames(names: Iterable): void; setLocalCommandNames(names: Iterable): void; setEngineCommandNames(names: Iterable): void; /** Render-only decoration pass: underlines every resolved-ref span visible * in the already-rendered content rows, using a from-scratch layout * recompute (`buildRefLayoutChunks`) that mirrors the base editor's own * private `layoutText`/`scrollOffset`/`maxVisibleLines` math so row/column * offsets line up. Never mutates stored text; a span whose recomputed * offset doesn't verify against the real rendered row (see `decorateRow`) * is simply left undecorated. Suppressed entirely (command-wide) whenever * the composer's own leading token dispatches — as a LOCAL viewer command * (`localCommandNames`, TRIMMED rule) or as an ENGINE-consumed leading * command/skill/template (`engineCommandNames`, UNTRIMMED rule, review * Minor 10) — the ref grammar and slash-dispatch's name charset differ, so * highlighting a ref inside a command that's about to dispatch would be * misleading chrome for text the command, not memory resolution, is about * to consume. The two rules are kept deliberately SEPARATE (review Major * 6) rather than collapsed into one set/predicate: they classify against * different text (trimmed vs. raw) and different authorities (this * viewer's own dispatch vs. the broker's). */ private decorateResolvedRefs; render(width: number): string[]; }