fix(tui): avoided auto-linked token rate format
Rendered the status-line token rate as an explicit tok/s unit so Ghostty no longer auto-detects the numeric value as a URL. Added a regression test for the token_rate segment rendering contract. Fixes #4541
This commit is contained in:
@@ -12,6 +12,8 @@
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed the status-line token-rate segment rendering as `<number>/s`, which Ghostty auto-detected as a hyperlink on Ctrl+hover. ([#4541](https://github.com/can1357/oh-my-pi/issues/4541))
|
||||
|
||||
- Fixed raw `read` ranges not contributing to edit seen-line provenance, so re-reading an anchor range with `:raw` now unblocks hashline edits without adding non-raw line prefixes.
|
||||
- Fixed replan-driven session title refresh updating the statusline but not the terminal window title: terminal-title updates now fire from the session-name-changed listener, so every `setSessionName` path (first-input titling, `/rename`, plan seeding, replan refresh) sets the OSC title consistently.
|
||||
- Fixed user-interrupt aborts rendering the persisted `Interrupted by user` label in assistant transcripts; replay and live views now suppress that redundant line again while preserving generic/custom abort labels.
|
||||
|
||||
@@ -379,7 +379,7 @@ const tokenRateSegment: StatusLineSegment = {
|
||||
const { tokensPerSecond } = ctx.usageStats;
|
||||
if (!tokensPerSecond) return { content: "", visible: false };
|
||||
|
||||
const content = withIcon(theme.icon.throughput, `${tokensPerSecond.toFixed(1)}/s`);
|
||||
const content = withIcon(theme.icon.throughput, `${tokensPerSecond.toFixed(1)} tok/s`);
|
||||
return { content: theme.fg("statusLineOutput", content), visible: true };
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import { beforeAll, describe, expect, it } from "bun:test";
|
||||
import { stripVTControlCharacters } from "node:util";
|
||||
import type { AssistantMessage } from "@oh-my-pi/pi-ai";
|
||||
import { renderSegment } from "@oh-my-pi/pi-coding-agent/modes/components/status-line/segments";
|
||||
import { calculateTokensPerSecond } from "@oh-my-pi/pi-coding-agent/modes/components/status-line/token-rate";
|
||||
import type { SegmentContext } from "@oh-my-pi/pi-coding-agent/modes/components/status-line/types";
|
||||
import { initTheme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
||||
|
||||
beforeAll(async () => {
|
||||
await initTheme();
|
||||
});
|
||||
|
||||
function assistantMessage(overrides?: Partial<AssistantMessage>): AssistantMessage {
|
||||
return {
|
||||
@@ -23,6 +31,33 @@ function assistantMessage(overrides?: Partial<AssistantMessage>): AssistantMessa
|
||||
};
|
||||
}
|
||||
|
||||
function ctxWithTokenRate(tokensPerSecond: number | null): SegmentContext {
|
||||
return {
|
||||
usageStats: {
|
||||
input: 0,
|
||||
output: 0,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
premiumRequests: 0,
|
||||
cost: 0,
|
||||
tokensPerSecond,
|
||||
},
|
||||
} as unknown as SegmentContext;
|
||||
}
|
||||
|
||||
describe("token_rate status-line segment", () => {
|
||||
it("renders per-second throughput without a numeric slash path", () => {
|
||||
const rendered = renderSegment("token_rate", ctxWithTokenRate(35.5));
|
||||
const content = stripVTControlCharacters(rendered.content);
|
||||
|
||||
expect(rendered.visible).toBe(true);
|
||||
expect(content).toContain("35.5");
|
||||
expect(content).toMatch(/(?:\/s|\bs\b|\bsec(?:ond)?s?\b|\btps\b)/i);
|
||||
expect(content).not.toContain("35.5/s");
|
||||
expect(content).not.toMatch(/\b\d+(?:\.\d+)?\/s\b/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("token rate calculation", () => {
|
||||
it("computes from completed message duration metadata", () => {
|
||||
const base = assistantMessage();
|
||||
|
||||
Reference in New Issue
Block a user