@henryqw/pi-deps 0.2.0 → 0.3.1

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.
Files changed (2) hide show
  1. package/extensions/deps.ts +35 -3
  2. package/package.json +4 -3
@@ -2,7 +2,8 @@ import { randomUUID } from "node:crypto";
2
2
  import { link, mkdir, readFile, rename, rm, writeFile, chmod } from "node:fs/promises";
3
3
  import { dirname, join, resolve } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
5
+ import { type ExtensionAPI, type ExtensionUIContext, type Theme } from "@earendil-works/pi-coding-agent";
6
+ import { type Component, truncateToWidth, type TUI } from "@earendil-works/pi-tui";
6
7
 
7
8
  const hookSourcePath = fileURLToPath(new URL("../hooks/post-checkout.mjs", import.meta.url));
8
9
  const managedHookMarker = "pi-deps-managed-hook";
@@ -67,6 +68,14 @@ const widgetKey = "pi-deps";
67
68
  const pollMs = 500;
68
69
  const successTtlMs = 5000;
69
70
  const waitTimeoutMs = 10 * 60_000;
71
+ const spinnerIntervalMs = 100;
72
+ const spinnerFrames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
73
+
74
+ function formatElapsed(startedAt: number, now = Date.now()): string {
75
+ const seconds = Math.max(0, Math.floor((now - startedAt) / 1_000));
76
+ const minutes = Math.floor(seconds / 60);
77
+ return minutes ? `${minutes}m ${seconds % 60}s` : `${seconds}s`;
78
+ }
70
79
 
71
80
  interface InstallStatus {
72
81
  state?: string;
@@ -76,7 +85,7 @@ interface InstallStatus {
76
85
  export interface InstallWatchContext {
77
86
  cwd: string;
78
87
  mode?: string;
79
- ui: { setWidget(key: string, content: string[] | undefined): void };
88
+ ui: Pick<ExtensionUIContext, "setWidget">;
80
89
  }
81
90
 
82
91
  // Watches the status file written by the background installer spawned by the post-checkout hook.
@@ -102,7 +111,30 @@ export async function watchDependencyInstallation(
102
111
 
103
112
  let status = await readStatus();
104
113
  if (!status) return;
105
- if (status.state === "running") ctx.ui.setWidget(widgetKey, ["pi-deps: installing dependencies…"]);
114
+ if (status.state === "running") {
115
+ const startedAt = Date.now();
116
+ let timer: ReturnType<typeof setInterval> | undefined;
117
+ ctx.ui.setWidget(widgetKey, (tui: TUI, theme: Theme): Component & { dispose?(): void } => {
118
+ timer ??= setInterval(() => tui.requestRender(), spinnerIntervalMs);
119
+ timer.unref();
120
+ return {
121
+ invalidate() {},
122
+ dispose() {
123
+ if (timer) clearInterval(timer);
124
+ timer = undefined;
125
+ },
126
+ render: (width: number) => {
127
+ const frame = spinnerFrames[Math.floor(Date.now() / spinnerIntervalMs) % spinnerFrames.length]!;
128
+ return [
129
+ truncateToWidth(
130
+ `${theme.fg("accent", frame)} pi-deps: installing dependencies… ${theme.fg("dim", formatElapsed(startedAt))}`,
131
+ width,
132
+ ),
133
+ ];
134
+ },
135
+ };
136
+ });
137
+ }
106
138
  const deadline = Date.now() + waitTimeoutMs;
107
139
  while (status.state === "running" && Date.now() < deadline) {
108
140
  await new Promise((resolveSleep) => setTimeout(resolveSleep, timing.pollMs ?? pollMs));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@henryqw/pi-deps",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Prepare Node and uv dependencies when opted-in Git worktrees are created.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -22,11 +22,12 @@
22
22
  ],
23
23
  "scripts": {
24
24
  "test": "node --test test/*.test.ts",
25
- "typecheck": "tsc --noEmit --allowImportingTsExtensions --target ES2022 --module NodeNext --moduleResolution NodeNext --skipLibCheck extensions/deps.ts test/*.test.ts",
25
+ "typecheck": "tsc --noEmit --allowImportingTsExtensions --target ES2022 --module NodeNext --moduleResolution NodeNext --skipLibCheck extensions/deps.ts",
26
26
  "pack:check": "npm pack --dry-run"
27
27
  },
28
28
  "peerDependencies": {
29
- "@earendil-works/pi-coding-agent": "*"
29
+ "@earendil-works/pi-coding-agent": "*",
30
+ "@earendil-works/pi-tui": "*"
30
31
  },
31
32
  "repository": {
32
33
  "type": "git",