Skip to content

Commit 6c4255f

Browse files
committed
Replace per-provider status bar splits with compact aggregate; add local-history disclaimers
- Status bar now shows single aggregated token count (e.g. 'PromptFuel: 15.5K local') - Tooltip includes 'Local history only', 'Live quota not enabled', 'Snapshots not included' - Dashboard shows disclaimer banner and 'Local history' overview labels - Refresh summary copy updated with '(local history)' suffix - Smoke tests updated for new wording and privacy assertions (106/106 pass)
1 parent 3f3a89f commit 6c4255f

5 files changed

Lines changed: 115 additions & 40 deletions

File tree

scripts/smoke-core.cjs

Lines changed: 71 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -129,14 +129,15 @@ test('formatStatusBarText: loaded state includes PromptFuel prefix', () => {
129129
assert.ok(t.startsWith('PromptFuel:'), `expected "PromptFuel:" prefix in "${t}"`);
130130
});
131131

132-
test('formatStatusBarText: loaded includes Claude label and compact tokens', () => {
132+
test('formatStatusBarText: loaded shows compact aggregate with local suffix', () => {
133133
const status = applyRefreshResults(
134134
createInitialStatus(['claude']),
135135
[{ providerId: 'claude', status: 'ok', totalTokens: 5000, totalAssistantMessages: 2, filesFound: 1 }],
136136
);
137137
const t = formatStatusBarText(status);
138-
assert.ok(t.includes('Claude'), `expected "Claude" in "${t}"`);
139138
assert.ok(t.includes('5.0K'), `expected "5.0K" in "${t}"`);
139+
assert.ok(t.includes('local'), `expected "local" suffix in "${t}"`);
140+
assert.ok(!t.includes(' | '), `should not include per-provider pipe separator in "${t}"`);
140141
});
141142

142143
test('formatStatusBarText: error/unknown returns refresh failed', () => {
@@ -148,7 +149,7 @@ test('formatStatusBarText: error/unknown returns refresh failed', () => {
148149
assert.strictEqual(t, 'PromptFuel: refresh failed');
149150
});
150151

151-
test('formatStatusBarText: mixed providers joined with pipe', () => {
152+
test('formatStatusBarText: mixed providers shows single aggregate', () => {
152153
const status = applyRefreshResults(
153154
createInitialStatus(['claude', 'codex']),
154155
[
@@ -158,12 +159,14 @@ test('formatStatusBarText: mixed providers joined with pipe', () => {
158159
);
159160
const t = formatStatusBarText(status);
160161
assert.ok(t.startsWith('PromptFuel:'), `expected "PromptFuel:" prefix in "${t}"`);
161-
assert.ok(t.includes(' | '), `expected " | " separator in "${t}"`);
162+
assert.ok(t.includes('5.0K'), `expected aggregate "5.0K" in "${t}"`);
163+
assert.ok(t.includes('local'), `expected "local" suffix in "${t}"`);
164+
assert.ok(!t.includes(' | '), `should not include per-provider pipe separator in "${t}"`);
162165
});
163166

164167
// === Status bar text: both loaded ===
165168

166-
test('formatStatusBarText: both loaded shows compact summary', () => {
169+
test('formatStatusBarText: both loaded shows single aggregate total', () => {
167170
const status = applyRefreshResults(
168171
createInitialStatus(['claude', 'codex']),
169172
[
@@ -172,10 +175,9 @@ test('formatStatusBarText: both loaded shows compact summary', () => {
172175
],
173176
);
174177
const t = formatStatusBarText(status);
175-
assert.ok(t.includes('Claude'), `expected "Claude" in "${t}"`);
176-
assert.ok(t.includes('12.4K'), `expected "12.4K" in "${t}"`);
177-
assert.ok(t.includes('Codex'), `expected "Codex" in "${t}"`);
178-
assert.ok(t.includes('3.1K'), `expected "3.1K" in "${t}"`);
178+
assert.ok(t.includes('15.5K'), `expected aggregate "15.5K" in "${t}"`);
179+
assert.ok(t.includes('local'), `expected "local" suffix in "${t}"`);
180+
assert.ok(!t.includes(' | '), `should not include per-provider pipe separator in "${t}"`);
179181
});
180182

181183
// === Status bar text: large token formatting ===
@@ -187,6 +189,7 @@ test('formatStatusBarText: large token counts use M suffix', () => {
187189
);
188190
const t = formatStatusBarText(status);
189191
assert.ok(t.includes('2.5M'), `expected "2.5M" in "${t}"`);
192+
assert.ok(t.includes('local'), `expected "local" suffix in "${t}"`);
190193
});
191194

192195
test('formatStatusBarText: small token counts show raw number', () => {
@@ -196,6 +199,7 @@ test('formatStatusBarText: small token counts show raw number', () => {
196199
);
197200
const t = formatStatusBarText(status);
198201
assert.ok(t.includes('500'), `expected "500" in "${t}"`);
202+
assert.ok(t.includes('local'), `expected "local" suffix in "${t}"`);
199203
});
200204

201205
// === Disabled provider omitted ===
@@ -236,11 +240,48 @@ test('formatTooltip: total tokens and messages shown for loaded providers', () =
236240
],
237241
);
238242
const tooltip = formatTooltip(status);
239-
assert.ok(tooltip.includes('Total:'), `expected "Total:" in tooltip`);
243+
assert.ok(tooltip.includes('Total local history:'), `expected "Total local history:" in tooltip`);
240244
assert.ok(tooltip.includes('15.0K'), `expected "15.0K" total in tooltip`);
241245
assert.ok(tooltip.includes('5 messages'), `expected "5 messages" total in tooltip`);
242246
});
243247

248+
// === Tooltip: local history disclaimers ===
249+
250+
test('formatTooltip: includes Local history only disclaimer', () => {
251+
const status = createInitialStatus(['claude']);
252+
const tooltip = formatTooltip(status);
253+
assert.ok(tooltip.includes('Local history only'), `expected "Local history only" in tooltip`);
254+
});
255+
256+
test('formatTooltip: includes Live quota not enabled disclaimer', () => {
257+
const status = createInitialStatus(['claude']);
258+
const tooltip = formatTooltip(status);
259+
assert.ok(tooltip.includes('Live quota not enabled yet'), `expected "Live quota not enabled yet" in tooltip`);
260+
});
261+
262+
test('formatTooltip: includes Snapshots not included disclaimer', () => {
263+
const status = createInitialStatus(['claude']);
264+
const tooltip = formatTooltip(status);
265+
assert.ok(tooltip.includes('Snapshots not included'), `expected "Snapshots not included" in tooltip`);
266+
});
267+
268+
// === Tooltip: provider splits still exist ===
269+
270+
test('formatTooltip: provider token splits still present', () => {
271+
const status = applyRefreshResults(
272+
createInitialStatus(['claude', 'codex']),
273+
[
274+
{ providerId: 'claude', status: 'ok', totalTokens: 10000, totalAssistantMessages: 3, filesFound: 2 },
275+
{ providerId: 'codex', status: 'ok', totalTokens: 5000, totalAssistantMessages: 2, filesFound: 1 },
276+
],
277+
);
278+
const tooltip = formatTooltip(status);
279+
assert.ok(tooltip.includes('Claude'), `expected "Claude" provider line in tooltip`);
280+
assert.ok(tooltip.includes('Codex'), `expected "Codex" provider line in tooltip`);
281+
assert.ok(tooltip.includes('10.0K'), `expected "10.0K" for Claude in tooltip`);
282+
assert.ok(tooltip.includes('5.0K'), `expected "5.0K" for Codex in tooltip`);
283+
});
284+
244285
// === formatTokenCount ===
245286

246287
test('formatTokenCount: M suffix for >= 1M', () => {
@@ -273,9 +314,9 @@ test('formatRefreshSummary: ok includes messages and tokens, no file paths', ()
273314
assert.ok(!s.includes('/'), `should not include slashes in "${s}"`);
274315
});
275316

276-
test('formatRefreshSummary: no-data shows no local usage', () => {
317+
test('formatRefreshSummary: no-data shows no local usage history', () => {
277318
const s = formatRefreshSummary([{ providerId: 'codex', status: 'no-data' }]);
278-
assert.ok(s.includes('no local usage'), `expected "no local usage" in "${s}"`);
319+
assert.ok(s.includes('no local usage history'), `expected "no local usage history" in "${s}"`);
279320
});
280321

281322
test('formatRefreshSummary: parse errors shown in summary', () => {
@@ -432,9 +473,27 @@ test('dashboard: uses local history wording, not subscription', () => {
432473
const mockWebview = { cspSource: 'http://example.com' };
433474
const html = buildDashboardHtml(mockWebview, model);
434475
assert.ok(html.includes('Local usage history'), `expected "Local usage history" subtitle`);
476+
assert.ok(html.includes('Local history tokens'), `expected "Local history tokens" overview label`);
477+
assert.ok(html.includes('Local history messages'), `expected "Local history messages" overview label`);
435478
assert.ok(!html.includes('subscription'), `should not include "subscription"`);
436479
});
437480

481+
test('dashboard: includes local history disclaimer banner', () => {
482+
const { buildDashboardHtml } = require(path.join(OUT, 'panel/dashboardHtml'));
483+
const status = applyRefreshResults(
484+
createInitialStatus(['claude']),
485+
[
486+
{ providerId: 'claude', status: 'ok', totalTokens: 5000, totalAssistantMessages: 2, filesFound: 1 },
487+
],
488+
);
489+
const model = buildDashboardModel(status);
490+
const mockWebview = { cspSource: 'http://example.com' };
491+
const html = buildDashboardHtml(mockWebview, model);
492+
assert.ok(html.includes('Local history only'), `expected "Local history only" disclaimer in HTML`);
493+
assert.ok(html.includes('live quota'), `expected "live quota" disclaimer in HTML`);
494+
assert.ok(html.includes('snapshots'), `expected "snapshots" disclaimer in HTML`);
495+
});
496+
438497
test('dashboard: no file paths or .jsonl in HTML', () => {
439498
const { buildDashboardHtml } = require(path.join(OUT, 'panel/dashboardHtml'));
440499
const status = applyRefreshResults(

scripts/smoke-providers.cjs

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -181,37 +181,36 @@ async function main() {
181181
assert.strictEqual(hasAnyError(updated), false);
182182
});
183183

184-
// ===== formatStatusBarText: compact status bar =====
184+
// ===== formatStatusBarText: compact aggregate status bar =====
185185

186186
await test('formatStatusBarText: all providers no-data shows no local usage', async () => {
187187
const status = createInitialStatus(['claude', 'codex']);
188188
const text = formatStatusBarText(status);
189189
assert.strictEqual(text, 'PromptFuel: no local usage');
190190
});
191191

192-
await test('formatStatusBarText: one provider loaded shows compact token count', async () => {
192+
await test('formatStatusBarText: one provider loaded shows aggregate with local suffix', async () => {
193193
const status = createInitialStatus(['claude', 'codex']);
194194
const updated = applyRefreshResults(status, [
195195
{ providerId: 'claude', status: 'ok', totalTokens: 12400, totalAssistantMessages: 5, filesFound: 3 },
196196
{ providerId: 'codex', status: 'not-found' },
197197
]);
198198
const text = formatStatusBarText(updated);
199-
assert.ok(text.includes('Claude'), `expected "Claude" in "${text}"`);
200199
assert.ok(text.includes('12.4K'), `expected "12.4K" in "${text}"`);
201-
assert.ok(text.includes('Codex'), `expected "Codex" in "${text}"`);
200+
assert.ok(text.includes('local'), `expected "local" in "${text}"`);
201+
assert.ok(!text.includes(' | '), `should not include pipe separator in "${text}"`);
202202
});
203203

204-
await test('formatStatusBarText: both providers loaded', async () => {
204+
await test('formatStatusBarText: both providers loaded shows single aggregate', async () => {
205205
const status = createInitialStatus(['claude', 'codex']);
206206
const updated = applyRefreshResults(status, [
207207
{ providerId: 'claude', status: 'ok', totalTokens: 12400, totalAssistantMessages: 5, filesFound: 3 },
208208
{ providerId: 'codex', status: 'ok', totalTokens: 3100, totalAssistantMessages: 2, filesFound: 1 },
209209
]);
210210
const text = formatStatusBarText(updated);
211-
assert.ok(text.includes('Claude'), `expected "Claude" in "${text}"`);
212-
assert.ok(text.includes('12.4K'), `expected "12.4K" in "${text}"`);
213-
assert.ok(text.includes('Codex'), `expected "Codex" in "${text}"`);
214-
assert.ok(text.includes('3.1K'), `expected "3.1K" in "${text}"`);
211+
assert.ok(text.includes('15.5K'), `expected aggregate "15.5K" in "${text}"`);
212+
assert.ok(text.includes('local'), `expected "local" in "${text}"`);
213+
assert.ok(!text.includes(' | '), `should not include pipe separator in "${text}"`);
215214
assert.ok(!text.includes('⛽'), `should not include emoji in "${text}"`);
216215
});
217216

@@ -224,23 +223,25 @@ async function main() {
224223
assert.strictEqual(text, 'PromptFuel: refresh failed');
225224
});
226225

227-
await test('formatStatusBarText: no-data shows dash when mixed with loaded', async () => {
226+
await test('formatStatusBarText: mixed loaded and no-data shows aggregate only', async () => {
228227
const status = createInitialStatus(['claude', 'codex']);
229228
const updated = applyRefreshResults(status, [
230229
{ providerId: 'claude', status: 'not-found' },
231230
{ providerId: 'codex', status: 'ok', totalTokens: 5000, totalAssistantMessages: 2, filesFound: 1 },
232231
]);
233232
const text = formatStatusBarText(updated);
234-
assert.ok(text.includes('—'), `expected dash for no-data in "${text}"`);
235-
assert.ok(text.includes('Codex'), `expected Codex in "${text}"`);
233+
assert.ok(text.includes('5.0K'), `expected "5.0K" in "${text}"`);
234+
assert.ok(text.includes('local'), `expected "local" in "${text}"`);
235+
assert.ok(!text.includes(' | '), `should not include pipe separator in "${text}"`);
236236
});
237237

238238
// ===== formatTooltip =====
239239

240-
await test('formatTooltip: initial state shows no local data', async () => {
240+
await test('formatTooltip: initial state shows local history disclaimers', async () => {
241241
const status = createInitialStatus(['claude', 'codex']);
242242
const tooltip = formatTooltip(status);
243243
assert.ok(tooltip.includes('PromptFuel'), `expected "PromptFuel" in tooltip`);
244+
assert.ok(tooltip.includes('Local history only'), `expected "Local history only" in tooltip`);
244245
assert.ok(tooltip.includes('no local data'), `expected "no local data" in tooltip`);
245246
assert.ok(!tooltip.includes('Last refreshed'), 'should not show refresh time before first refresh');
246247
});
@@ -344,10 +345,10 @@ async function main() {
344345
assert.ok(!s.includes('files'), `should not include files in "${s}"`);
345346
});
346347

347-
await test('formatRefreshSummary: no-data includes label and "no local usage"', async () => {
348+
await test('formatRefreshSummary: no-data includes label and "no local usage history"', async () => {
348349
const s = formatRefreshSummary([{ providerId: 'codex', status: 'no-data', filesFound: 0 }]);
349350
assert.ok(s.includes('Codex'), `expected "Codex" in "${s}"`);
350-
assert.ok(s.includes('no local usage'), `expected "no local usage" in "${s}"`);
351+
assert.ok(s.includes('no local usage history'), `expected "no local usage history" in "${s}"`);
351352
});
352353

353354
await test('formatRefreshSummary: error includes label and "read error"', async () => {

src/core/formatQuota.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,20 @@ export function formatStatusBarText(status: PromptFuelStatus): string {
1818
return 'PromptFuel: no local usage';
1919
}
2020

21-
const parts: string[] = [];
21+
let totalTokens = 0;
22+
let anyLoaded = false;
2223
for (const state of status.providerStates) {
23-
const label = PROVIDER_LABELS[state.providerId as ProviderId] ?? state.providerId;
24-
const text = formatProviderCompact(label, state);
25-
if (text) {
26-
parts.push(text);
24+
if (state.status === 'loaded' && (state.totalTokens ?? 0) > 0) {
25+
totalTokens += state.totalTokens ?? 0;
26+
anyLoaded = true;
2727
}
2828
}
2929

30-
if (parts.length === 0) {
30+
if (!anyLoaded) {
3131
return 'PromptFuel: no local usage';
3232
}
3333

34-
return `PromptFuel: ${parts.join(' | ')}`;
34+
return `PromptFuel: ${formatTokenCountCompact(totalTokens)} local`;
3535
}
3636

3737
function formatProviderCompact(
@@ -76,14 +76,14 @@ export function formatRefreshSummary(results: ReadResult[]): string {
7676
const msgs = r.totalAssistantMessages ?? 0;
7777
const tokens = r.totalTokens ?? 0;
7878
const parseErr = r.parseErrors ?? 0;
79-
let text = `${label}: ${msgs} messages, ${formatTokenCount(tokens)}`;
79+
let text = `${label}: ${msgs} messages, ${formatTokenCount(tokens)} (local history)`;
8080
if (parseErr > 0) {
81-
text += ` (${parseErr} parse error${parseErr !== 1 ? 's' : ''})`;
81+
text += `, ${parseErr} parse error${parseErr !== 1 ? 's' : ''}`;
8282
}
8383
return text;
8484
}
8585
case 'no-data':
86-
return `${label}: no local usage`;
86+
return `${label}: no local usage history`;
8787
case 'not-found':
8888
return `${label}: not found`;
8989
case 'error':

src/core/statusTooltip.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ export function formatTooltip(status: PromptFuelStatus): string {
88
const lines: string[] = [];
99

1010
lines.push('PromptFuel');
11+
lines.push('Local history only');
12+
lines.push('Live quota not enabled yet');
13+
lines.push('Snapshots not included');
14+
lines.push('');
1115

1216
let totalTokens = 0;
1317
let totalMessages = 0;
@@ -25,7 +29,7 @@ export function formatTooltip(status: PromptFuelStatus): string {
2529

2630
if (totalTokens > 0) {
2731
lines.push('');
28-
lines.push(`Total: ${formatTokenCount(totalTokens)} (${totalMessages} messages)`);
32+
lines.push(`Total local history: ${formatTokenCount(totalTokens)} (${totalMessages} messages)`);
2933
}
3034

3135
if (totalParseErrors > 0) {

src/panel/dashboardHtml.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,17 @@ export function buildDashboardHtml(
9595
.subtitle {
9696
font-size: 12px;
9797
color: var(--vscode-descriptionForeground, #999999);
98+
margin-bottom: 8px;
99+
}
100+
.disclaimer {
101+
font-size: 11px;
102+
color: var(--vscode-descriptionForeground, #999999);
103+
background: rgba(136,136,136,0.1);
104+
border: 1px solid var(--vscode-panel-border, rgba(128,128,128,0.3));
105+
border-radius: 4px;
106+
padding: 8px 12px;
98107
margin-bottom: 20px;
108+
line-height: 1.5;
99109
}
100110
.overview {
101111
background: var(--vscode-editor-background, #1e1e1e);
@@ -208,14 +218,15 @@ export function buildDashboardHtml(
208218
<div class="dashboard">
209219
<div class="title">PromptFuel</div>
210220
<div class="subtitle">Local usage history</div>
221+
<div class="disclaimer">Local history only — live quota and snapshots are not included yet.</div>
211222
212223
<div class="overview">
213224
<div class="overview-row">
214-
<span class="overview-label">Total tokens</span>
225+
<span class="overview-label">Local history tokens</span>
215226
<span class="overview-value">${esc(formatTokenCount(model.totalTokens))}</span>
216227
</div>
217228
<div class="overview-row">
218-
<span class="overview-label">Total messages</span>
229+
<span class="overview-label">Local history messages</span>
219230
<span class="overview-value">${esc(String(model.totalAssistantMessages))}</span>
220231
</div>
221232
</div>

0 commit comments

Comments
 (0)