@shanepadgett/tau-tui 0.42.3 → 0.43.0

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shanepadgett/tau-tui",
3
- "version": "0.42.3",
3
+ "version": "0.43.0",
4
4
  "description": "Shared TUI components and helpers for Tau Agent",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { editorTheme } from "./editor-theme.ts";
2
+ export { pushSavedNote, renderInlineEditor, renderNoteEditor, wrapWithPrefix } from "./inline-editor-row.ts";
2
3
  export { bindingHint, bindingsHint, rawHint, type ToolKeyHint } from "./key-hints.ts";
3
4
  export { Marker, type MarkerState } from "./marker.ts";
4
5
  export { ScrollableMarkdown } from "./scrollable-markdown.ts";
@@ -0,0 +1,56 @@
1
+ import type { Theme } from "@earendil-works/pi-coding-agent";
2
+ import { type Editor, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
3
+
4
+ export function renderInlineEditor(prefix: string, editor: Editor, width: number): string[] {
5
+ const available = Math.max(1, width - visibleWidth(prefix));
6
+ const lines = editorContentLines(editor.render(available));
7
+ const contentLines = lines.length ? lines : [""];
8
+ const continuation = " ".repeat(visibleWidth(prefix));
9
+ return contentLines.map((line, index) =>
10
+ truncateToWidth(`${index === 0 ? prefix : continuation}${line}`, width, ""),
11
+ );
12
+ }
13
+
14
+ export function renderNoteEditor(lines: string[], editor: Editor, width: number, theme: Theme, indent: string): void {
15
+ const prefix = `${indent}${theme.fg("muted", "└─ ")}`;
16
+ const available = Math.max(1, width - visibleWidth(prefix));
17
+ const editorLines = editorContentLines(editor.render(available));
18
+ const contentLines = editorLines.length ? editorLines : [""];
19
+ lines.push(truncateToWidth(`${prefix}${contentLines[0] ?? ""}`, width, ""));
20
+ const continuation = " ".repeat(visibleWidth(prefix));
21
+ for (const line of contentLines.slice(1)) lines.push(truncateToWidth(`${continuation}${line}`, width, ""));
22
+ }
23
+
24
+ export function pushSavedNote(lines: string[], note: string, width: number, theme: Theme, indent: string): void {
25
+ for (const line of wrapWithPrefix(`${indent}${theme.fg("muted", "└─ ")}`, theme.fg("muted", note), width)) {
26
+ lines.push(line);
27
+ }
28
+ }
29
+
30
+ export function wrapWithPrefix(prefix: string, text: string, width: number): string[] {
31
+ const restWidth = Math.max(1, width - visibleWidth(prefix));
32
+ return wrapTextWithAnsi(text, restWidth).map(
33
+ (line, index) => `${index === 0 ? prefix : " ".repeat(visibleWidth(prefix))}${line}`,
34
+ );
35
+ }
36
+
37
+ function editorContentLines(lines: string[]): string[] {
38
+ if (lines.length <= 2) return lines;
39
+ return lines.slice(1).filter((line) => !isEditorBorderLine(line));
40
+ }
41
+
42
+ function isEditorBorderLine(line: string): boolean {
43
+ const text = stripAnsi(line).trim();
44
+ return /^[┌┐└┘─]+$/.test(text) || /^─── [↑↓] \d+ more ─*$/.test(text);
45
+ }
46
+
47
+ function stripAnsi(text: string): string {
48
+ let result = text;
49
+ while (true) {
50
+ const start = result.indexOf("\u001b[");
51
+ if (start === -1) return result;
52
+ const end = result.indexOf("m", start + 2);
53
+ if (end === -1) return result;
54
+ result = result.slice(0, start) + result.slice(end + 1);
55
+ }
56
+ }