packages/ai: - feat: expose provider responseId on AssistantMessage - feat: lazy-load provider modules for faster startup - fix: hash foreign Responses API tool call IDs exceeding 64-char limit - fix: ignore null chunks in openai-completions streams - fix: keep image tool results inline for Gemini 3+ and Antigravity - fix: correct Bedrock Claude 4.6 context window to 200k - fix: support prompt caching for Bedrock application inference profiles - fix: add OpenRouter reasoning payload format - fix: ignore placeholder Vertex API keys - fix: skip AJV validation in restricted runtimes - fix: Anthropic OAuth client injection and responseId extraction - fix: Codex incomplete/failed response status handling packages/agent: - fix: defer steering until after tool execution completes packages/tui: - feat: namespaced keybinding IDs with KeybindingsManager conflict detection - feat: configurable select list column sizing (#2154 by @markusylisiurunen) - fix: stream truncateToWidth for large strings - fix: skip Termux height redraws - fix: stop evicting unrelated default keybindings - fix: resolve raw backspace ambiguity on Windows Terminal - fix: clear stale scrollback on session switch (#2155 by @Perlence) - fix: remove trailing markdown block spacing (#2152 by @markusylisiurunen) packages/coding-agent: - feat: add resizable share sidebar (#2435 by @dmmulroy) - feat: emit OSC 133 command-executed marker - feat: reload custom themes from disk watcher - feat: add --fork session flag - feat: file mutation queue for serialized writes - feat: initial message consolidation utility - fix: keybindings migrated to namespaced IDs - fix: resolve waitForRetry() race when auto-retry produces tool calls - fix: handle slash-delimited /model refs - fix: refresh active model after provider updates - fix: extended transient error patterns for retry
50 lines
1.9 KiB
TypeScript
50 lines
1.9 KiB
TypeScript
import { describe, expect, it } from "bun:test";
|
|
import { Ellipsis, truncateToWidth, visibleWidth } from "@oh-my-pi/pi-tui/utils";
|
|
|
|
describe("truncateToWidth", () => {
|
|
it("keeps output within width for very large unicode input", () => {
|
|
const text = "🙂界".repeat(100_000);
|
|
const truncated = truncateToWidth(text, 40, Ellipsis.Unicode);
|
|
|
|
expect(visibleWidth(truncated)).toBeLessThanOrEqual(40);
|
|
});
|
|
|
|
it("preserves ANSI styling for kept text", () => {
|
|
const text = `\x1b[31m${"hello ".repeat(1000)}\x1b[0m`;
|
|
const truncated = truncateToWidth(text, 20, Ellipsis.Unicode);
|
|
|
|
expect(visibleWidth(truncated)).toBeLessThanOrEqual(20);
|
|
expect(truncated.includes("\x1b[31m")).toBe(true);
|
|
});
|
|
|
|
it("handles malformed ANSI escape prefixes without hanging", () => {
|
|
const text = `abc\x1bnot-ansi ${"🙂".repeat(1000)}`;
|
|
// Should complete without hanging — the exact width depends on how the
|
|
// native implementation classifies the malformed escape prefix.
|
|
const truncated = truncateToWidth(text, 20, Ellipsis.Unicode);
|
|
expect(typeof truncated).toBe("string");
|
|
});
|
|
|
|
it("returns the original text when it already fits", () => {
|
|
expect(truncateToWidth("a", 2, Ellipsis.Unicode)).toBe("a");
|
|
expect(truncateToWidth("界", 2, Ellipsis.Unicode)).toBe("界");
|
|
});
|
|
|
|
it("pads truncated output to requested width", () => {
|
|
const truncated = truncateToWidth("🙂界🙂界🙂界", 8, Ellipsis.Unicode, true);
|
|
expect(visibleWidth(truncated)).toBe(8);
|
|
});
|
|
|
|
it("adds a trailing reset when truncating without an ellipsis", () => {
|
|
const truncated = truncateToWidth(`\x1b[31m${"hello".repeat(100)}`, 10, Ellipsis.Omit);
|
|
expect(visibleWidth(truncated)).toBeLessThanOrEqual(10);
|
|
expect(truncated.endsWith("\x1b[0m")).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("visibleWidth", () => {
|
|
it("counts tabs inline and skips ANSI inline", () => {
|
|
expect(visibleWidth("\t\x1b[31m界\x1b[0m")).toBe(5);
|
|
});
|
|
});
|