pi-zentui 0.1.3 → 0.1.5
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/README.md +67 -27
- package/extensions/zentui/config.ts +17 -18
- package/extensions/zentui/index.ts +45 -54
- package/extensions/zentui/project-refresh.ts +13 -0
- package/extensions/zentui/runtime.ts +776 -109
- package/extensions/zentui/ui.ts +16 -109
- package/package.json +7 -7
- package/extensions/zentui/compact-tools.ts +0 -565
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Zentui
|
|
2
2
|
|
|
3
|
-
A Starship-inspired statusline and Opencode-style TUI for [Pi](https://
|
|
3
|
+
A Starship-inspired statusline and Opencode-style TUI for [Pi](https://pi.dev).
|
|
4
4
|
|
|
5
5
|
## Screenshots
|
|
6
6
|
|
|
@@ -11,7 +11,7 @@ A Starship-inspired statusline and Opencode-style TUI for [Pi](https://github.co
|
|
|
11
11
|
Zentui brings two popular aesthetics to Pi:
|
|
12
12
|
|
|
13
13
|
- **[Starship](https://starship.rs/) footer** — shows your current directory, git branch, git status indicators, and runtime/version detection in a compact, icon-rich format
|
|
14
|
-
- **[Opencode](https://github.com/opencode-ai/opencode) editor
|
|
14
|
+
- **[Opencode](https://github.com/opencode-ai/opencode) editor** — clean bordered input box with accent rail and model/provider display inside the editor frame
|
|
15
15
|
|
|
16
16
|
## Features
|
|
17
17
|
|
|
@@ -20,7 +20,7 @@ Zentui brings two popular aesthetics to Pi:
|
|
|
20
20
|
- ` dirname` — current directory with icon
|
|
21
21
|
- `on branch` — git branch with icon
|
|
22
22
|
- `[!?↑]` — git status indicators (modified, untracked, ahead/behind, stashed, etc.)
|
|
23
|
-
- `via v5.5.0` — runtime detection with version
|
|
23
|
+
- `via v5.5.0` — runtime detection with version and logo color for Starship Nerd Font runtime/language modules
|
|
24
24
|
- Right side shows context usage, token counts, and cost
|
|
25
25
|
|
|
26
26
|
### Editor (Opencode-inspired)
|
|
@@ -28,7 +28,6 @@ Zentui brings two popular aesthetics to Pi:
|
|
|
28
28
|
- Bordered input box with accent-colored left rail
|
|
29
29
|
- Model name and provider displayed inside the editor frame
|
|
30
30
|
- Thinking level indicator when enabled
|
|
31
|
-
- Matching style for previous user messages
|
|
32
31
|
|
|
33
32
|
### Git Status Icons
|
|
34
33
|
|
|
@@ -47,20 +46,67 @@ Zentui brings two popular aesthetics to Pi:
|
|
|
47
46
|
|
|
48
47
|
### Runtime Detection
|
|
49
48
|
|
|
50
|
-
Detects
|
|
51
|
-
|
|
52
|
-
| Runtime | Detection
|
|
53
|
-
|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
|
|
|
59
|
-
|
|
|
60
|
-
|
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
-
|
|
|
49
|
+
Detects Starship Nerd Font runtime/language modules, uses the Starship Nerd Font symbols, and colors each runtime with its official brand color where available:
|
|
50
|
+
|
|
51
|
+
| Runtime/language | Detection examples |
|
|
52
|
+
| ---------------- | ------------------------------------------------------------ |
|
|
53
|
+
| Buf | `buf.yaml`, `buf.gen.yaml`, `buf.work.yaml` |
|
|
54
|
+
| Bun | `bun.lock`, `bun.lockb` |
|
|
55
|
+
| C | `.c`, `.h` files |
|
|
56
|
+
| C++ | `.cpp`, `.cc`, `.cxx`, `.hpp` files |
|
|
57
|
+
| CMake | `CMakeLists.txt`, `CMakeCache.txt` |
|
|
58
|
+
| COBOL | `.cbl`, `.cob` files |
|
|
59
|
+
| Conda | `CONDA_DEFAULT_ENV` environment |
|
|
60
|
+
| Crystal | `.cr` files, `shard.yml` |
|
|
61
|
+
| Dart | `.dart` files, `pubspec.yaml`, `.dart_tool/` |
|
|
62
|
+
| Deno | `deno.json`, `deno.jsonc`, `deno.lock` |
|
|
63
|
+
| .NET | `.csproj`, `.fsproj`, `global.json`, `Directory.Build.*` |
|
|
64
|
+
| Elixir | `mix.exs` |
|
|
65
|
+
| Elm | `.elm` files, `elm.json`, `elm-stuff/` |
|
|
66
|
+
| Erlang | `rebar.config`, `erlang.mk` |
|
|
67
|
+
| Fennel | `.fnl` files |
|
|
68
|
+
| Fortran | `.f`, `.f90`, `.f95`, `.f03`, `.f08`, `.f18`, `fpm.toml` |
|
|
69
|
+
| Gleam | `.gleam` files, `gleam.toml` |
|
|
70
|
+
| Go | `go.mod` |
|
|
71
|
+
| Gradle | `build.gradle`, `build.gradle.kts`, `gradle/` |
|
|
72
|
+
| Guix shell | `GUIX_ENVIRONMENT` environment |
|
|
73
|
+
| Haskell | `.hs`, `.cabal`, `stack.yaml`, `cabal.project` |
|
|
74
|
+
| Haxe | `.hx`, `.hxml`, `haxelib.json`, `.haxerc` |
|
|
75
|
+
| Helm | `helmfile.yaml`, `Chart.yaml` |
|
|
76
|
+
| Java | `.java-version` |
|
|
77
|
+
| Julia | `.jl` files, `Project.toml`, `Manifest.toml` |
|
|
78
|
+
| Kotlin | `.kt`, `.kts` files |
|
|
79
|
+
| Lua | `.lua` files, `stylua.toml`, `.luarc.json`, `lua/` dir |
|
|
80
|
+
| Maven | `pom.xml` |
|
|
81
|
+
| Meson | `MESON_DEVENV=1` and `MESON_PROJECT_NAME` environment |
|
|
82
|
+
| Mojo | `.mojo` files |
|
|
83
|
+
| Nim | `.nim`, `.nims`, `.nimble`, `nim.cfg` |
|
|
84
|
+
| Nix shell | `IN_NIX_SHELL=pure` or `IN_NIX_SHELL=impure` environment |
|
|
85
|
+
| Node.js | `package.json`, `.nvmrc`, `.node-version` |
|
|
86
|
+
| OCaml | `.opam`, `.ml`, `.mli`, `dune`, `_opam/`, `esy.lock/` |
|
|
87
|
+
| Odin | `.odin` files |
|
|
88
|
+
| OPA/Rego | `.rego` files |
|
|
89
|
+
| Perl | `.pl`, `.pm`, `Makefile.PL`, `cpanfile`, `META.*` |
|
|
90
|
+
| PHP | `composer.json` |
|
|
91
|
+
| Pixi | `pixi.toml`, `pixi.lock`, `PIXI_ENVIRONMENT_NAME` environment |
|
|
92
|
+
| Pulumi | `Pulumi.yaml`, `Pulumi.yml` |
|
|
93
|
+
| PureScript | `.purs` files, `spago.dhall`, `spago.yaml`, `spago.lock` |
|
|
94
|
+
| Python | `pyproject.toml`, `requirements.txt`, `setup.py`, `Pipfile` |
|
|
95
|
+
| R | `.R`, `.Rmd`, `.Rproj`, `DESCRIPTION`, `.Rproj.user/` |
|
|
96
|
+
| Raku | `.raku`, `.rakumod`, `.p6`, `.pm6`, `META6.json` |
|
|
97
|
+
| Red | `.red`, `.reds` files |
|
|
98
|
+
| Ruby | `Gemfile`, `.ruby-version` |
|
|
99
|
+
| Rust | `Cargo.toml` |
|
|
100
|
+
| Scala | `.scala`, `.sbt`, `build.sbt`, `.metals/` |
|
|
101
|
+
| Solidity | `.sol` files |
|
|
102
|
+
| Spack | `SPACK_ENV` environment |
|
|
103
|
+
| Swift | `.swift` files, `Package.swift` |
|
|
104
|
+
| Terraform | `.tf`, `.tfplan`, `.tfstate`, `.terraform/` |
|
|
105
|
+
| Typst | `.typ` files, `template.typ` |
|
|
106
|
+
| Vagrant | `Vagrantfile` |
|
|
107
|
+
| V | `.v` files, `v.mod`, `vpkg.json` |
|
|
108
|
+
| Xmake | `xmake.lua` |
|
|
109
|
+
| Zig | `.zig` files, `build.zig` |
|
|
64
110
|
|
|
65
111
|
## Install
|
|
66
112
|
|
|
@@ -84,6 +130,7 @@ On first run, Zentui creates a config file at:
|
|
|
84
130
|
|
|
85
131
|
```json
|
|
86
132
|
{
|
|
133
|
+
"projectRefreshIntervalMs": 30000,
|
|
87
134
|
"icons": {
|
|
88
135
|
"cwd": "",
|
|
89
136
|
"git": "",
|
|
@@ -109,18 +156,11 @@ On first run, Zentui creates a config file at:
|
|
|
109
156
|
"tokens": "muted",
|
|
110
157
|
"cost": "success",
|
|
111
158
|
"separator": "borderMuted"
|
|
112
|
-
},
|
|
113
|
-
"tools": {
|
|
114
|
-
"style": "compact"
|
|
115
159
|
}
|
|
116
160
|
}
|
|
117
161
|
```
|
|
118
162
|
|
|
119
|
-
`
|
|
120
|
-
|
|
121
|
-
- `compact` — one-line tool calls by default
|
|
122
|
-
- `truncated` — expanded preview with long output truncated
|
|
123
|
-
- `full` — expanded full output
|
|
163
|
+
`projectRefreshIntervalMs` controls how often Zentui refreshes project status (git/runtime) while Pi is idle. Set it to `0` to disable polling; invalid values or values below 5000 ms fall back to `30000`.
|
|
124
164
|
|
|
125
165
|
### Color values
|
|
126
166
|
|
|
@@ -133,7 +173,7 @@ This means Zentui works with any Pi theme — it uses your theme's colors by def
|
|
|
133
173
|
|
|
134
174
|
## Requirements
|
|
135
175
|
|
|
136
|
-
- [Pi](https://
|
|
176
|
+
- [Pi](https://pi.dev) coding agent 0.74 or newer
|
|
137
177
|
- A [Nerd Font](https://www.nerdfonts.com/) for icons
|
|
138
178
|
|
|
139
179
|
## Development
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { getAgentDir } from "@
|
|
3
|
+
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
|
|
5
5
|
export type ColorSpec = string;
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
const DEFAULT_PROJECT_REFRESH_INTERVAL_MS = 30_000;
|
|
8
|
+
const MIN_PROJECT_REFRESH_INTERVAL_MS = 5_000;
|
|
7
9
|
|
|
8
10
|
export type PolishedTuiConfig = {
|
|
11
|
+
projectRefreshIntervalMs: number;
|
|
9
12
|
icons: {
|
|
10
13
|
cwd: string;
|
|
11
14
|
git: string;
|
|
@@ -32,9 +35,6 @@ export type PolishedTuiConfig = {
|
|
|
32
35
|
cost: ColorSpec;
|
|
33
36
|
separator: ColorSpec;
|
|
34
37
|
};
|
|
35
|
-
tools: {
|
|
36
|
-
style: ToolOutputStyle;
|
|
37
|
-
};
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
export const configPath = join(getAgentDir(), "zentui.json");
|
|
@@ -88,6 +88,7 @@ const themeColorTokens = new Set([
|
|
|
88
88
|
]);
|
|
89
89
|
|
|
90
90
|
export const defaultConfig: PolishedTuiConfig = {
|
|
91
|
+
projectRefreshIntervalMs: DEFAULT_PROJECT_REFRESH_INTERVAL_MS,
|
|
91
92
|
icons: {
|
|
92
93
|
cwd: "",
|
|
93
94
|
git: "",
|
|
@@ -114,9 +115,6 @@ export const defaultConfig: PolishedTuiConfig = {
|
|
|
114
115
|
cost: "success",
|
|
115
116
|
separator: "borderMuted",
|
|
116
117
|
},
|
|
117
|
-
tools: {
|
|
118
|
-
style: "compact",
|
|
119
|
-
},
|
|
120
118
|
};
|
|
121
119
|
|
|
122
120
|
function isHexColor(value: string): boolean {
|
|
@@ -141,10 +139,16 @@ function isRecord(value: unknown): value is ConfigRecord {
|
|
|
141
139
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
142
140
|
}
|
|
143
141
|
|
|
144
|
-
function
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
142
|
+
function parseProjectRefreshIntervalMs(value: unknown): number {
|
|
143
|
+
if (value === 0) return 0;
|
|
144
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
145
|
+
return defaultConfig.projectRefreshIntervalMs;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const interval = Math.round(value);
|
|
149
|
+
return interval >= MIN_PROJECT_REFRESH_INTERVAL_MS
|
|
150
|
+
? interval
|
|
151
|
+
: defaultConfig.projectRefreshIntervalMs;
|
|
148
152
|
}
|
|
149
153
|
|
|
150
154
|
export function colorize(theme: ThemeLike, color: ColorSpec, text: string): string {
|
|
@@ -173,9 +177,8 @@ export function mergeConfig(parsed: unknown): PolishedTuiConfig {
|
|
|
173
177
|
const colors = isRecord(config.colors)
|
|
174
178
|
? (config.colors as Partial<PolishedTuiConfig["colors"]>)
|
|
175
179
|
: {};
|
|
176
|
-
const tools = isRecord(config.tools) ? (config.tools as Partial<PolishedTuiConfig["tools"]>) : {};
|
|
177
|
-
|
|
178
180
|
return {
|
|
181
|
+
projectRefreshIntervalMs: parseProjectRefreshIntervalMs(config.projectRefreshIntervalMs),
|
|
179
182
|
icons: {
|
|
180
183
|
...defaultConfig.icons,
|
|
181
184
|
...icons,
|
|
@@ -184,10 +187,6 @@ export function mergeConfig(parsed: unknown): PolishedTuiConfig {
|
|
|
184
187
|
...defaultConfig.colors,
|
|
185
188
|
...colors,
|
|
186
189
|
},
|
|
187
|
-
tools: {
|
|
188
|
-
...defaultConfig.tools,
|
|
189
|
-
style: parseToolOutputStyle(tools.style),
|
|
190
|
-
},
|
|
191
190
|
};
|
|
192
191
|
}
|
|
193
192
|
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import type { AssistantMessage } from "@
|
|
1
|
+
import type { AssistantMessage } from "@earendil-works/pi-ai";
|
|
2
2
|
import type {
|
|
3
3
|
ExtensionAPI,
|
|
4
4
|
ExtensionContext,
|
|
5
5
|
KeybindingsManager,
|
|
6
6
|
Theme,
|
|
7
|
-
} from "@
|
|
8
|
-
import { type EditorTheme, type TUI, truncateToWidth, visibleWidth } from "@
|
|
9
|
-
import { registerCompactTools } from "./compact-tools";
|
|
7
|
+
} from "@earendil-works/pi-coding-agent";
|
|
8
|
+
import { type EditorTheme, type TUI, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
10
9
|
import { type PolishedTuiConfig, colorize, ensureConfigExists, loadConfig } from "./config";
|
|
11
10
|
import { type GitStatusSummary, emptyGitStatus, readGitStatus } from "./git";
|
|
11
|
+
import { type StopProjectRefreshInterval, startProjectRefreshInterval } from "./project-refresh";
|
|
12
12
|
import { type RuntimeInfo, readRuntimeInfo } from "./runtime";
|
|
13
|
-
import { PolishedEditor
|
|
13
|
+
import { PolishedEditor } from "./ui";
|
|
14
14
|
|
|
15
15
|
type FooterState = GitStatusSummary & {
|
|
16
16
|
modelLabel: string;
|
|
@@ -85,30 +85,6 @@ function buildContextLabel(ctx: ExtensionContext): string {
|
|
|
85
85
|
return `${percent}/${formatCount(contextWindow)}`;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
function getRuntimeColorToken(runtime: RuntimeInfo | undefined): string {
|
|
89
|
-
switch (runtime?.name) {
|
|
90
|
-
case "nodejs":
|
|
91
|
-
return "success";
|
|
92
|
-
case "deno":
|
|
93
|
-
return "syntaxType";
|
|
94
|
-
case "bun":
|
|
95
|
-
return "warning";
|
|
96
|
-
case "python":
|
|
97
|
-
case "java":
|
|
98
|
-
return "warning";
|
|
99
|
-
case "rust":
|
|
100
|
-
case "ruby":
|
|
101
|
-
return "error";
|
|
102
|
-
case "golang":
|
|
103
|
-
return "syntaxType";
|
|
104
|
-
case "lua":
|
|
105
|
-
case "php":
|
|
106
|
-
return "accent";
|
|
107
|
-
default:
|
|
108
|
-
return "text";
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
88
|
function formatRuntimeSegment(
|
|
113
89
|
theme: Pick<Theme, "fg">,
|
|
114
90
|
runtime: RuntimeInfo | undefined,
|
|
@@ -116,7 +92,7 @@ function formatRuntimeSegment(
|
|
|
116
92
|
): string {
|
|
117
93
|
if (!runtime) return "";
|
|
118
94
|
const label = runtime.version ? `${runtime.symbol} ${runtime.version}` : runtime.symbol;
|
|
119
|
-
return `${colorize(theme, mutedColor, "via")} ${colorize(theme,
|
|
95
|
+
return `${colorize(theme, mutedColor, "via")} ${colorize(theme, runtime.color, label)}`;
|
|
120
96
|
}
|
|
121
97
|
|
|
122
98
|
function formatCwdLabel(cwd: string, cwdIcon: string): string {
|
|
@@ -127,8 +103,6 @@ function formatCwdLabel(cwd: string, cwdIcon: string): string {
|
|
|
127
103
|
}
|
|
128
104
|
|
|
129
105
|
export default function (pi: ExtensionAPI) {
|
|
130
|
-
registerCompactTools(pi);
|
|
131
|
-
|
|
132
106
|
const state: FooterState = {
|
|
133
107
|
modelLabel: "no-model",
|
|
134
108
|
providerLabel: "Unknown",
|
|
@@ -141,11 +115,31 @@ export default function (pi: ExtensionAPI) {
|
|
|
141
115
|
|
|
142
116
|
let currentConfig: PolishedTuiConfig = loadConfig();
|
|
143
117
|
let requestFooterRender: (() => void) | undefined;
|
|
118
|
+
let stopProjectRefreshInterval: StopProjectRefreshInterval = () => {};
|
|
144
119
|
let projectRefreshInFlight = false;
|
|
145
120
|
let projectRefreshPending = false;
|
|
146
121
|
|
|
147
122
|
const refresh = () => requestFooterRender?.();
|
|
148
123
|
|
|
124
|
+
const cleanupUi = (ctx?: ExtensionContext) => {
|
|
125
|
+
stopProjectRefreshInterval();
|
|
126
|
+
stopProjectRefreshInterval = () => {};
|
|
127
|
+
projectRefreshInFlight = false;
|
|
128
|
+
projectRefreshPending = false;
|
|
129
|
+
requestFooterRender = undefined;
|
|
130
|
+
if (ctx?.hasUI) {
|
|
131
|
+
ctx.ui.setFooter(undefined);
|
|
132
|
+
ctx.ui.setEditorComponent(undefined);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const refreshInteractiveState = (ctx: ExtensionContext, project = false) => {
|
|
137
|
+
if (!ctx.hasUI) return;
|
|
138
|
+
syncState(ctx);
|
|
139
|
+
if (project) scheduleProjectRefresh(ctx);
|
|
140
|
+
refresh();
|
|
141
|
+
};
|
|
142
|
+
|
|
149
143
|
const syncState = (ctx: ExtensionContext) => {
|
|
150
144
|
const totals = getUsageTotals(ctx);
|
|
151
145
|
state.modelLabel = ctx.model?.id ?? "no-model";
|
|
@@ -199,6 +193,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
199
193
|
},
|
|
200
194
|
invalidate() {},
|
|
201
195
|
render(width: number): string[] {
|
|
196
|
+
if (width <= 0) return [""];
|
|
202
197
|
const innerWidth = Math.max(1, width - 2);
|
|
203
198
|
const cwdLabel = colorize(
|
|
204
199
|
theme,
|
|
@@ -255,11 +250,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
255
250
|
const rightWidth = visibleWidth(right);
|
|
256
251
|
const content =
|
|
257
252
|
leftWidth >= innerWidth
|
|
258
|
-
? truncateToWidth(left, innerWidth)
|
|
253
|
+
? truncateToWidth(left, innerWidth, "")
|
|
259
254
|
: leftWidth + 1 + rightWidth <= innerWidth
|
|
260
255
|
? `${left}${" ".repeat(innerWidth - leftWidth - rightWidth)}${right}`
|
|
261
|
-
: left;
|
|
262
|
-
|
|
256
|
+
: truncateToWidth(left, innerWidth, "");
|
|
257
|
+
const framed = width > 2 ? ` ${truncateToWidth(content, width - 2, "")} ` : content;
|
|
258
|
+
return [truncateToWidth(framed, width, "")];
|
|
263
259
|
},
|
|
264
260
|
};
|
|
265
261
|
});
|
|
@@ -286,11 +282,16 @@ export default function (pi: ExtensionAPI) {
|
|
|
286
282
|
};
|
|
287
283
|
|
|
288
284
|
const installUi = (ctx: ExtensionContext) => {
|
|
285
|
+
if (!ctx.hasUI) return;
|
|
289
286
|
ensureConfigExists();
|
|
290
287
|
currentConfig = loadConfig();
|
|
291
|
-
patchUserMessageComponent(ctx.ui.theme);
|
|
292
288
|
installFooter(ctx);
|
|
293
289
|
installEditor(ctx);
|
|
290
|
+
stopProjectRefreshInterval();
|
|
291
|
+
stopProjectRefreshInterval = startProjectRefreshInterval(
|
|
292
|
+
currentConfig.projectRefreshIntervalMs,
|
|
293
|
+
() => scheduleProjectRefresh(ctx),
|
|
294
|
+
);
|
|
294
295
|
scheduleProjectRefresh(ctx);
|
|
295
296
|
refresh();
|
|
296
297
|
};
|
|
@@ -299,41 +300,31 @@ export default function (pi: ExtensionAPI) {
|
|
|
299
300
|
installUi(ctx);
|
|
300
301
|
});
|
|
301
302
|
|
|
302
|
-
pi.on("session_shutdown", async () => {
|
|
303
|
-
|
|
303
|
+
pi.on("session_shutdown", async (_event, ctx) => {
|
|
304
|
+
cleanupUi(ctx);
|
|
304
305
|
});
|
|
305
306
|
|
|
306
307
|
pi.on("agent_start", async (_event, ctx) => {
|
|
307
|
-
|
|
308
|
-
refresh();
|
|
308
|
+
refreshInteractiveState(ctx);
|
|
309
309
|
});
|
|
310
310
|
|
|
311
311
|
pi.on("agent_end", async (_event, ctx) => {
|
|
312
|
-
|
|
313
|
-
scheduleProjectRefresh(ctx);
|
|
314
|
-
refresh();
|
|
312
|
+
refreshInteractiveState(ctx, true);
|
|
315
313
|
});
|
|
316
314
|
|
|
317
315
|
pi.on("model_select", async (_event, ctx) => {
|
|
318
|
-
|
|
319
|
-
refresh();
|
|
316
|
+
refreshInteractiveState(ctx);
|
|
320
317
|
});
|
|
321
318
|
|
|
322
319
|
pi.on("message_end", async (_event, ctx) => {
|
|
323
|
-
|
|
324
|
-
scheduleProjectRefresh(ctx);
|
|
325
|
-
refresh();
|
|
320
|
+
refreshInteractiveState(ctx, true);
|
|
326
321
|
});
|
|
327
322
|
|
|
328
323
|
pi.on("tool_execution_end", async (_event, ctx) => {
|
|
329
|
-
|
|
330
|
-
scheduleProjectRefresh(ctx);
|
|
331
|
-
refresh();
|
|
324
|
+
refreshInteractiveState(ctx, true);
|
|
332
325
|
});
|
|
333
326
|
|
|
334
327
|
pi.on("session_compact", async (_event, ctx) => {
|
|
335
|
-
|
|
336
|
-
scheduleProjectRefresh(ctx);
|
|
337
|
-
refresh();
|
|
328
|
+
refreshInteractiveState(ctx, true);
|
|
338
329
|
});
|
|
339
330
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type StopProjectRefreshInterval = () => void;
|
|
2
|
+
|
|
3
|
+
export function startProjectRefreshInterval(
|
|
4
|
+
intervalMs: number,
|
|
5
|
+
refresh: () => void,
|
|
6
|
+
): StopProjectRefreshInterval {
|
|
7
|
+
if (intervalMs <= 0) return () => {};
|
|
8
|
+
|
|
9
|
+
const timer = setInterval(refresh, intervalMs);
|
|
10
|
+
timer.unref?.();
|
|
11
|
+
|
|
12
|
+
return () => clearInterval(timer);
|
|
13
|
+
}
|