@pi-unipi/updater 2.18.0 → 2.19.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/updater",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.19.0",
|
|
4
4
|
"description": "Auto-updater, changelog browser, and readme browser for Unipi — checks npm registry, renders CHANGELOG.md and README.md files in TUI overlays",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"access": "public"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@pi-unipi/core": "2.
|
|
37
|
+
"@pi-unipi/core": "2.19.0"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"@earendil-works/pi-coding-agent": "^0.84.0",
|
|
@@ -24,8 +24,8 @@ export function renderChangelogOverlay() {
|
|
|
24
24
|
return createListDetailOverlay<ChangelogEntry>({
|
|
25
25
|
title: " 📋 Changelog ",
|
|
26
26
|
emptyMessage: "No changelog available.",
|
|
27
|
-
listFooter: " j/k navigate Enter view details q/Esc close",
|
|
28
|
-
detailFooter: " j/k scroll q/Esc back to list",
|
|
27
|
+
listFooter: " ↑/↓ j/k navigate Enter view details q/Esc close",
|
|
28
|
+
detailFooter: " ↑/↓ j/k scroll q/Esc back to list",
|
|
29
29
|
|
|
30
30
|
loadEntries: () => {
|
|
31
31
|
const changelogPath = resolveChangelogPath();
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Used by both the changelog and readme browsers.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { Key, matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
8
|
+
import { Key, matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
9
9
|
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
10
10
|
import { boxInnerWidth } from "@pi-unipi/core";
|
|
11
11
|
|
|
@@ -168,18 +168,22 @@ export function createListDetailOverlay<T>(
|
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
const title = config.renderDetailTitle(entry, theme);
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
171
|
+
const wrapLine = (line: string) =>
|
|
172
|
+
visibleWidth(line) > innerWidth ? wrapTextWithAnsi(line, innerWidth) : [line];
|
|
173
|
+
for (const titleLine of wrapLine(` ${title}`)) {
|
|
174
|
+
lines.push(
|
|
175
|
+
theme.fg("accent", "│") +
|
|
176
|
+
padVisible(titleLine, innerWidth) +
|
|
177
|
+
theme.fg("accent", "│"),
|
|
178
|
+
);
|
|
179
|
+
}
|
|
176
180
|
lines.push(
|
|
177
181
|
theme.fg("accent", "│") +
|
|
178
182
|
padVisible("", innerWidth) +
|
|
179
183
|
theme.fg("accent", "│"),
|
|
180
184
|
);
|
|
181
185
|
|
|
182
|
-
const bodyLines = config.renderDetailBody(entry, innerWidth, theme);
|
|
186
|
+
const bodyLines = config.renderDetailBody(entry, innerWidth, theme).flatMap((line) => wrapLine(` ${line}`));
|
|
183
187
|
const maxScroll = Math.max(0, bodyLines.length - 15);
|
|
184
188
|
state.detailScroll = Math.min(state.detailScroll, maxScroll);
|
|
185
189
|
state.detailScroll = Math.max(0, state.detailScroll);
|
|
@@ -188,7 +192,7 @@ export function createListDetailOverlay<T>(
|
|
|
188
192
|
for (const line of visible) {
|
|
189
193
|
lines.push(
|
|
190
194
|
theme.fg("accent", "│") +
|
|
191
|
-
padVisible(
|
|
195
|
+
padVisible(line, innerWidth) +
|
|
192
196
|
theme.fg("accent", "│"),
|
|
193
197
|
);
|
|
194
198
|
}
|
|
@@ -28,8 +28,8 @@ export function renderReadmeOverlay(params?: ReadmeParams) {
|
|
|
28
28
|
return createListDetailOverlay<ReadmeEntry>({
|
|
29
29
|
title: " 📖 README Browser ",
|
|
30
30
|
emptyMessage: "No README files found.",
|
|
31
|
-
listFooter: " j/k navigate Enter read q/Esc close",
|
|
32
|
-
detailFooter: " j/k scroll q/Esc back to list",
|
|
31
|
+
listFooter: " ↑/↓ j/k navigate Enter read q/Esc close",
|
|
32
|
+
detailFooter: " ↑/↓ j/k scroll q/Esc back to list",
|
|
33
33
|
|
|
34
34
|
loadEntries: () => {
|
|
35
35
|
const entries = discoverReadmes();
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { join } from "path";
|
|
10
|
-
import { Key, matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
10
|
+
import { Key, matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
11
11
|
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
12
12
|
import { parseChangelog, getNewerVersions, resolveChangelogPath } from "../changelog.js";
|
|
13
13
|
import { renderMarkdown } from "../markdown.js";
|
|
@@ -28,7 +28,7 @@ function padVisible(content: string, targetWidth: number): string {
|
|
|
28
28
|
|
|
29
29
|
interface UpdateState {
|
|
30
30
|
result: UpdateCheckResult;
|
|
31
|
-
contentLines: string[];
|
|
31
|
+
contentLines: { width: number; lines: string[] } | null;
|
|
32
32
|
scroll: number;
|
|
33
33
|
installing: boolean;
|
|
34
34
|
installError: string | null;
|
|
@@ -61,27 +61,9 @@ export function renderUpdateOverlay(checkResult: UpdateCheckResult, providedNewe
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
// Build content lines from changelog using markdown renderer
|
|
65
|
-
const contentLines: string[] = [];
|
|
66
|
-
for (const entry of newerVersions) {
|
|
67
|
-
const title = entry.date
|
|
68
|
-
? `${theme.bold(entry.version)} — ${theme.fg("muted", entry.date)}`
|
|
69
|
-
: `${theme.bold(entry.version)} — ${theme.fg("muted", "Unreleased")}`;
|
|
70
|
-
contentLines.push(` ${title}`);
|
|
71
|
-
// Use markdown renderer for the body content
|
|
72
|
-
const bodyLines = renderMarkdown(entry.body, (tui.terminal?.columns ?? 80) - 6, theme);
|
|
73
|
-
for (const line of bodyLines) {
|
|
74
|
-
contentLines.push(` ${line}`);
|
|
75
|
-
}
|
|
76
|
-
contentLines.push("");
|
|
77
|
-
}
|
|
78
|
-
if (contentLines.length === 0) {
|
|
79
|
-
contentLines.push(` ${theme.fg("muted", `No changelog available for ${checkResult.latestVersion} (offline?).`)}`);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
64
|
const state: UpdateState = {
|
|
83
65
|
result: checkResult,
|
|
84
|
-
contentLines,
|
|
66
|
+
contentLines: null,
|
|
85
67
|
scroll: 0,
|
|
86
68
|
installing: false,
|
|
87
69
|
installError: null,
|
|
@@ -125,6 +107,29 @@ export function renderUpdateOverlay(checkResult: UpdateCheckResult, providedNewe
|
|
|
125
107
|
const innerWidth = boxInnerWidth(width);
|
|
126
108
|
const lines: string[] = [];
|
|
127
109
|
|
|
110
|
+
if (state.contentLines?.width !== innerWidth) {
|
|
111
|
+
const contentLines: string[] = [];
|
|
112
|
+
const wrapLine = (line: string) =>
|
|
113
|
+
visibleWidth(line) > innerWidth ? wrapTextWithAnsi(line, innerWidth) : [line];
|
|
114
|
+
for (const entry of newerVersions) {
|
|
115
|
+
const title = entry.date
|
|
116
|
+
? `${theme.bold(entry.version)} — ${theme.fg("muted", entry.date)}`
|
|
117
|
+
: `${theme.bold(entry.version)} — ${theme.fg("muted", "Unreleased")}`;
|
|
118
|
+
contentLines.push(...wrapLine(` ${title}`));
|
|
119
|
+
const bodyLines = renderMarkdown(entry.body, innerWidth - 2, theme);
|
|
120
|
+
for (const line of bodyLines) {
|
|
121
|
+
contentLines.push(...wrapLine(` ${line}`));
|
|
122
|
+
}
|
|
123
|
+
contentLines.push("");
|
|
124
|
+
}
|
|
125
|
+
if (contentLines.length === 0) {
|
|
126
|
+
contentLines.push(...wrapLine(` ${theme.fg("muted", `No changelog available for ${checkResult.latestVersion} (offline?).`)}`));
|
|
127
|
+
}
|
|
128
|
+
state.contentLines = { width: innerWidth, lines: contentLines };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const contentLines = state.contentLines.lines;
|
|
132
|
+
|
|
128
133
|
// ── Header ──────────────────────────────────────────────────────
|
|
129
134
|
lines.push(theme.fg("accent", `╭${"─".repeat(innerWidth)}╮`));
|
|
130
135
|
lines.push(
|
|
@@ -150,16 +155,16 @@ export function renderUpdateOverlay(checkResult: UpdateCheckResult, providedNewe
|
|
|
150
155
|
|
|
151
156
|
// ── Changelog content (scrollable) ──────────────────────────────
|
|
152
157
|
const contentHeight = 12;
|
|
153
|
-
const maxScroll = Math.max(0,
|
|
158
|
+
const maxScroll = Math.max(0, contentLines.length - contentHeight);
|
|
154
159
|
state.scroll = Math.min(state.scroll, maxScroll);
|
|
155
160
|
state.scroll = Math.max(0, state.scroll);
|
|
156
161
|
|
|
157
|
-
const visible =
|
|
162
|
+
const visible = contentLines.slice(state.scroll, state.scroll + contentHeight);
|
|
158
163
|
for (let i = 0; i < contentHeight; i++) {
|
|
159
164
|
const line = visible[i] ?? "";
|
|
160
165
|
lines.push(
|
|
161
166
|
theme.fg("accent", "│") +
|
|
162
|
-
padVisible(
|
|
167
|
+
padVisible(line, innerWidth) +
|
|
163
168
|
theme.fg("accent", "│"),
|
|
164
169
|
);
|
|
165
170
|
}
|
|
@@ -192,7 +197,7 @@ export function renderUpdateOverlay(checkResult: UpdateCheckResult, providedNewe
|
|
|
192
197
|
theme.fg("accent", "│"),
|
|
193
198
|
);
|
|
194
199
|
} else {
|
|
195
|
-
const actionLine = ` ${theme.fg("success", "[Y]")} Update now ${theme.fg("muted", "[n]")} Skip ${theme.fg("accent", "j/k")}: scroll`;
|
|
200
|
+
const actionLine = ` ${theme.fg("success", "[Y]")} Update now ${theme.fg("muted", "[n]")} Skip ${theme.fg("accent", "↑/↓ j/k")}: scroll`;
|
|
196
201
|
lines.push(
|
|
197
202
|
theme.fg("accent", "│") +
|
|
198
203
|
padVisible(truncateToWidth(actionLine, innerWidth), innerWidth) +
|