import { CustomEditor, type ExtensionAPI } from "@earendil-works/pi-coding-agent"; import { Key, matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui"; class AdaptiveMultilineEditor extends CustomEditor { handleInput(data: string): void { if (matchesKey(data, Key.ctrl("u"))) { const text = this.getText(); if (text.length > 0) { // killRing is a private Editor field, not public API; stock ctrl+y/alt+y read from // this same instance, so pushing here makes them paste the whole cut buffer. (this as unknown as { killRing: { push(text: string, opts: { prepend: boolean; accumulate?: boolean }): void } }).killRing.push(text, { prepend: true, accumulate: false }); this.setText(""); } return; } const explicitSubmit = matchesKey(data, Key.super("enter")) || matchesKey(data, Key.ctrl("enter")); if (explicitSubmit) { if (this.isShowingAutocomplete()) super.handleInput("\x1b"); super.handleInput("\r"); return; } if (matchesKey(data, Key.enter) && this.getText().includes("\n") && !this.isShowingAutocomplete()) { super.handleInput("\n"); return; } super.handleInput(data); } render(width: number): string[] { const lines = super.render(width); if (!this.getText().includes("\n") || lines.length === 0) return lines; const label = " MULTILINE · ⌘↵ send "; const last = lines.length - 1; if (visibleWidth(lines[last]!) >= label.length) { lines[last] = truncateToWidth(lines[last]!, width - label.length, "") + label; } return lines; } } export default function (pi: ExtensionAPI) { pi.on("session_start", (_event, ctx) => { if (ctx.mode !== "tui") return; ctx.ui.setEditorComponent((tui, theme, keybindings) => new AdaptiveMultilineEditor(tui, theme, keybindings)); }); }