@pi-unipi/unipi 2.12.2 → 2.13.0
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/CHANGELOG.md +11 -0
- package/package.json +3 -3
- package/packages/background-tasks/README.md +16 -0
- package/packages/background-tasks/package.json +1 -1
- package/packages/background-tasks/src/index.ts +8 -0
- package/packages/background-tasks/src/registry-shared.ts +32 -0
- package/packages/footer/README.md +1 -0
- package/packages/footer/package.json +3 -2
- package/packages/footer/src/index.ts +7 -5
- package/packages/footer/src/process-line.ts +74 -0
- package/packages/unipi/bundled.js +78 -24
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [2.13.0] — 2026-08-28
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `footer`: **bg-process one-liner above the glance footer** — a centered strip directly above the frame showing live background-task counts with status dots: green ● running, yellow ● stopped (killed), red ● failed, gray ● done. Zero-count buckets are omitted and the line hides entirely when idle. Reads `BackgroundTaskRegistry.allTasks()` directly (no events, no polling channels) and re-renders on the footer's 1s refresh timer; counts reset per session.
|
|
14
|
+
- `background-tasks`: **shared registry accessor** (`getSharedTaskRegistry` / `setSharedTaskRegistry` / `clearSharedTaskRegistry` in `src/registry-shared.ts`) — lets sibling extensions read the live task registry synchronously. Stored on `globalThis` under a `Symbol.for` key so the singleton survives duplicate module instances; published at extension init and `session_start`, cleared on `session_shutdown`.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- `footer`: new dependency on `@pi-unipi/background-tasks` for the process one-liner's direct registry reads.
|
|
19
|
+
|
|
9
20
|
## [2.12.0] — 2026-08-27
|
|
10
21
|
|
|
11
22
|
### Added
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/unipi",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.13.0",
|
|
4
4
|
"description": "All-in-one extension suite for Pi coding agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -83,12 +83,12 @@
|
|
|
83
83
|
},
|
|
84
84
|
"dependencies": {
|
|
85
85
|
"@pi-unipi/ask-user": "2.12.0",
|
|
86
|
-
"@pi-unipi/background-tasks": "2.
|
|
86
|
+
"@pi-unipi/background-tasks": "2.13.0",
|
|
87
87
|
"@pi-unipi/btw": "2.12.0",
|
|
88
88
|
"@pi-unipi/command-enchantment": "2.12.0",
|
|
89
89
|
"@pi-unipi/compactor": "2.12.0",
|
|
90
90
|
"@pi-unipi/core": "2.12.0",
|
|
91
|
-
"@pi-unipi/footer": "2.
|
|
91
|
+
"@pi-unipi/footer": "2.13.0",
|
|
92
92
|
"@pi-unipi/image": "2.12.0",
|
|
93
93
|
"@pi-unipi/info-screen": "2.12.0",
|
|
94
94
|
"@pi-unipi/input-shortcuts": "2.12.0",
|
|
@@ -74,6 +74,22 @@ subscription OAuth attribution, exact-match system-prompt sanitization, and
|
|
|
74
74
|
cache-retention policy for Anthropic routes. Duplicate installed copies resolve
|
|
75
75
|
ownership through an EventBus claim; later copies go inert.
|
|
76
76
|
|
|
77
|
+
## Shared registry (for sibling extensions)
|
|
78
|
+
|
|
79
|
+
Other packages can read the live task registry synchronously — no events, no polling:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import { getSharedTaskRegistry } from "@pi-unipi/background-tasks";
|
|
83
|
+
|
|
84
|
+
const tasks = getSharedTaskRegistry()?.allTasks() ?? [];
|
|
85
|
+
const running = tasks.filter((t) => t.status === "running").length;
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The registry is published on `globalThis` under a `Symbol.for` key at extension
|
|
89
|
+
init and `session_start`, and cleared on `session_shutdown` (counts are
|
|
90
|
+
per-session). Returns `undefined` when the module is disabled — callers must
|
|
91
|
+
treat that as "no data", e.g. the footer's glance process line does.
|
|
92
|
+
|
|
77
93
|
## Differences from the reference
|
|
78
94
|
|
|
79
95
|
- Commands live in the `/unipi:*` namespace; env prefix is `UNIPI_BG_*`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/background-tasks",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.13.0",
|
|
4
4
|
"description": "Background tasks for UniPi — durable shell jobs, delegated agents, attested Pi runs, and fixed-purpose Fusion workflows",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -19,10 +19,14 @@ import {
|
|
|
19
19
|
import { registerToolsAndCommands } from "./tools.js";
|
|
20
20
|
import { registerFusionExtension } from "./fusion-extension.js";
|
|
21
21
|
import { registerDelegateExtension } from "./delegate-extension.js";
|
|
22
|
+
import { setSharedTaskRegistry, clearSharedTaskRegistry } from "./registry-shared.js";
|
|
22
23
|
import { taskDisplayName, type BgTask, type StartAttestedPiTaskOptions, type StartTaskOptions } from "./types.js";
|
|
23
24
|
|
|
24
25
|
const STATUS_INTERVAL_MS = 1000;
|
|
25
26
|
|
|
27
|
+
// Direct synchronous access for sibling extensions (footer process one-liner).
|
|
28
|
+
export { getSharedTaskRegistry } from "./registry-shared.js";
|
|
29
|
+
|
|
26
30
|
export default function backgroundTasksExtension(pi: ExtensionAPI): void {
|
|
27
31
|
const { config, warnings } = loadBackgroundTasksConfig(process.cwd());
|
|
28
32
|
|
|
@@ -53,6 +57,8 @@ export default function backgroundTasksExtension(pi: ExtensionAPI): void {
|
|
|
53
57
|
eventService.publishTerminal(task);
|
|
54
58
|
},
|
|
55
59
|
});
|
|
60
|
+
setSharedTaskRegistry(registry);
|
|
61
|
+
|
|
56
62
|
const eventService: BackgroundTaskExtensionService = installBackgroundTaskExtensionApi({
|
|
57
63
|
events: pi.events,
|
|
58
64
|
registry,
|
|
@@ -253,6 +259,7 @@ export default function backgroundTasksExtension(pi: ExtensionAPI): void {
|
|
|
253
259
|
|
|
254
260
|
pi.on("session_start", async (_event, ctx) => {
|
|
255
261
|
registry.setShuttingDown(false);
|
|
262
|
+
setSharedTaskRegistry(registry);
|
|
256
263
|
currentCtx = ctx;
|
|
257
264
|
await registry.ensureRuntimeDir(ctx);
|
|
258
265
|
updateUi(ctx);
|
|
@@ -264,6 +271,7 @@ export default function backgroundTasksExtension(pi: ExtensionAPI): void {
|
|
|
264
271
|
|
|
265
272
|
pi.on("session_shutdown", async (_event, ctx) => {
|
|
266
273
|
registry.setShuttingDown(true);
|
|
274
|
+
clearSharedTaskRegistry();
|
|
267
275
|
currentCtx = undefined;
|
|
268
276
|
if (statusInterval) {
|
|
269
277
|
clearInterval(statusInterval);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared BackgroundTaskRegistry accessor
|
|
3
|
+
*
|
|
4
|
+
* Exposes the live registry to sibling extensions (e.g. @pi-unipi/footer)
|
|
5
|
+
* without events or request/response channels — direct synchronous reads of
|
|
6
|
+
* `allTasks()`.
|
|
7
|
+
*
|
|
8
|
+
* Stored on globalThis under a `Symbol.for` key so the singleton is shared
|
|
9
|
+
* even if this package ends up instantiated more than once (duplicate
|
|
10
|
+
* node_modules copies would otherwise each hold their own module state).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { BackgroundTaskRegistry } from "./registry.js";
|
|
14
|
+
|
|
15
|
+
const SHARED_REGISTRY_KEY = Symbol.for("unipi.background-tasks.shared-registry");
|
|
16
|
+
|
|
17
|
+
/** Publish the live registry (idempotent; later calls overwrite). */
|
|
18
|
+
export function setSharedTaskRegistry(registry: BackgroundTaskRegistry): void {
|
|
19
|
+
(globalThis as unknown as Record<symbol, unknown>)[SHARED_REGISTRY_KEY] = registry;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Read the live registry, or undefined when background-tasks is not loaded. */
|
|
23
|
+
export function getSharedTaskRegistry(): BackgroundTaskRegistry | undefined {
|
|
24
|
+
return (globalThis as unknown as Record<symbol, unknown>)[SHARED_REGISTRY_KEY] as
|
|
25
|
+
| BackgroundTaskRegistry
|
|
26
|
+
| undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Drop the shared reference (used on session shutdown so readers see a clean slate). */
|
|
30
|
+
export function clearSharedTaskRegistry(): void {
|
|
31
|
+
delete (globalThis as unknown as Record<symbol, unknown>)[SHARED_REGISTRY_KEY];
|
|
32
|
+
}
|
|
@@ -18,6 +18,7 @@ An experimental input surface, on by default and toggleable in `/unipi:footer-se
|
|
|
18
18
|
- **Top border:** animated lolcat-gradient UNIPI brand + git branch (turns rainbow-frame animated while thinking is max/xhigh)
|
|
19
19
|
- **Bottom border:** workspace · context %/window · model · thinking level
|
|
20
20
|
- **Session strip:** turns/steps, wall + tool wall time, average TTFT, tok/s, cache hit % — colored per stat, honest across restarts (derived from persisted session timestamps when live hooks are unavailable; provider-reported `usage.output` anchors token counts whenever present)
|
|
21
|
+
- **Process line (new in 2.13):** centered one-liner directly above the frame while background work is in flight — `● 3 running ● 1 stopped ● 1 failed ● 2 done` — green ● running, yellow ● stopped (killed), red ● failed, gray ● done. Covers every task type (shell jobs, delegates, fusion workflows) via direct registry reads; zero-count buckets are omitted and the line hides when idle. Counts reset per session.
|
|
21
22
|
- The classic segment status line is suppressed while glance mode is on; toggle it back for the classic footer
|
|
22
23
|
|
|
23
24
|
## Commands
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/footer",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.13.0",
|
|
4
4
|
"description": "Persistent status bar for Unipi — subscribes to UNIPI_EVENTS and renders key stats from all unipi packages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -32,7 +32,8 @@
|
|
|
32
32
|
"access": "public"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@pi-unipi/core": "2.12.0"
|
|
35
|
+
"@pi-unipi/core": "2.12.0",
|
|
36
|
+
"@pi-unipi/background-tasks": "2.13.0"
|
|
36
37
|
},
|
|
37
38
|
"peerDependencies": {
|
|
38
39
|
"@earendil-works/pi-coding-agent": "^0.84.0",
|
|
@@ -29,6 +29,7 @@ import { STATUS_EXT_SEGMENTS } from "./segments/status-ext.js";
|
|
|
29
29
|
|
|
30
30
|
import type { FooterGroup, FooterSegment } from "./types.js";
|
|
31
31
|
import { tpsTracker } from "./tps-tracker.js";
|
|
32
|
+
import { renderProcessLine } from "./process-line.js";
|
|
32
33
|
|
|
33
34
|
/** All segment groups */
|
|
34
35
|
const ALL_GROUPS: FooterGroup[] = [
|
|
@@ -328,9 +329,10 @@ function setupFooterUI(pi: ExtensionAPI, ctx: ExtensionContext, state: FooterSta
|
|
|
328
329
|
};
|
|
329
330
|
});
|
|
330
331
|
|
|
331
|
-
// Top row widget —
|
|
332
|
-
//
|
|
333
|
-
//
|
|
332
|
+
// Top row widget — dual role. Classic mode: status segment line. Glance
|
|
333
|
+
// mode: the bg-process one-liner, rendered directly above the glance frame
|
|
334
|
+
// (the frame replaces the editor, so this aboveEditor slot sits right above
|
|
335
|
+
// the footer); the frame's own borders show branch/context/model/thinking.
|
|
334
336
|
ctx.ui.setWidget("footer-top", (_tui, theme) => {
|
|
335
337
|
// Update the renderer's theme-like
|
|
336
338
|
const themeLike = { fg: (color: string, text: string) => theme.fg(color as any, text) };
|
|
@@ -344,8 +346,8 @@ function setupFooterUI(pi: ExtensionAPI, ctx: ExtensionContext, state: FooterSta
|
|
|
344
346
|
},
|
|
345
347
|
render(width: number): string[] {
|
|
346
348
|
if (!state.enabled || !state.piContext || width <= 0) return [];
|
|
347
|
-
// Glance mode
|
|
348
|
-
if (state.glanceMode) return
|
|
349
|
+
// Glance mode: this slot becomes the bg-process one-liner.
|
|
350
|
+
if (state.glanceMode) return renderProcessLine(width);
|
|
349
351
|
const layout = state.renderer.computeLayout(width);
|
|
350
352
|
if (!layout.topContent) return [];
|
|
351
353
|
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pi-unipi/footer — Background process one-liner
|
|
3
|
+
*
|
|
4
|
+
* Glance-mode strip rendered above the footer frame: one colored dot + count
|
|
5
|
+
* per background-task status. Reads DIRECTLY from the
|
|
6
|
+
* @pi-unipi/background-tasks shared registry (no events, no polling
|
|
7
|
+
* channels); re-renders on the footer's existing 1s refresh timer.
|
|
8
|
+
*
|
|
9
|
+
* Dot → status mapping:
|
|
10
|
+
* green ● running yellow ● stopped (killed) red ● failed gray ● done (completed)
|
|
11
|
+
*
|
|
12
|
+
* Buckets with zero count are omitted; with nothing in flight the line is
|
|
13
|
+
* empty so the footer stays clean.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
17
|
+
import { getSharedTaskRegistry } from "@pi-unipi/background-tasks";
|
|
18
|
+
|
|
19
|
+
const GREEN_DOT = "\x1b[38;5;82m●\x1b[0m"; // running — active work
|
|
20
|
+
const YELLOW_DOT = "\x1b[38;5;220m●\x1b[0m"; // stopped (killed) — needs attention
|
|
21
|
+
const RED_DOT = "\x1b[38;5;196m●\x1b[0m"; // failed — needs attention
|
|
22
|
+
const GRAY_DOT = "\x1b[38;5;245m●\x1b[0m"; // done (completed) — idle info
|
|
23
|
+
|
|
24
|
+
export interface BgProcessCounts {
|
|
25
|
+
running: number;
|
|
26
|
+
stopped: number;
|
|
27
|
+
failed: number;
|
|
28
|
+
done: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Count background tasks by display status straight from the registry.
|
|
33
|
+
* Returns null when background-tasks has not published a registry (module
|
|
34
|
+
* disabled, before first load, or after session shutdown).
|
|
35
|
+
*/
|
|
36
|
+
export function countBgProcesses(): BgProcessCounts | null {
|
|
37
|
+
try {
|
|
38
|
+
const tasks = getSharedTaskRegistry()?.allTasks();
|
|
39
|
+
if (!tasks) return null;
|
|
40
|
+
const counts: BgProcessCounts = { running: 0, stopped: 0, failed: 0, done: 0 };
|
|
41
|
+
for (const task of tasks) {
|
|
42
|
+
if (task.status === "running") counts.running++;
|
|
43
|
+
else if (task.status === "killed") counts.stopped++;
|
|
44
|
+
else if (task.status === "failed") counts.failed++;
|
|
45
|
+
else if (task.status === "completed") counts.done++;
|
|
46
|
+
}
|
|
47
|
+
return counts;
|
|
48
|
+
} catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Render the centered one-liner for the given terminal width.
|
|
55
|
+
* Returns [] when there is nothing to show.
|
|
56
|
+
*/
|
|
57
|
+
export function renderProcessLine(width: number): string[] {
|
|
58
|
+
if (width <= 0) return [];
|
|
59
|
+
const counts = countBgProcesses();
|
|
60
|
+
if (!counts) return [];
|
|
61
|
+
|
|
62
|
+
const parts: string[] = [];
|
|
63
|
+
if (counts.running > 0) parts.push(`${GREEN_DOT} ${counts.running} running`);
|
|
64
|
+
if (counts.stopped > 0) parts.push(`${YELLOW_DOT} ${counts.stopped} stopped`);
|
|
65
|
+
if (counts.failed > 0) parts.push(`${RED_DOT} ${counts.failed} failed`);
|
|
66
|
+
if (counts.done > 0) parts.push(`${GRAY_DOT} ${counts.done} done`);
|
|
67
|
+
if (parts.length === 0) return [];
|
|
68
|
+
|
|
69
|
+
const line = parts.join(" ");
|
|
70
|
+
const w = visibleWidth(line);
|
|
71
|
+
if (w >= width) return [truncateToWidth(line, width)];
|
|
72
|
+
const leftPad = Math.floor((width - w) / 2);
|
|
73
|
+
return [" ".repeat(leftPad) + line];
|
|
74
|
+
}
|
|
@@ -34743,6 +34743,18 @@ ${verified.answer}`), details };
|
|
|
34743
34743
|
});
|
|
34744
34744
|
}
|
|
34745
34745
|
|
|
34746
|
+
// packages/background-tasks/src/registry-shared.ts
|
|
34747
|
+
var SHARED_REGISTRY_KEY = /* @__PURE__ */ Symbol.for("unipi.background-tasks.shared-registry");
|
|
34748
|
+
function setSharedTaskRegistry(registry3) {
|
|
34749
|
+
globalThis[SHARED_REGISTRY_KEY] = registry3;
|
|
34750
|
+
}
|
|
34751
|
+
function getSharedTaskRegistry() {
|
|
34752
|
+
return globalThis[SHARED_REGISTRY_KEY];
|
|
34753
|
+
}
|
|
34754
|
+
function clearSharedTaskRegistry() {
|
|
34755
|
+
delete globalThis[SHARED_REGISTRY_KEY];
|
|
34756
|
+
}
|
|
34757
|
+
|
|
34746
34758
|
// packages/background-tasks/src/index.ts
|
|
34747
34759
|
init_types();
|
|
34748
34760
|
var STATUS_INTERVAL_MS2 = 1e3;
|
|
@@ -34771,6 +34783,7 @@ function backgroundTasksExtension(pi) {
|
|
|
34771
34783
|
eventService.publishTerminal(task);
|
|
34772
34784
|
}
|
|
34773
34785
|
});
|
|
34786
|
+
setSharedTaskRegistry(registry3);
|
|
34774
34787
|
const eventService = installBackgroundTaskExtensionApi({
|
|
34775
34788
|
events: pi.events,
|
|
34776
34789
|
registry: registry3,
|
|
@@ -34951,6 +34964,7 @@ ${task.outputPath}`, "info");
|
|
|
34951
34964
|
});
|
|
34952
34965
|
pi.on("session_start", async (_event, ctx) => {
|
|
34953
34966
|
registry3.setShuttingDown(false);
|
|
34967
|
+
setSharedTaskRegistry(registry3);
|
|
34954
34968
|
currentCtx = ctx;
|
|
34955
34969
|
await registry3.ensureRuntimeDir(ctx);
|
|
34956
34970
|
updateUi(ctx);
|
|
@@ -34961,6 +34975,7 @@ ${task.outputPath}`, "info");
|
|
|
34961
34975
|
});
|
|
34962
34976
|
pi.on("session_shutdown", async (_event, ctx) => {
|
|
34963
34977
|
registry3.setShuttingDown(true);
|
|
34978
|
+
clearSharedTaskRegistry();
|
|
34964
34979
|
currentCtx = void 0;
|
|
34965
34980
|
if (statusInterval) {
|
|
34966
34981
|
clearInterval(statusInterval);
|
|
@@ -51419,7 +51434,7 @@ function compactorExtension(pi) {
|
|
|
51419
51434
|
|
|
51420
51435
|
// packages/footer/src/index.ts
|
|
51421
51436
|
init_core();
|
|
51422
|
-
import { truncateToWidth as
|
|
51437
|
+
import { truncateToWidth as truncateToWidth23, visibleWidth as visibleWidth20 } from "@earendil-works/pi-tui";
|
|
51423
51438
|
|
|
51424
51439
|
// packages/footer/src/registry/index.ts
|
|
51425
51440
|
var FooterRegistry = class {
|
|
@@ -54880,6 +54895,45 @@ var STATUS_EXT_SEGMENTS = [
|
|
|
54880
54895
|
{ id: "extension_statuses", label: "Extensions", shortLabel: "EXT", description: "Extension statuses overview", zone: "center", render: renderExtensionStatusesSegment, defaultShow: true }
|
|
54881
54896
|
];
|
|
54882
54897
|
|
|
54898
|
+
// packages/footer/src/process-line.ts
|
|
54899
|
+
import { truncateToWidth as truncateToWidth22, visibleWidth as visibleWidth19 } from "@earendil-works/pi-tui";
|
|
54900
|
+
var GREEN_DOT2 = "\x1B[38;5;82m\u25CF\x1B[0m";
|
|
54901
|
+
var YELLOW_DOT = "\x1B[38;5;220m\u25CF\x1B[0m";
|
|
54902
|
+
var RED_DOT2 = "\x1B[38;5;196m\u25CF\x1B[0m";
|
|
54903
|
+
var GRAY_DOT = "\x1B[38;5;245m\u25CF\x1B[0m";
|
|
54904
|
+
function countBgProcesses() {
|
|
54905
|
+
try {
|
|
54906
|
+
const tasks = getSharedTaskRegistry()?.allTasks();
|
|
54907
|
+
if (!tasks) return null;
|
|
54908
|
+
const counts = { running: 0, stopped: 0, failed: 0, done: 0 };
|
|
54909
|
+
for (const task of tasks) {
|
|
54910
|
+
if (task.status === "running") counts.running++;
|
|
54911
|
+
else if (task.status === "killed") counts.stopped++;
|
|
54912
|
+
else if (task.status === "failed") counts.failed++;
|
|
54913
|
+
else if (task.status === "completed") counts.done++;
|
|
54914
|
+
}
|
|
54915
|
+
return counts;
|
|
54916
|
+
} catch {
|
|
54917
|
+
return null;
|
|
54918
|
+
}
|
|
54919
|
+
}
|
|
54920
|
+
function renderProcessLine(width) {
|
|
54921
|
+
if (width <= 0) return [];
|
|
54922
|
+
const counts = countBgProcesses();
|
|
54923
|
+
if (!counts) return [];
|
|
54924
|
+
const parts = [];
|
|
54925
|
+
if (counts.running > 0) parts.push(`${GREEN_DOT2} ${counts.running} running`);
|
|
54926
|
+
if (counts.stopped > 0) parts.push(`${YELLOW_DOT} ${counts.stopped} stopped`);
|
|
54927
|
+
if (counts.failed > 0) parts.push(`${RED_DOT2} ${counts.failed} failed`);
|
|
54928
|
+
if (counts.done > 0) parts.push(`${GRAY_DOT} ${counts.done} done`);
|
|
54929
|
+
if (parts.length === 0) return [];
|
|
54930
|
+
const line = parts.join(" ");
|
|
54931
|
+
const w = visibleWidth19(line);
|
|
54932
|
+
if (w >= width) return [truncateToWidth22(line, width)];
|
|
54933
|
+
const leftPad = Math.floor((width - w) / 2);
|
|
54934
|
+
return [" ".repeat(leftPad) + line];
|
|
54935
|
+
}
|
|
54936
|
+
|
|
54883
54937
|
// packages/footer/src/index.ts
|
|
54884
54938
|
var ALL_GROUPS = [
|
|
54885
54939
|
{ id: "core", name: "Core", segments: CORE_SEGMENTS, defaultShow: true },
|
|
@@ -55095,11 +55149,11 @@ function setupFooterUI(pi, ctx, state2) {
|
|
|
55095
55149
|
},
|
|
55096
55150
|
render(width) {
|
|
55097
55151
|
if (!state2.enabled || !state2.piContext || width <= 0) return [];
|
|
55098
|
-
if (state2.glanceMode) return
|
|
55152
|
+
if (state2.glanceMode) return renderProcessLine(width);
|
|
55099
55153
|
const layout = state2.renderer.computeLayout(width);
|
|
55100
55154
|
if (!layout.topContent) return [];
|
|
55101
55155
|
const line = layout.topContent;
|
|
55102
|
-
return [
|
|
55156
|
+
return [visibleWidth20(line) > width ? truncateToWidth23(line, width) : line];
|
|
55103
55157
|
}
|
|
55104
55158
|
};
|
|
55105
55159
|
}, { placement: "aboveEditor" });
|
|
@@ -55114,8 +55168,8 @@ function setupFooterUI(pi, ctx, state2) {
|
|
|
55114
55168
|
if (!state2.enabled || !state2.glanceMode || !state2.piContext || width <= 0) return [];
|
|
55115
55169
|
const strip = renderSessionStrip(state2.piContext);
|
|
55116
55170
|
if (!strip) return [];
|
|
55117
|
-
const w =
|
|
55118
|
-
if (w >= width) return [
|
|
55171
|
+
const w = visibleWidth20(strip);
|
|
55172
|
+
if (w >= width) return [truncateToWidth23(strip, width)];
|
|
55119
55173
|
const leftPad = Math.floor((width - w) / 2);
|
|
55120
55174
|
return [" ".repeat(leftPad) + strip];
|
|
55121
55175
|
}
|
|
@@ -55420,9 +55474,9 @@ function renderMarkdown(text, width, theme) {
|
|
|
55420
55474
|
|
|
55421
55475
|
// packages/updater/src/tui/list-detail-overlay.ts
|
|
55422
55476
|
init_core();
|
|
55423
|
-
import { Key as Key16, matchesKey as matchesKey22, truncateToWidth as
|
|
55477
|
+
import { Key as Key16, matchesKey as matchesKey22, truncateToWidth as truncateToWidth24, visibleWidth as visibleWidth21 } from "@earendil-works/pi-tui";
|
|
55424
55478
|
function padVisible2(content, targetWidth) {
|
|
55425
|
-
const vw =
|
|
55479
|
+
const vw = visibleWidth21(content);
|
|
55426
55480
|
const pad = Math.max(0, targetWidth - vw);
|
|
55427
55481
|
return content + " ".repeat(pad);
|
|
55428
55482
|
}
|
|
@@ -55463,7 +55517,7 @@ function createListDetailOverlay(config) {
|
|
|
55463
55517
|
lines.push(theme.fg("accent", `\u251C${"\u2500".repeat(innerWidth)}\u2524`));
|
|
55464
55518
|
const footer = state2.view === "list" ? config.listFooter : config.detailFooter;
|
|
55465
55519
|
lines.push(
|
|
55466
|
-
theme.fg("accent", "\u2502") + padVisible2(
|
|
55520
|
+
theme.fg("accent", "\u2502") + padVisible2(truncateToWidth24(footer, innerWidth), innerWidth) + theme.fg("accent", "\u2502")
|
|
55467
55521
|
);
|
|
55468
55522
|
lines.push(theme.fg("accent", `\u2570${"\u2500".repeat(innerWidth)}\u256F`));
|
|
55469
55523
|
return lines;
|
|
@@ -55490,7 +55544,7 @@ function createListDetailOverlay(config) {
|
|
|
55490
55544
|
const line = config.renderItem(entry, selected, theme);
|
|
55491
55545
|
lines.push(
|
|
55492
55546
|
theme.fg("accent", "\u2502") + padVisible2(
|
|
55493
|
-
selected ? theme.bg("selectedBg",
|
|
55547
|
+
selected ? theme.bg("selectedBg", truncateToWidth24(line, innerWidth)) : truncateToWidth24(line, innerWidth),
|
|
55494
55548
|
innerWidth
|
|
55495
55549
|
) + theme.fg("accent", "\u2502")
|
|
55496
55550
|
);
|
|
@@ -55506,7 +55560,7 @@ function createListDetailOverlay(config) {
|
|
|
55506
55560
|
}
|
|
55507
55561
|
const title = config.renderDetailTitle(entry, theme);
|
|
55508
55562
|
lines.push(
|
|
55509
|
-
theme.fg("accent", "\u2502") + padVisible2(
|
|
55563
|
+
theme.fg("accent", "\u2502") + padVisible2(truncateToWidth24(` ${title}`, innerWidth), innerWidth) + theme.fg("accent", "\u2502")
|
|
55510
55564
|
);
|
|
55511
55565
|
lines.push(
|
|
55512
55566
|
theme.fg("accent", "\u2502") + padVisible2("", innerWidth) + theme.fg("accent", "\u2502")
|
|
@@ -55518,7 +55572,7 @@ function createListDetailOverlay(config) {
|
|
|
55518
55572
|
const visible = bodyLines.slice(state2.detailScroll, state2.detailScroll + 15);
|
|
55519
55573
|
for (const line of visible) {
|
|
55520
55574
|
lines.push(
|
|
55521
|
-
theme.fg("accent", "\u2502") + padVisible2(
|
|
55575
|
+
theme.fg("accent", "\u2502") + padVisible2(truncateToWidth24(` ${line}`, innerWidth), innerWidth) + theme.fg("accent", "\u2502")
|
|
55522
55576
|
);
|
|
55523
55577
|
}
|
|
55524
55578
|
};
|
|
@@ -55777,7 +55831,7 @@ function renderChangelogOverlay() {
|
|
|
55777
55831
|
}
|
|
55778
55832
|
|
|
55779
55833
|
// packages/updater/src/tui/settings-overlay.ts
|
|
55780
|
-
import { Key as Key17, matchesKey as matchesKey23, truncateToWidth as
|
|
55834
|
+
import { Key as Key17, matchesKey as matchesKey23, truncateToWidth as truncateToWidth25 } from "@earendil-works/pi-tui";
|
|
55781
55835
|
|
|
55782
55836
|
// packages/updater/src/settings.ts
|
|
55783
55837
|
init_core();
|
|
@@ -55854,14 +55908,14 @@ function renderSettingsOverlay2() {
|
|
|
55854
55908
|
const modeOptions = getAutoUpdateOptions();
|
|
55855
55909
|
const render = (width) => {
|
|
55856
55910
|
const lines = [];
|
|
55857
|
-
lines.push(
|
|
55911
|
+
lines.push(truncateToWidth25(` ${BOLD}\u2699 Updater Settings${RESET2}`, width));
|
|
55858
55912
|
lines.push("\u2500".repeat(width));
|
|
55859
55913
|
lines.push("");
|
|
55860
55914
|
const intervalLabel = getIntervalLabel(state2.config.checkIntervalMs);
|
|
55861
55915
|
const row0Selected = state2.row === 0;
|
|
55862
55916
|
const row0Prefix = row0Selected ? `${TEAL}\u25B8${RESET2} ` : " ";
|
|
55863
55917
|
lines.push(
|
|
55864
|
-
|
|
55918
|
+
truncateToWidth25(
|
|
55865
55919
|
` ${row0Prefix}${BOLD}Check Interval${RESET2} ${DIM}${intervalLabel}${RESET2}`,
|
|
55866
55920
|
width
|
|
55867
55921
|
)
|
|
@@ -55870,13 +55924,13 @@ function renderSettingsOverlay2() {
|
|
|
55870
55924
|
const active = opt.ms === state2.config.checkIntervalMs;
|
|
55871
55925
|
return active ? `${GREEN}\u25CF ${opt.label}${RESET2}` : `${DIM}\u25CB ${opt.label}${RESET2}`;
|
|
55872
55926
|
}).join(" ");
|
|
55873
|
-
lines.push(
|
|
55927
|
+
lines.push(truncateToWidth25(` ${intervalLine}`, width));
|
|
55874
55928
|
lines.push("");
|
|
55875
55929
|
const modeLabel = state2.config.autoUpdate;
|
|
55876
55930
|
const row1Selected = state2.row === 1;
|
|
55877
55931
|
const row1Prefix = row1Selected ? `${TEAL}\u25B8${RESET2} ` : " ";
|
|
55878
55932
|
lines.push(
|
|
55879
|
-
|
|
55933
|
+
truncateToWidth25(
|
|
55880
55934
|
` ${row1Prefix}${BOLD}Auto Update${RESET2} ${DIM}${modeLabel}${RESET2}`,
|
|
55881
55935
|
width
|
|
55882
55936
|
)
|
|
@@ -55885,11 +55939,11 @@ function renderSettingsOverlay2() {
|
|
|
55885
55939
|
const active = mode === state2.config.autoUpdate;
|
|
55886
55940
|
return active ? `${GREEN}\u25CF ${mode}${RESET2}` : `${DIM}\u25CB ${mode}${RESET2}`;
|
|
55887
55941
|
}).join(" ");
|
|
55888
|
-
lines.push(
|
|
55942
|
+
lines.push(truncateToWidth25(` ${modeLine}`, width));
|
|
55889
55943
|
lines.push("");
|
|
55890
55944
|
lines.push("\u2500".repeat(width));
|
|
55891
55945
|
lines.push(
|
|
55892
|
-
|
|
55946
|
+
truncateToWidth25(
|
|
55893
55947
|
` j/k: navigate Space: cycle ${GREEN}Enter: save${RESET2} ${DIM}Esc: cancel${RESET2}`,
|
|
55894
55948
|
width
|
|
55895
55949
|
)
|
|
@@ -56127,7 +56181,7 @@ async function checkForUpdates() {
|
|
|
56127
56181
|
}
|
|
56128
56182
|
|
|
56129
56183
|
// packages/updater/src/tui/update-overlay.ts
|
|
56130
|
-
import { Key as Key18, matchesKey as matchesKey24, truncateToWidth as
|
|
56184
|
+
import { Key as Key18, matchesKey as matchesKey24, truncateToWidth as truncateToWidth26, visibleWidth as visibleWidth22 } from "@earendil-works/pi-tui";
|
|
56131
56185
|
|
|
56132
56186
|
// packages/updater/src/installer.ts
|
|
56133
56187
|
init_core();
|
|
@@ -56162,7 +56216,7 @@ async function installUpdate() {
|
|
|
56162
56216
|
// packages/updater/src/tui/update-overlay.ts
|
|
56163
56217
|
init_core();
|
|
56164
56218
|
function padVisible3(content, targetWidth) {
|
|
56165
|
-
const vw =
|
|
56219
|
+
const vw = visibleWidth22(content);
|
|
56166
56220
|
const pad = Math.max(0, targetWidth - vw);
|
|
56167
56221
|
return content + " ".repeat(pad);
|
|
56168
56222
|
}
|
|
@@ -56237,7 +56291,7 @@ function renderUpdateOverlay(checkResult) {
|
|
|
56237
56291
|
const current = theme.fg("muted", state2.result.currentVersion);
|
|
56238
56292
|
const latest = theme.fg("success", theme.bold(state2.result.latestVersion));
|
|
56239
56293
|
lines.push(
|
|
56240
|
-
theme.fg("accent", "\u2502") + padVisible3(
|
|
56294
|
+
theme.fg("accent", "\u2502") + padVisible3(truncateToWidth26(` ${current} \u2192 ${latest}`, innerWidth), innerWidth) + theme.fg("accent", "\u2502")
|
|
56241
56295
|
);
|
|
56242
56296
|
lines.push(
|
|
56243
56297
|
theme.fg("accent", "\u2502") + padVisible3("", innerWidth) + theme.fg("accent", "\u2502")
|
|
@@ -56250,7 +56304,7 @@ function renderUpdateOverlay(checkResult) {
|
|
|
56250
56304
|
for (let i = 0; i < contentHeight; i++) {
|
|
56251
56305
|
const line = visible[i] ?? "";
|
|
56252
56306
|
lines.push(
|
|
56253
|
-
theme.fg("accent", "\u2502") + padVisible3(
|
|
56307
|
+
theme.fg("accent", "\u2502") + padVisible3(truncateToWidth26(line, innerWidth), innerWidth) + theme.fg("accent", "\u2502")
|
|
56254
56308
|
);
|
|
56255
56309
|
}
|
|
56256
56310
|
lines.push(theme.fg("accent", `\u251C${"\u2500".repeat(innerWidth)}\u2524`));
|
|
@@ -56268,12 +56322,12 @@ function renderUpdateOverlay(checkResult) {
|
|
|
56268
56322
|
} else if (config.autoUpdate === "auto" && !state2.autoCancelled) {
|
|
56269
56323
|
const actionLine = ` ${theme.fg("success", "[Y]")} Update now ${theme.fg("muted", "[n]")} Cancel Auto-updating in ${theme.fg("warning", String(state2.autoCountdown))}...`;
|
|
56270
56324
|
lines.push(
|
|
56271
|
-
theme.fg("accent", "\u2502") + padVisible3(
|
|
56325
|
+
theme.fg("accent", "\u2502") + padVisible3(truncateToWidth26(actionLine, innerWidth), innerWidth) + theme.fg("accent", "\u2502")
|
|
56272
56326
|
);
|
|
56273
56327
|
} else {
|
|
56274
56328
|
const actionLine = ` ${theme.fg("success", "[Y]")} Update now ${theme.fg("muted", "[n]")} Skip ${theme.fg("accent", "j/k")}: scroll`;
|
|
56275
56329
|
lines.push(
|
|
56276
|
-
theme.fg("accent", "\u2502") + padVisible3(
|
|
56330
|
+
theme.fg("accent", "\u2502") + padVisible3(truncateToWidth26(actionLine, innerWidth), innerWidth) + theme.fg("accent", "\u2502")
|
|
56277
56331
|
);
|
|
56278
56332
|
}
|
|
56279
56333
|
lines.push(theme.fg("accent", `\u2570${"\u2500".repeat(innerWidth)}\u256F`));
|