pi-message-sidebar 1.5.0 → 1.6.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 +12 -0
- package/package.json +1 -1
- package/src/sidebar-component.ts +17 -11
- package/src/status-dock.ts +32 -21
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.6.0
|
|
4
|
+
|
|
5
|
+
> Readability pass driven by a live populated PTY audit: less cropping, more signal per row.
|
|
6
|
+
|
|
7
|
+
- Collapsed message previews now use up to two wrapped lines instead of one clipped line.
|
|
8
|
+
- Single header row: `Messages 5/5 · #5 01:26` replaces the two-row header.
|
|
9
|
+
- Goal recap shows the objective on its own line(s) again, with a second line when dock space allows.
|
|
10
|
+
- cmux row drops the label and leads with the surface ref (`surface:38 · title`), so operational identity never truncates away.
|
|
11
|
+
- Status dock is priority-ordered (identity, goal status, objective, runtime, hint) and degrades to goal-status-only before dropping the goal.
|
|
12
|
+
- Dock budget raised to five rows at normal heights; tiny-height behavior unchanged.
|
|
13
|
+
|
|
14
|
+
|
|
3
15
|
## 1.5.0
|
|
4
16
|
|
|
5
17
|
- Replaced selected/newest pinning with a row-aware contiguous chronological viewport.
|
package/package.json
CHANGED
package/src/sidebar-component.ts
CHANGED
|
@@ -134,7 +134,7 @@ export class SidebarComponent implements Component {
|
|
|
134
134
|
if (signature === this.cachedSignature) return this.cachedLines;
|
|
135
135
|
|
|
136
136
|
const fullHeader = this.renderHeader(safeWidth);
|
|
137
|
-
const headerRows = this.messages.length > 0 && targetHeight === 1 ? 0 :
|
|
137
|
+
const headerRows = this.messages.length > 0 && targetHeight === 1 ? 0 : 1;
|
|
138
138
|
const header = fullHeader.slice(0, Math.min(headerRows, targetHeight));
|
|
139
139
|
const available = Math.max(0, targetHeight - header.length);
|
|
140
140
|
// At tiny heights, keep one chronological message row before allocating dock chrome.
|
|
@@ -146,7 +146,7 @@ export class SidebarComponent implements Component {
|
|
|
146
146
|
this.options.getThinkingLevel(),
|
|
147
147
|
this.focused,
|
|
148
148
|
this.options.getCmuxContext,
|
|
149
|
-
Math.min(
|
|
149
|
+
Math.min(5, Math.max(0, available - messageReserve)),
|
|
150
150
|
);
|
|
151
151
|
const bodyHeight = Math.max(0, targetHeight - header.length - dock.length);
|
|
152
152
|
const result = [...header, ...this.renderBody(safeWidth, bodyHeight), ...dock];
|
|
@@ -165,14 +165,10 @@ export class SidebarComponent implements Component {
|
|
|
165
165
|
private renderHeader(width: number): string[] {
|
|
166
166
|
const position = this.selectedIndex() + 1;
|
|
167
167
|
const count = this.messages.length;
|
|
168
|
-
const location = count > 0 ? `${position}/${count}` : "0";
|
|
169
168
|
const selected = this.messages[this.selectedIndex()];
|
|
170
|
-
const detail = selected
|
|
171
|
-
? `${this.focused ? "Selected" : "Current"} #${selected.index} ${formatTime(selected.timestamp)}`
|
|
172
|
-
: "User prompts";
|
|
169
|
+
const detail = selected ? ` ${FG_FAINT}·${RST} ${FG_DIM}#${selected.index} ${formatTime(selected.timestamp)}${RST}` : "";
|
|
173
170
|
return [
|
|
174
|
-
fillRow(` ${BOLD}${FG_BRIGHT}Messages${RST} ${FG_FAINT}${
|
|
175
|
-
fillRow(` ${FG_DIM}${detail}${RST}`, width, BG),
|
|
171
|
+
fillRow(` ${BOLD}${FG_BRIGHT}Messages${RST} ${FG_FAINT}${count > 0 ? `${position}/${count}` : "0"}${RST}${detail}`, width, BG_HDR),
|
|
176
172
|
];
|
|
177
173
|
}
|
|
178
174
|
|
|
@@ -241,10 +237,20 @@ export class SidebarComponent implements Component {
|
|
|
241
237
|
const selected = message.id === this.selectedId;
|
|
242
238
|
const background = selected ? BG_SEL : BG;
|
|
243
239
|
const arrow = selected && this.focused ? `${FG_ACC}›${RST}` : " ";
|
|
244
|
-
if (!this.expandedIds.has(message.id)
|
|
240
|
+
if (!this.expandedIds.has(message.id)) {
|
|
241
|
+
// Collapsed preview: up to two wrapped lines so real prompts stay readable.
|
|
245
242
|
const prefix = ` ${arrow} `;
|
|
246
|
-
const
|
|
247
|
-
|
|
243
|
+
const textWidth = Math.max(1, width - visibleWidth(prefix));
|
|
244
|
+
const wrapped = wrapText(message.text, textWidth);
|
|
245
|
+
const rows = Math.max(1, Math.min(2, maxRows));
|
|
246
|
+
const shown = wrapped.slice(0, rows);
|
|
247
|
+
if (wrapped.length > shown.length && shown.length > 0) {
|
|
248
|
+
const overflow = shown[shown.length - 1]!;
|
|
249
|
+
shown[shown.length - 1] = truncateToWidth(overflow, Math.max(1, textWidth - 1), "") + "…";
|
|
250
|
+
}
|
|
251
|
+
return shown.map((line, position) =>
|
|
252
|
+
fillRow(`${position === 0 ? prefix : " "}${selected ? FG_BRIGHT : FG_NORM}${line}${RST}`, width, background),
|
|
253
|
+
);
|
|
248
254
|
}
|
|
249
255
|
|
|
250
256
|
const number = `${FG_FAINT}#${message.index}${RST}`;
|
package/src/status-dock.ts
CHANGED
|
@@ -99,7 +99,7 @@ function goalStatus(goal: ThreadGoal): { icon: string; color: string; label: str
|
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
function renderGoal(width: number, goal: ThreadGoal): string[] {
|
|
102
|
+
function renderGoal(width: number, goal: ThreadGoal, objectiveLines: number): string[] {
|
|
103
103
|
const status = goalStatus(goal);
|
|
104
104
|
const budget = goal.tokenBudget
|
|
105
105
|
? `${formatTokens(goal.usage.tokensUsed)}/${formatTokens(goal.tokenBudget)}`
|
|
@@ -108,7 +108,7 @@ function renderGoal(width: number, goal: ThreadGoal): string[] {
|
|
|
108
108
|
const metadata = `${status.color}${status.icon} ${status.label}${RST} ${overBudget ? FG_ERR : FG_MID}${budget}${RST} ${FG_FAINT}${formatDuration(goal.usage.activeSeconds)}${RST}`;
|
|
109
109
|
const objective = wrapText(goal.objective, Math.max(1, width - 4));
|
|
110
110
|
const lines = [fillRow(` ${metadata}`, width, BG)];
|
|
111
|
-
|
|
111
|
+
for (const line of objective.slice(0, Math.max(0, objectiveLines))) lines.push(fillRow(` ${FG_BRIGHT}${line}${RST}`, width, BG));
|
|
112
112
|
return lines;
|
|
113
113
|
}
|
|
114
114
|
|
|
@@ -124,15 +124,13 @@ function renderWorkspace(width: number, ctx: ExtensionContext, footerData: Reado
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
function renderCmux(width: number, cmux: CmuxContext): string {
|
|
127
|
-
|
|
128
|
-
const
|
|
129
|
-
const surfaceText = cmux.surfaceRef ?? "";
|
|
130
|
-
const surface = truncateToWidth(surfaceText, available, "");
|
|
127
|
+
// Operational identity first: the surface ref never truncates away.
|
|
128
|
+
const surface = cmux.surfaceRef ?? "";
|
|
131
129
|
const separator = surface ? ` ${FG_FAINT}·${RST} ` : "";
|
|
132
|
-
const
|
|
133
|
-
const
|
|
130
|
+
const titleText = cmux.workspaceTitle ?? cmux.workspaceRef ?? "";
|
|
131
|
+
const titleBudget = Math.max(0, width - 2 - visibleWidth(surface) - visibleWidth(separator));
|
|
134
132
|
const title = truncateToWidth(titleText, titleBudget, "…");
|
|
135
|
-
return fillRow(
|
|
133
|
+
return fillRow(` ${FG_INFO}${surface}${RST}${separator}${FG_BRIGHT}${title}${RST}`, width, BG);
|
|
136
134
|
}
|
|
137
135
|
|
|
138
136
|
function renderRuntime(width: number, ctx: ExtensionContext, footerData: ReadonlyFooterDataProvider | null, thinkingLevel: string, usage: Usage): string | null {
|
|
@@ -165,9 +163,9 @@ export function renderStatusDock(
|
|
|
165
163
|
thinkingLevel: string,
|
|
166
164
|
focused: boolean,
|
|
167
165
|
getCmuxContext: () => CmuxContext | null,
|
|
168
|
-
maxRows =
|
|
166
|
+
maxRows = 5,
|
|
169
167
|
): string[] {
|
|
170
|
-
const limit = Math.max(0, Math.min(
|
|
168
|
+
const limit = Math.max(0, Math.min(5, Math.floor(maxRows)));
|
|
171
169
|
if (limit === 0) return [];
|
|
172
170
|
const hint = fillRow(
|
|
173
171
|
` ${FG_DIM}${focused ? "↑↓ move Enter open c copy Esc close" : "Ctrl+Shift+H focus"}${RST}`,
|
|
@@ -179,18 +177,31 @@ export function renderStatusDock(
|
|
|
179
177
|
const usage = computeUsage(ctx);
|
|
180
178
|
const goal = readSessionGoal(ctx);
|
|
181
179
|
const cmux = getCmuxContext();
|
|
182
|
-
const rows: string[] = [];
|
|
183
180
|
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
181
|
+
// Priority order: identity, goal status, first objective line, runtime, then
|
|
182
|
+
// the second objective line. Truncation drops the least important rows last.
|
|
183
|
+
const rows: string[] = [];
|
|
184
|
+
const push = (line: string | string[] | null): boolean => {
|
|
185
|
+
if (!line) return true;
|
|
186
|
+
const added = Array.isArray(line) ? line : [line];
|
|
187
|
+
if (rows.length + added.length > limit - 1) return false;
|
|
188
|
+
rows.push(...added);
|
|
189
|
+
return true;
|
|
190
|
+
};
|
|
191
|
+
if (cmux) push(renderCmux(width, cmux));
|
|
192
|
+
else if (!goal) push(renderWorkspace(width, ctx, footerData));
|
|
193
|
+
if (goal) {
|
|
194
|
+
if (!push(renderGoal(width, goal, 1)) && rows.length + 1 <= limit - 1) {
|
|
195
|
+
// Degrade to goal status without the objective line before dropping it entirely.
|
|
196
|
+
rows.push(...renderGoal(width, goal, 0));
|
|
197
|
+
}
|
|
198
|
+
const runtime = renderRuntime(width, ctx, footerData, thinkingLevel, usage);
|
|
199
|
+
if (!push(runtime)) return [...rows.slice(0, limit - 1), hint];
|
|
200
|
+
push(renderGoal(width, goal, 2).slice(2));
|
|
201
|
+
} else {
|
|
202
|
+
push(renderRuntime(width, ctx, footerData, thinkingLevel, usage));
|
|
192
203
|
const status = validExtensionStatuses(footerData)[0];
|
|
193
|
-
if (status)
|
|
204
|
+
if (status) push(row(width, "stat", `${FG_MID}${status}${RST}`));
|
|
194
205
|
}
|
|
195
206
|
|
|
196
207
|
return [...rows.slice(0, limit - 1), hint];
|