@leo000001/opencode-quota-sidebar 1.10.0 → 1.12.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/README.md +111 -0
- package/dist/format.js +64 -4
- package/dist/index.js +0 -1
- package/dist/providers/core/copilot.js +7 -3
- package/dist/storage.js +2 -0
- package/dist/title.js +54 -17
- package/dist/title_apply.d.ts +0 -3
- package/dist/title_apply.js +1 -1
- package/dist/types.d.ts +6 -0
- package/package.json +1 -1
- package/quota-sidebar.config.example.json +2 -0
package/README.md
CHANGED
|
@@ -164,6 +164,38 @@ Resolution order (low -> high):
|
|
|
164
164
|
|
|
165
165
|
Values are layered; later sources override earlier ones.
|
|
166
166
|
|
|
167
|
+
## Defaults
|
|
168
|
+
|
|
169
|
+
If you do not provide any config file, the plugin uses the built-in defaults below.
|
|
170
|
+
|
|
171
|
+
Sidebar defaults:
|
|
172
|
+
|
|
173
|
+
- `sidebar.enabled`: `true`
|
|
174
|
+
- `sidebar.width`: `36` (clamped to `20`-`60`)
|
|
175
|
+
- `sidebar.multilineTitle`: `true`
|
|
176
|
+
- `sidebar.showCost`: `true`
|
|
177
|
+
- `sidebar.showQuota`: `true`
|
|
178
|
+
- `sidebar.wrapQuotaLines`: `true`
|
|
179
|
+
- `sidebar.includeChildren`: `true`
|
|
180
|
+
- `sidebar.childrenMaxDepth`: `6` (clamped to `1`-`32`)
|
|
181
|
+
- `sidebar.childrenMaxSessions`: `128` (clamped to `0`-`2000`)
|
|
182
|
+
- `sidebar.childrenConcurrency`: `5` (clamped to `1`-`10`)
|
|
183
|
+
|
|
184
|
+
Quota defaults:
|
|
185
|
+
|
|
186
|
+
- `quota.refreshMs`: `300000` (clamped to `>=30000`)
|
|
187
|
+
- `quota.includeOpenAI`: `true`
|
|
188
|
+
- `quota.includeCopilot`: `true`
|
|
189
|
+
- `quota.includeAnthropic`: `true`
|
|
190
|
+
- `quota.providers`: `{}` (per-adapter switches, for example `rightcode.enabled`)
|
|
191
|
+
- `quota.refreshAccessToken`: `false`
|
|
192
|
+
- `quota.requestTimeoutMs`: `8000` (clamped to `>=1000`)
|
|
193
|
+
|
|
194
|
+
Other defaults:
|
|
195
|
+
|
|
196
|
+
- `toast.durationMs`: `12000` (clamped to `>=1000`)
|
|
197
|
+
- `retentionDays`: `730`
|
|
198
|
+
|
|
167
199
|
Example config:
|
|
168
200
|
|
|
169
201
|
```json
|
|
@@ -171,8 +203,10 @@ Example config:
|
|
|
171
203
|
"sidebar": {
|
|
172
204
|
"enabled": true,
|
|
173
205
|
"width": 36,
|
|
206
|
+
"multilineTitle": true,
|
|
174
207
|
"showCost": true,
|
|
175
208
|
"showQuota": true,
|
|
209
|
+
"wrapQuotaLines": true,
|
|
176
210
|
"includeChildren": true,
|
|
177
211
|
"childrenMaxDepth": 6,
|
|
178
212
|
"childrenMaxSessions": 128,
|
|
@@ -202,6 +236,8 @@ Notes:
|
|
|
202
236
|
|
|
203
237
|
- `sidebar.showCost` controls API-cost visibility in sidebar title, `quota_summary` markdown report, and toast message.
|
|
204
238
|
- `sidebar.width` is measured in terminal cells. CJK/emoji truncation is best-effort to avoid sidebar overflow.
|
|
239
|
+
- `sidebar.multilineTitle` controls multi-line sidebar layout (default: `true`). Set `false` for compact single-line title.
|
|
240
|
+
- `sidebar.wrapQuotaLines` controls quota line wrapping and continuation indentation (default: `true`).
|
|
205
241
|
- `sidebar.includeChildren` controls whether session-scoped usage/quota includes descendant subagent sessions (default: `true`).
|
|
206
242
|
- `sidebar.childrenMaxDepth` limits how many levels of nested subagents are traversed (default: `6`, clamped 1–32).
|
|
207
243
|
- `sidebar.childrenMaxSessions` caps the total number of descendant sessions aggregated (default: `128`, clamped 0–2000).
|
|
@@ -211,6 +247,81 @@ Notes:
|
|
|
211
247
|
- `quota.providers` is the extensible per-adapter switch map.
|
|
212
248
|
- If API Cost is `$0.00`, it usually means the model/provider has no pricing mapping in OpenCode at the moment, so equivalent API cost cannot be estimated.
|
|
213
249
|
|
|
250
|
+
## Rendering examples
|
|
251
|
+
|
|
252
|
+
These examples show the quota block portion of the sidebar title.
|
|
253
|
+
|
|
254
|
+
### `sidebar.multilineTitle=true`
|
|
255
|
+
|
|
256
|
+
0 providers (no quota data):
|
|
257
|
+
|
|
258
|
+
```text
|
|
259
|
+
(no quota block)
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
1 provider, 1 window (fits):
|
|
263
|
+
|
|
264
|
+
```text
|
|
265
|
+
Copilot Monthly 78% Rst 04-01
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
1 provider, multi-window (for example OpenAI 5h + Weekly):
|
|
269
|
+
|
|
270
|
+
```text
|
|
271
|
+
OpenAI
|
|
272
|
+
5h 78% Rst 05:05
|
|
273
|
+
Weekly 73% Rst 03-12
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
2+ providers (even if each provider is single-window):
|
|
277
|
+
|
|
278
|
+
```text
|
|
279
|
+
OpenAI
|
|
280
|
+
5h 78% Rst 05:05
|
|
281
|
+
Copilot
|
|
282
|
+
Monthly 78% Rst 04-01
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
2+ providers mixed (multi-window + single-window):
|
|
286
|
+
|
|
287
|
+
```text
|
|
288
|
+
OpenAI
|
|
289
|
+
5h 78% Rst 05:05
|
|
290
|
+
Weekly 73% Rst 03-12
|
|
291
|
+
Copilot
|
|
292
|
+
Monthly 78% Rst 04-01
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Balance-style quota:
|
|
296
|
+
|
|
297
|
+
```text
|
|
298
|
+
RC Balance $260
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Multi-detail quota (window + balance):
|
|
302
|
+
|
|
303
|
+
```text
|
|
304
|
+
RC
|
|
305
|
+
Daily $88.9/$60 Exp 02-27
|
|
306
|
+
Balance $260
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Provider status (examples):
|
|
310
|
+
|
|
311
|
+
```text
|
|
312
|
+
Anthropic unsupported
|
|
313
|
+
Copilot unavailable
|
|
314
|
+
OpenAI Remaining ?
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### `sidebar.multilineTitle=false`
|
|
318
|
+
|
|
319
|
+
Quota is rendered inline as part of a single-line title:
|
|
320
|
+
|
|
321
|
+
```text
|
|
322
|
+
<base> | Input ... | Output ... | OpenAI 5h 78%+ | Copilot Monthly 78% | ...
|
|
323
|
+
```
|
|
324
|
+
|
|
214
325
|
`quota_summary` also supports an optional `includeChildren` flag (only effective for `period=session`) to override the config per call. For `day`/`week`/`month` periods, children are never merged — each session is counted independently.
|
|
215
326
|
|
|
216
327
|
## Debug logging
|
package/dist/format.js
CHANGED
|
@@ -162,6 +162,56 @@ function alignPairs(pairs, indent = ' ') {
|
|
|
162
162
|
return `${indent}${padEndCells(pair.label, labelWidth)} ${pair.value}`;
|
|
163
163
|
});
|
|
164
164
|
}
|
|
165
|
+
function compactQuotaInline(quota) {
|
|
166
|
+
const label = sanitizeLine(quotaDisplayLabel(quota));
|
|
167
|
+
if (quota.status !== 'ok')
|
|
168
|
+
return label;
|
|
169
|
+
if (quota.windows && quota.windows.length > 0) {
|
|
170
|
+
const first = quota.windows[0];
|
|
171
|
+
const showPercent = first.showPercent !== false;
|
|
172
|
+
const firstLabel = sanitizeLine(first.label || '');
|
|
173
|
+
const pct = first.remainingPercent === undefined
|
|
174
|
+
? undefined
|
|
175
|
+
: `${Math.round(first.remainingPercent)}%`;
|
|
176
|
+
const summary = showPercent
|
|
177
|
+
? [firstLabel, pct].filter(Boolean).join(' ')
|
|
178
|
+
: firstLabel.replace(/^Daily\s+/i, '') || firstLabel;
|
|
179
|
+
const hasMore = quota.windows.length > 1 ||
|
|
180
|
+
(quota.balance !== undefined && !summary.includes('Balance '));
|
|
181
|
+
return `${label}${summary ? ` ${summary}` : ''}${hasMore ? '+' : ''}`;
|
|
182
|
+
}
|
|
183
|
+
if (quota.balance) {
|
|
184
|
+
return `${label} ${formatCurrency(quota.balance.amount, quota.balance.currency)}`;
|
|
185
|
+
}
|
|
186
|
+
if (quota.remainingPercent !== undefined) {
|
|
187
|
+
return `${label} ${Math.round(quota.remainingPercent)}%`;
|
|
188
|
+
}
|
|
189
|
+
return label;
|
|
190
|
+
}
|
|
191
|
+
function renderSingleLineTitle(baseTitle, usage, quotas, config, width) {
|
|
192
|
+
const baseBudget = Math.min(16, Math.max(8, Math.floor(width * 0.35)));
|
|
193
|
+
const base = fitLine(baseTitle, baseBudget);
|
|
194
|
+
const segments = [
|
|
195
|
+
`Input ${sidebarNumber(usage.input)} Output ${sidebarNumber(usage.output)}`,
|
|
196
|
+
];
|
|
197
|
+
if (usage.cacheRead > 0) {
|
|
198
|
+
segments.push(`Cache Read ${sidebarNumber(usage.cacheRead)}`);
|
|
199
|
+
}
|
|
200
|
+
if (usage.cacheWrite > 0) {
|
|
201
|
+
segments.push(`Cache Write ${sidebarNumber(usage.cacheWrite)}`);
|
|
202
|
+
}
|
|
203
|
+
if (config.sidebar.showCost && usage.apiCost > 0) {
|
|
204
|
+
segments.push(formatApiCostLine(usage.apiCost));
|
|
205
|
+
}
|
|
206
|
+
if (config.sidebar.showQuota) {
|
|
207
|
+
const visibleQuotas = collapseQuotaSnapshots(quotas).filter((q) => ['ok', 'error', 'unsupported', 'unavailable'].includes(q.status));
|
|
208
|
+
segments.push(...visibleQuotas.map(compactQuotaInline));
|
|
209
|
+
}
|
|
210
|
+
const detail = segments.filter(Boolean).join(' | ');
|
|
211
|
+
if (!detail)
|
|
212
|
+
return fitLine(baseTitle, width);
|
|
213
|
+
return fitLine(`${base} | ${detail}`, width);
|
|
214
|
+
}
|
|
165
215
|
/**
|
|
166
216
|
* Render sidebar title with multi-line token breakdown.
|
|
167
217
|
*
|
|
@@ -175,8 +225,11 @@ function alignPairs(pairs, indent = ' ') {
|
|
|
175
225
|
*/
|
|
176
226
|
export function renderSidebarTitle(baseTitle, usage, quotas, config) {
|
|
177
227
|
const width = Math.max(8, Math.floor(config.sidebar.width || 36));
|
|
178
|
-
const lines = [];
|
|
179
228
|
const safeBaseTitle = stripAnsi(baseTitle || 'Session').split(/\r?\n/, 1)[0] || 'Session';
|
|
229
|
+
if (config.sidebar.multilineTitle !== true) {
|
|
230
|
+
return renderSingleLineTitle(safeBaseTitle, usage, quotas, config, width);
|
|
231
|
+
}
|
|
232
|
+
const lines = [];
|
|
180
233
|
lines.push(fitLine(safeBaseTitle, width));
|
|
181
234
|
lines.push('');
|
|
182
235
|
// Input / Output line
|
|
@@ -195,6 +248,9 @@ export function renderSidebarTitle(baseTitle, usage, quotas, config) {
|
|
|
195
248
|
// Quota lines (one provider per line for stable wrapping)
|
|
196
249
|
if (config.sidebar.showQuota) {
|
|
197
250
|
const visibleQuotas = collapseQuotaSnapshots(quotas).filter((q) => ['ok', 'error', 'unsupported', 'unavailable'].includes(q.status));
|
|
251
|
+
// When multiple providers are visible, keep a consistent visual rhythm by
|
|
252
|
+
// always rendering each provider as a header line + indented detail line(s).
|
|
253
|
+
const forceWrappedProviders = visibleQuotas.length > 1;
|
|
198
254
|
const labelWidth = visibleQuotas.reduce((max, item) => {
|
|
199
255
|
const label = sanitizeLine(quotaDisplayLabel(item));
|
|
200
256
|
return Math.max(max, stringCellWidth(label));
|
|
@@ -203,6 +259,7 @@ export function renderSidebarTitle(baseTitle, usage, quotas, config) {
|
|
|
203
259
|
.flatMap((item) => compactQuotaWide(item, labelWidth, {
|
|
204
260
|
width,
|
|
205
261
|
wrapLines: config.sidebar.wrapQuotaLines,
|
|
262
|
+
forceWrapped: forceWrappedProviders,
|
|
206
263
|
}))
|
|
207
264
|
.filter((s) => Boolean(s));
|
|
208
265
|
if (quotaItems.length > 0) {
|
|
@@ -237,19 +294,22 @@ function compactQuotaWide(quota, labelWidth = 0, options) {
|
|
|
237
294
|
const withLabel = (content) => `${labelPadded} ${content}`;
|
|
238
295
|
const wrap = options?.wrapLines === true && (options?.width || 0) > 0;
|
|
239
296
|
const width = options?.width || 0;
|
|
297
|
+
const forceWrapped = options?.forceWrapped === true;
|
|
240
298
|
/** If inline version overflows, break into label-line + indented detail lines. */
|
|
241
299
|
const maybeBreak = (inlineText, detailLines) => {
|
|
242
300
|
const inline = withLabel(inlineText);
|
|
301
|
+
if (forceWrapped)
|
|
302
|
+
return [label, ...detailLines.map((d) => `${detailIndent}${d}`)];
|
|
243
303
|
if (!wrap || stringCellWidth(inline) <= width)
|
|
244
304
|
return [inline];
|
|
245
305
|
return [label, ...detailLines.map((d) => `${detailIndent}${d}`)];
|
|
246
306
|
};
|
|
247
307
|
if (quota.status === 'error')
|
|
248
|
-
return
|
|
308
|
+
return maybeBreak('Remaining ?', ['Remaining ?']);
|
|
249
309
|
if (quota.status === 'unsupported')
|
|
250
|
-
return
|
|
310
|
+
return maybeBreak('unsupported', ['unsupported']);
|
|
251
311
|
if (quota.status === 'unavailable')
|
|
252
|
-
return
|
|
312
|
+
return maybeBreak('unavailable', ['unavailable']);
|
|
253
313
|
if (quota.status !== 'ok')
|
|
254
314
|
return [];
|
|
255
315
|
const balanceText = quota.balance
|
package/dist/index.js
CHANGED
|
@@ -2,11 +2,15 @@ import { isRecord, swallow } from '../../helpers.js';
|
|
|
2
2
|
import { asNumber, configuredProviderEnabled, fetchWithTimeout, normalizePercent, toIso, } from '../common.js';
|
|
3
3
|
async function fetchCopilotQuota(ctx) {
|
|
4
4
|
const checkedAt = Date.now();
|
|
5
|
+
const sourceProviderID = typeof ctx.sourceProviderID === 'string' && ctx.sourceProviderID
|
|
6
|
+
? ctx.sourceProviderID
|
|
7
|
+
: ctx.providerID;
|
|
8
|
+
const enterprise = sourceProviderID.startsWith('github-copilot-enterprise');
|
|
5
9
|
const base = {
|
|
6
|
-
providerID:
|
|
10
|
+
providerID: sourceProviderID,
|
|
7
11
|
adapterID: 'github-copilot',
|
|
8
|
-
label: 'GitHub Copilot',
|
|
9
|
-
shortLabel: 'Copilot',
|
|
12
|
+
label: enterprise ? 'GitHub Copilot Enterprise' : 'GitHub Copilot',
|
|
13
|
+
shortLabel: enterprise ? 'Copilot Ent' : 'Copilot',
|
|
10
14
|
sortOrder: 20,
|
|
11
15
|
};
|
|
12
16
|
if (!ctx.auth) {
|
package/dist/storage.js
CHANGED
|
@@ -11,6 +11,7 @@ export const defaultConfig = {
|
|
|
11
11
|
sidebar: {
|
|
12
12
|
enabled: true,
|
|
13
13
|
width: 36,
|
|
14
|
+
multilineTitle: true,
|
|
14
15
|
showCost: true,
|
|
15
16
|
showQuota: true,
|
|
16
17
|
wrapQuotaLines: true,
|
|
@@ -64,6 +65,7 @@ export async function loadConfig(paths) {
|
|
|
64
65
|
sidebar: {
|
|
65
66
|
enabled: asBoolean(sidebar.enabled, base.sidebar.enabled),
|
|
66
67
|
width: Math.max(20, Math.min(60, asNumber(sidebar.width, base.sidebar.width))),
|
|
68
|
+
multilineTitle: asBoolean(sidebar.multilineTitle, base.sidebar.multilineTitle ?? true),
|
|
67
69
|
showCost: asBoolean(sidebar.showCost, base.sidebar.showCost),
|
|
68
70
|
showQuota: asBoolean(sidebar.showQuota, base.sidebar.showQuota),
|
|
69
71
|
wrapQuotaLines: asBoolean(sidebar.wrapQuotaLines, base.sidebar.wrapQuotaLines),
|
package/dist/title.js
CHANGED
|
@@ -1,6 +1,51 @@
|
|
|
1
|
+
function sanitizeTitleFragment(value) {
|
|
2
|
+
return stripAnsi(value)
|
|
3
|
+
.replace(/[\x00-\x1F\x7F-\x9F]/g, ' ')
|
|
4
|
+
.trimEnd();
|
|
5
|
+
}
|
|
6
|
+
function isStrongDecoratedDetail(line) {
|
|
7
|
+
if (!line)
|
|
8
|
+
return false;
|
|
9
|
+
if (/^Input\s+\S+\s+Output(?:\s+\S+)?/.test(line))
|
|
10
|
+
return true;
|
|
11
|
+
if (/^Cache\s+(Read|Write)\s+\S+/.test(line))
|
|
12
|
+
return true;
|
|
13
|
+
if (/^\$\S+\s+as API cost\b/.test(line))
|
|
14
|
+
return true;
|
|
15
|
+
// Single-line compact mode compatibility.
|
|
16
|
+
if (/^I(?:nput)?\s+\$?\d[\d.,]*[kKmM]?\s+O(?:utput)?\s+\$?\d[\d.,]*[kKmM]?$/.test(line))
|
|
17
|
+
return true;
|
|
18
|
+
if (/^C(?:ache\s*)?R(?:ead)?\s+\$?\d[\d.,]*[kKmM]?$/.test(line))
|
|
19
|
+
return true;
|
|
20
|
+
if (/^C(?:ache\s*)?W(?:rite)?\s+\$?\d[\d.,]*[kKmM]?$/.test(line))
|
|
21
|
+
return true;
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
function isQuotaLikeProviderDetail(line) {
|
|
25
|
+
if (!line)
|
|
26
|
+
return false;
|
|
27
|
+
if (!/^(OpenAI|Copilot|Anthropic|RightCode|RC)\b/.test(line))
|
|
28
|
+
return false;
|
|
29
|
+
return /\b(Rst|Exp\+?|Balance|Remaining)\b|\d{1,3}%/.test(line);
|
|
30
|
+
}
|
|
31
|
+
function decoratedSingleLineBase(line) {
|
|
32
|
+
const parts = sanitizeTitleFragment(line)
|
|
33
|
+
.split(/\s*\|\s*/)
|
|
34
|
+
.map((part) => part.trim());
|
|
35
|
+
if (parts.length < 2)
|
|
36
|
+
return undefined;
|
|
37
|
+
const details = parts.slice(1);
|
|
38
|
+
if (!details.some((detail) => isStrongDecoratedDetail(detail))) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
return parts[0] || 'Session';
|
|
42
|
+
}
|
|
1
43
|
export function normalizeBaseTitle(title) {
|
|
2
44
|
const firstLine = stripAnsi(title).split(/\r?\n/, 1)[0] || 'Session';
|
|
3
|
-
|
|
45
|
+
const decoratedBase = decoratedSingleLineBase(firstLine);
|
|
46
|
+
if (decoratedBase)
|
|
47
|
+
return decoratedBase;
|
|
48
|
+
return sanitizeTitleFragment(firstLine) || 'Session';
|
|
4
49
|
}
|
|
5
50
|
export function stripAnsi(value) {
|
|
6
51
|
// Remove terminal escape sequences. Sidebar titles must be plain text.
|
|
@@ -40,20 +85,12 @@ export function canonicalizeTitleForCompare(value) {
|
|
|
40
85
|
*/
|
|
41
86
|
export function looksDecorated(title) {
|
|
42
87
|
const lines = stripAnsi(title).split(/\r?\n/);
|
|
43
|
-
if (lines.length < 2)
|
|
44
|
-
return
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (/^Cache\s+(Read|Write)\s+\S+/.test(line))
|
|
52
|
-
return true;
|
|
53
|
-
if (/^\$\S+\s+as API cost/.test(line))
|
|
54
|
-
return true;
|
|
55
|
-
if (/^(OpenAI|Copilot|Anthropic|RightCode|RC)\b/.test(line))
|
|
56
|
-
return true;
|
|
57
|
-
return false;
|
|
58
|
-
});
|
|
88
|
+
if (lines.length < 2) {
|
|
89
|
+
return Boolean(decoratedSingleLineBase(lines[0] || ''));
|
|
90
|
+
}
|
|
91
|
+
const detail = lines
|
|
92
|
+
.slice(1)
|
|
93
|
+
.map((line) => sanitizeTitleFragment(line).trim());
|
|
94
|
+
return (detail.some((line) => isStrongDecoratedDetail(line)) ||
|
|
95
|
+
detail.some((line) => isQuotaLikeProviderDetail(line)));
|
|
59
96
|
}
|
package/dist/title_apply.d.ts
CHANGED
|
@@ -10,9 +10,6 @@ export declare function createTitleApplicator(deps: {
|
|
|
10
10
|
markDirty: (dateKey: string | undefined) => void;
|
|
11
11
|
scheduleSave: () => void;
|
|
12
12
|
renderSidebarTitle: (baseTitle: string, usage: UsageSummary, quotas: QuotaSnapshot[], config: QuotaSidebarConfig) => string;
|
|
13
|
-
quotaRuntime: {
|
|
14
|
-
normalizeProviderID: (providerID: string) => string;
|
|
15
|
-
};
|
|
16
13
|
getQuotaSnapshots: (providerIDs: string[], options?: {
|
|
17
14
|
allowDefault?: boolean;
|
|
18
15
|
}) => Promise<QuotaSnapshot[]>;
|
package/dist/title_apply.js
CHANGED
|
@@ -56,7 +56,7 @@ export function createTitleApplicator(deps) {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
const usage = await deps.summarizeSessionUsageForDisplay(sessionID, deps.config.sidebar.includeChildren);
|
|
59
|
-
const quotaProviders = Array.from(new Set(Object.keys(usage.providers)
|
|
59
|
+
const quotaProviders = Array.from(new Set(Object.keys(usage.providers)));
|
|
60
60
|
const quotas = deps.config.sidebar.showQuota && quotaProviders.length > 0
|
|
61
61
|
? await deps.getQuotaSnapshots(quotaProviders)
|
|
62
62
|
: [];
|
package/dist/types.d.ts
CHANGED
|
@@ -97,6 +97,12 @@ export type QuotaSidebarConfig = {
|
|
|
97
97
|
sidebar: {
|
|
98
98
|
enabled: boolean;
|
|
99
99
|
width: number;
|
|
100
|
+
/**
|
|
101
|
+
* When true, render multi-line decorated session titles.
|
|
102
|
+
* Enabled by default for clearer token/quota layout in sidebar.
|
|
103
|
+
* Set false to keep a compact single-line title.
|
|
104
|
+
*/
|
|
105
|
+
multilineTitle?: boolean;
|
|
100
106
|
showCost: boolean;
|
|
101
107
|
showQuota: boolean;
|
|
102
108
|
/** When true, wrap long quota lines and indent continuations. */
|
package/package.json
CHANGED