feat(coding-agent): add configurable working indicator closes #3413
This commit is contained in:
@@ -53,6 +53,7 @@ cp permission-gate.ts ~/.pi/agent/extensions/
|
||||
| `status-line.ts` | Shows turn progress in footer via `ctx.ui.setStatus()` with themed colors |
|
||||
| `widget-placement.ts` | Shows widgets above and below the editor via `ctx.ui.setWidget()` placement |
|
||||
| `hidden-thinking-label.ts` | Customizes the collapsed thinking label via `ctx.ui.setHiddenThinkingLabel()` |
|
||||
| `working-indicator.ts` | Customizes the streaming working indicator via `ctx.ui.setWorkingIndicator()` |
|
||||
| `model-status.ts` | Shows model changes in status bar via `model_select` hook |
|
||||
| `snake.ts` | Snake game with custom UI, keyboard handling, and session persistence |
|
||||
| `tic-tac-toe.ts` | Tic-tac-toe vs the agent with `executionMode: "sequential"` tools to prevent race conditions on shared cursor state |
|
||||
|
||||
105
packages/coding-agent/examples/extensions/working-indicator.ts
Normal file
105
packages/coding-agent/examples/extensions/working-indicator.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* Working Indicator Extension
|
||||
*
|
||||
* Demonstrates `ctx.ui.setWorkingIndicator()` for customizing the inline
|
||||
* working indicator shown while pi is streaming a response.
|
||||
*
|
||||
* Usage:
|
||||
* pi --extension examples/extensions/working-indicator.ts
|
||||
*
|
||||
* Commands:
|
||||
* /working-indicator Show current mode
|
||||
* /working-indicator dot Use a static dot indicator
|
||||
* /working-indicator pulse Use a custom animated indicator
|
||||
* /working-indicator none Hide the indicator entirely
|
||||
* /working-indicator spinner Restore an animated spinner
|
||||
* /working-indicator reset Restore pi's default spinner
|
||||
*/
|
||||
|
||||
import type { ExtensionAPI, ExtensionContext, WorkingIndicatorOptions } from "@mariozechner/pi-coding-agent";
|
||||
|
||||
type WorkingIndicatorMode = "dot" | "none" | "pulse" | "spinner" | "default";
|
||||
|
||||
const SPINNER_INDICATOR: WorkingIndicatorOptions = {
|
||||
frames: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
|
||||
intervalMs: 80,
|
||||
};
|
||||
const DOT_INDICATOR: WorkingIndicatorOptions = {
|
||||
frames: ["●"],
|
||||
};
|
||||
const PULSE_INDICATOR: WorkingIndicatorOptions = {
|
||||
frames: ["·", "•", "●", "•"],
|
||||
intervalMs: 120,
|
||||
};
|
||||
const HIDDEN_INDICATOR: WorkingIndicatorOptions = {
|
||||
frames: [],
|
||||
};
|
||||
|
||||
function getIndicator(mode: WorkingIndicatorMode): WorkingIndicatorOptions | undefined {
|
||||
switch (mode) {
|
||||
case "dot":
|
||||
return DOT_INDICATOR;
|
||||
case "none":
|
||||
return HIDDEN_INDICATOR;
|
||||
case "pulse":
|
||||
return PULSE_INDICATOR;
|
||||
case "spinner":
|
||||
return SPINNER_INDICATOR;
|
||||
case "default":
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function describeMode(mode: WorkingIndicatorMode): string {
|
||||
switch (mode) {
|
||||
case "dot":
|
||||
return "static dot";
|
||||
case "none":
|
||||
return "hidden";
|
||||
case "pulse":
|
||||
return "custom pulse";
|
||||
case "spinner":
|
||||
return "custom spinner";
|
||||
case "default":
|
||||
return "pi default spinner";
|
||||
}
|
||||
}
|
||||
|
||||
export default function (pi: ExtensionAPI) {
|
||||
let mode: WorkingIndicatorMode = "dot";
|
||||
|
||||
const applyIndicator = (ctx: ExtensionContext) => {
|
||||
ctx.ui.setWorkingIndicator(getIndicator(mode));
|
||||
ctx.ui.setStatus("working-indicator", ctx.ui.theme.fg("dim", `Indicator: ${describeMode(mode)}`));
|
||||
};
|
||||
|
||||
pi.on("session_start", async (_event, ctx) => {
|
||||
applyIndicator(ctx);
|
||||
});
|
||||
|
||||
pi.registerCommand("working-indicator", {
|
||||
description: "Set the streaming working indicator: dot, pulse, none, spinner, or reset.",
|
||||
handler: async (args, ctx) => {
|
||||
const nextMode = args.trim().toLowerCase();
|
||||
if (!nextMode) {
|
||||
ctx.ui.notify(`Working indicator: ${describeMode(mode)}`, "info");
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
nextMode !== "dot" &&
|
||||
nextMode !== "none" &&
|
||||
nextMode !== "pulse" &&
|
||||
nextMode !== "spinner" &&
|
||||
nextMode !== "reset"
|
||||
) {
|
||||
ctx.ui.notify("Usage: /working-indicator [dot|pulse|none|spinner|reset]", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
mode = nextMode === "reset" ? "default" : nextMode;
|
||||
applyIndicator(ctx);
|
||||
ctx.ui.notify(`Working indicator set to: ${describeMode(mode)}`, "info");
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user