fix(wow-statusline): stopped hiding working indicator when applying statusline UI
- Removed setWorkingVisible(false) call in applyStatuslineUI that hid the working indicator. - Removed setWorkingVisible(true) and setWorkingIndicator() calls in clearStatuslineUI on teardown. - Extracted installStatusWidget and installEditor functions from applyStatuslineUI. - Consolidated duplicate inline event handlers into shared refreshOnEvent function. - Extracted buildTopBorderLine and findBottomBorderIndex helpers in StatuslineEditor.
This commit is contained in:
@@ -4,7 +4,7 @@ import type {
|
||||
} from "@earendil-works/pi-coding-agent";
|
||||
import { registerModule, resetWowConfigCache } from "wow-core";
|
||||
import { getStatuslineConfig } from "./config";
|
||||
import { TAG, log } from "./global";
|
||||
import { log, TAG } from "./global";
|
||||
import { createState } from "./state";
|
||||
import {
|
||||
applyStatuslineUI,
|
||||
@@ -12,6 +12,8 @@ import {
|
||||
updateStatuslineState,
|
||||
} from "./ui";
|
||||
|
||||
const STREAMING_REFRESH_INTERVAL_MS = 80;
|
||||
|
||||
const setup = (pi: ExtensionAPI): void => {
|
||||
const state = createState();
|
||||
let lastStreamingRefresh = 0;
|
||||
@@ -26,47 +28,36 @@ const setup = (pi: ExtensionAPI): void => {
|
||||
updateStatuslineState(state);
|
||||
};
|
||||
|
||||
const refreshOnEvent = async (): Promise<void> => {
|
||||
refresh();
|
||||
};
|
||||
|
||||
pi.on("session_start", async (_event, ctx) => {
|
||||
state.thinkingLevel = pi.getThinkingLevel();
|
||||
apply(ctx);
|
||||
log.debug("statusline applied", { cwd: ctx.cwd });
|
||||
});
|
||||
|
||||
pi.on("agent_start", async () => {
|
||||
refresh();
|
||||
});
|
||||
pi.on("agent_start", refreshOnEvent);
|
||||
|
||||
pi.on("message_update", async () => {
|
||||
const now = Date.now();
|
||||
if (now - lastStreamingRefresh < 80) return;
|
||||
if (now - lastStreamingRefresh < STREAMING_REFRESH_INTERVAL_MS) return;
|
||||
lastStreamingRefresh = now;
|
||||
refresh();
|
||||
});
|
||||
|
||||
pi.on("model_select", async () => {
|
||||
refresh();
|
||||
});
|
||||
pi.on("model_select", refreshOnEvent);
|
||||
|
||||
pi.on("thinking_level_select", async (event) => {
|
||||
state.thinkingLevel = event.level;
|
||||
refresh();
|
||||
});
|
||||
|
||||
pi.on("turn_end", async () => {
|
||||
refresh();
|
||||
});
|
||||
|
||||
pi.on("agent_end", async () => {
|
||||
refresh();
|
||||
});
|
||||
|
||||
pi.on("session_tree", async () => {
|
||||
refresh();
|
||||
});
|
||||
|
||||
pi.on("session_compact", async () => {
|
||||
refresh();
|
||||
});
|
||||
pi.on("turn_end", refreshOnEvent);
|
||||
pi.on("agent_end", refreshOnEvent);
|
||||
pi.on("session_tree", refreshOnEvent);
|
||||
pi.on("session_compact", refreshOnEvent);
|
||||
|
||||
pi.on("session_shutdown", async (_event, ctx) => {
|
||||
clearStatuslineUI(ctx, state);
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
import type { Component, EditorTheme, TUI } from "@earendil-works/pi-tui";
|
||||
import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
||||
import { buildEditorTopLine, buildStatusSummary } from "./format";
|
||||
import { snapshotFromContext, type StatuslineState } from "./state";
|
||||
import { type StatuslineState, snapshotFromContext } from "./state";
|
||||
import type { ResolvedStatuslineConfig } from "./types";
|
||||
|
||||
const ANSI_SGR_RE = /\u001b\[[0-9;]*m/g;
|
||||
@@ -50,46 +50,62 @@ export class StatuslineEditor extends CustomEditor {
|
||||
this.statuslineState,
|
||||
this.extensionCtx,
|
||||
);
|
||||
let bottom = lines.length - 1;
|
||||
for (let index = lines.length - 1; index >= 1; index--) {
|
||||
const plain = lines[index]!.replace(ANSI_SGR_RE, "");
|
||||
if (plain.length > 0 && /^─{3,}/.test(plain)) {
|
||||
bottom = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const topText = buildEditorTopLine(theme, snapshot, this.statuslineConfig);
|
||||
const folder = this.statuslineConfig.editorStyle.folderIcon;
|
||||
const content = `${folder} ${topText}`;
|
||||
const border = theme.fg("borderMuted", "─");
|
||||
const contentWidth = visibleWidth(content);
|
||||
const remaining = width - contentWidth - 2;
|
||||
const borderLine = border.repeat(Math.max(0, width));
|
||||
const topContent = `${this.statuslineConfig.editorStyle.folderIcon} ${buildEditorTopLine(theme, snapshot, this.statuslineConfig)}`;
|
||||
const remainingBorderWidth = width - visibleWidth(topContent) - 2;
|
||||
|
||||
lines[0] =
|
||||
remaining >= 1
|
||||
? content + theme.fg("dim", " ") + border.repeat(remaining) + border
|
||||
: border.repeat(Math.max(0, width));
|
||||
lines[0] = buildTopBorderLine(
|
||||
topContent,
|
||||
border,
|
||||
borderLine,
|
||||
theme,
|
||||
remainingBorderWidth,
|
||||
);
|
||||
|
||||
if (lines.length > 1) {
|
||||
lines[1] =
|
||||
theme.fg("accent", this.statuslineConfig.editorStyle.prompt) +
|
||||
theme.fg("dim", " ") +
|
||||
(lines[1] ?? "");
|
||||
}
|
||||
|
||||
lines[bottom] = border.repeat(Math.max(0, width));
|
||||
lines[findBottomBorderIndex(lines)] = borderLine;
|
||||
|
||||
for (let index = 0; index < lines.length; index++) {
|
||||
if (visibleWidth(lines[index]!) > width) {
|
||||
if (visibleWidth(lines[index]!) <= width) continue;
|
||||
lines[index] = truncateToWidth(lines[index]!, width);
|
||||
}
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
}
|
||||
|
||||
function buildTopBorderLine(
|
||||
content: string,
|
||||
border: string,
|
||||
borderLine: string,
|
||||
theme: Theme,
|
||||
remainingBorderWidth: number,
|
||||
): string {
|
||||
if (remainingBorderWidth < 1) return borderLine;
|
||||
return (
|
||||
content +
|
||||
theme.fg("dim", " ") +
|
||||
border.repeat(remainingBorderWidth) +
|
||||
border
|
||||
);
|
||||
}
|
||||
|
||||
function findBottomBorderIndex(lines: string[]): number {
|
||||
for (let index = lines.length - 1; index >= 1; index--) {
|
||||
const plain = lines[index]!.replace(ANSI_SGR_RE, "");
|
||||
if (plain.length > 0 && /^─{3,}/.test(plain)) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
return lines.length - 1;
|
||||
}
|
||||
|
||||
export function buildStatuslineWidget(
|
||||
ctx: ExtensionContext,
|
||||
state: StatuslineState,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
|
||||
import { buildEditorTopLine, buildStatusSummary, EMPTY_USAGE } from "./format";
|
||||
import { type StatuslineState, syncFooterSnapshot } from "./state";
|
||||
import {
|
||||
buildStatuslineWidget,
|
||||
EmptyFooter,
|
||||
StatuslineEditor,
|
||||
buildStatuslineWidget,
|
||||
} from "./statusline-editor";
|
||||
import { syncFooterSnapshot, type StatuslineState } from "./state";
|
||||
import type { FooterDataSnapshot, ResolvedStatuslineConfig } from "./types";
|
||||
|
||||
const STATUS_WIDGET_ID = "wow-statusline:summary";
|
||||
@@ -61,26 +61,10 @@ export function applyStatuslineUI(
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.ui.setWorkingVisible(false);
|
||||
hijackFooter(ctx, state);
|
||||
installEmptyFooter(ctx, state);
|
||||
|
||||
ctx.ui.setWidget(
|
||||
STATUS_WIDGET_ID,
|
||||
(_tui, theme) => ({
|
||||
render(width: number): string[] {
|
||||
const render = buildStatuslineWidget(ctx, state, config);
|
||||
return render(width, theme);
|
||||
},
|
||||
invalidate() {},
|
||||
}),
|
||||
{ placement: "belowEditor" },
|
||||
);
|
||||
|
||||
ctx.ui.setEditorComponent(
|
||||
(tui, theme, keybindings) =>
|
||||
new StatuslineEditor(tui, theme, keybindings, ctx, state, config),
|
||||
);
|
||||
installStatusWidget(ctx, state, config);
|
||||
installEditor(ctx, state, config);
|
||||
}
|
||||
|
||||
export function updateStatuslineState(state: StatuslineState): void {
|
||||
@@ -102,8 +86,6 @@ export function clearStatuslineUI(
|
||||
ctx.ui.setWidget(STATUS_WIDGET_ID, undefined, { placement: "belowEditor" });
|
||||
ctx.ui.setWidget(STATUS_WIDGET_ID, undefined, { placement: "aboveEditor" });
|
||||
ctx.ui.setEditorComponent(undefined);
|
||||
ctx.ui.setWorkingVisible(true);
|
||||
ctx.ui.setWorkingIndicator();
|
||||
}
|
||||
|
||||
function hijackFooter(ctx: ExtensionContext, state: StatuslineState): void {
|
||||
@@ -133,6 +115,35 @@ function hijackFooter(ctx: ExtensionContext, state: StatuslineState): void {
|
||||
state.footerHijacked = true;
|
||||
}
|
||||
|
||||
function installStatusWidget(
|
||||
ctx: ExtensionContext,
|
||||
state: StatuslineState,
|
||||
config: ResolvedStatuslineConfig,
|
||||
): void {
|
||||
ctx.ui.setWidget(
|
||||
STATUS_WIDGET_ID,
|
||||
(_tui, theme) => ({
|
||||
render(width: number): string[] {
|
||||
const render = buildStatuslineWidget(ctx, state, config);
|
||||
return render(width, theme);
|
||||
},
|
||||
invalidate() {},
|
||||
}),
|
||||
{ placement: "belowEditor" },
|
||||
);
|
||||
}
|
||||
|
||||
function installEditor(
|
||||
ctx: ExtensionContext,
|
||||
state: StatuslineState,
|
||||
config: ResolvedStatuslineConfig,
|
||||
): void {
|
||||
ctx.ui.setEditorComponent(
|
||||
(tui, theme, keybindings) =>
|
||||
new StatuslineEditor(tui, theme, keybindings, ctx, state, config),
|
||||
);
|
||||
}
|
||||
|
||||
function installEmptyFooter(
|
||||
ctx: ExtensionContext,
|
||||
state: StatuslineState,
|
||||
@@ -151,7 +162,6 @@ function installEmptyFooter(
|
||||
) => {
|
||||
captureFooterData(state, theme, footerData);
|
||||
const dispose = footerData.onBranchChange(() => {
|
||||
state.branch = footerData.getGitBranch() || "";
|
||||
syncFooterSnapshot(state);
|
||||
state.requestRender?.();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user