@slkiser/opencode-quota 5.0.0-beta.1 → 5.0.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.
Files changed (54) hide show
  1. package/README.md +72 -131
  2. package/dist/lib/format-utils.d.ts +0 -4
  3. package/dist/lib/format-utils.d.ts.map +1 -1
  4. package/dist/lib/format-utils.js +0 -3
  5. package/dist/lib/format-utils.js.map +1 -1
  6. package/dist/lib/init-installer.js +2 -2
  7. package/dist/lib/init-installer.js.map +1 -1
  8. package/dist/lib/markdown-table.d.ts +36 -0
  9. package/dist/lib/markdown-table.d.ts.map +1 -1
  10. package/dist/lib/markdown-table.js +56 -0
  11. package/dist/lib/markdown-table.js.map +1 -1
  12. package/dist/lib/opencode-auth.d.ts.map +1 -1
  13. package/dist/lib/opencode-auth.js +4 -3
  14. package/dist/lib/opencode-auth.js.map +1 -1
  15. package/dist/lib/quota-columns.d.ts +38 -0
  16. package/dist/lib/quota-columns.d.ts.map +1 -0
  17. package/dist/lib/quota-columns.js +129 -0
  18. package/dist/lib/quota-columns.js.map +1 -0
  19. package/dist/lib/quota-command-format.d.ts +11 -0
  20. package/dist/lib/quota-command-format.d.ts.map +1 -1
  21. package/dist/lib/quota-command-format.js +80 -27
  22. package/dist/lib/quota-command-format.js.map +1 -1
  23. package/dist/lib/quota-dialog-command-specs.d.ts +4 -0
  24. package/dist/lib/quota-dialog-command-specs.d.ts.map +1 -1
  25. package/dist/lib/quota-dialog-command-specs.js.map +1 -1
  26. package/dist/lib/quota-dialog-commands.d.ts +2 -0
  27. package/dist/lib/quota-dialog-commands.d.ts.map +1 -1
  28. package/dist/lib/quota-dialog-commands.js +114 -101
  29. package/dist/lib/quota-dialog-commands.js.map +1 -1
  30. package/dist/lib/quota-report-message.d.ts +3 -2
  31. package/dist/lib/quota-report-message.d.ts.map +1 -1
  32. package/dist/lib/quota-report-message.js +7 -11
  33. package/dist/lib/quota-report-message.js.map +1 -1
  34. package/dist/lib/quota-stats-format.d.ts +8 -3
  35. package/dist/lib/quota-stats-format.d.ts.map +1 -1
  36. package/dist/lib/quota-stats-format.js +36 -37
  37. package/dist/lib/quota-stats-format.js.map +1 -1
  38. package/dist/lib/quota-status.d.ts +5 -2
  39. package/dist/lib/quota-status.d.ts.map +1 -1
  40. package/dist/lib/quota-status.js +9 -5
  41. package/dist/lib/quota-status.js.map +1 -1
  42. package/dist/lib/report-document.d.ts +56 -2
  43. package/dist/lib/report-document.d.ts.map +1 -1
  44. package/dist/lib/report-document.js +105 -24
  45. package/dist/lib/report-document.js.map +1 -1
  46. package/dist/plugin.d.ts.map +1 -1
  47. package/dist/plugin.js +5 -1
  48. package/dist/plugin.js.map +1 -1
  49. package/dist/tui-v2.d.ts.map +1 -1
  50. package/dist/tui-v2.jsx +149 -23
  51. package/dist/tui-v2.jsx.map +1 -1
  52. package/dist/tui.d.ts.map +1 -1
  53. package/dist/tui.js +370 -98
  54. package/package.json +1 -1
package/dist/tui.js CHANGED
@@ -1,8 +1,8 @@
1
- import { memo as _$memo } from "@opentui/solid";
2
- import { effect as _$effect } from "@opentui/solid";
3
1
  import { use as _$use } from "@opentui/solid";
2
+ import { memo as _$memo } from "@opentui/solid";
4
3
  import { createTextNode as _$createTextNode } from "@opentui/solid";
5
4
  import { insertNode as _$insertNode } from "@opentui/solid";
5
+ import { effect as _$effect } from "@opentui/solid";
6
6
  import { createComponent as _$createComponent } from "@opentui/solid";
7
7
  import { insert as _$insert } from "@opentui/solid";
8
8
  import { setProp as _$setProp } from "@opentui/solid";
@@ -10,16 +10,18 @@ import { createElement as _$createElement } from "@opentui/solid";
10
10
  /** @jsxImportSource @opentui/solid */
11
11
 
12
12
  import { Plugin } from "@opencode/plugin/tui";
13
- import { RGBA, TextAttributes } from "@opentui/core";
13
+ import { TextAttributes } from "@opentui/core";
14
14
  import { useTerminalDimensions } from "@opentui/solid";
15
15
  import { createSignal, onCleanup, Show } from "solid-js";
16
16
  import { loadConfig } from "./lib/config.js";
17
17
  import { resolveOpenCodeLocationRoots } from "./lib/config-file-utils.js";
18
18
  import { sanitizeDisplayText } from "./lib/display-sanitize.js";
19
+ import { fitTableToWidth } from "./lib/markdown-table.js";
20
+ import { layoutQuotaTables } from "./lib/quota-columns.js";
19
21
  import { parseQuotaSlashCommand, QUOTA_DIALOG_COMMANDS } from "./lib/quota-dialog-command-specs.js";
20
- import { readQuotaReport, readQuotaReportMetadata } from "./lib/quota-report-message.js";
22
+ import { readQuotaReportMetadata } from "./lib/quota-report-message.js";
23
+ import { renderableSections, renderKvRow } from "./lib/report-document.js";
21
24
  import { QuotaRpc } from "./rpc.js";
22
- const terminalForeground = RGBA.defaultForeground();
23
25
  const REFRESH_INTERVAL_MS = 60_000;
24
26
  const RPC_TIMEOUT_MS = 60_000;
25
27
  function getSessionID(event) {
@@ -150,8 +152,8 @@ function QuotaFooter(props) {
150
152
  _$setProp(_el$, "flexDirection", "column");
151
153
  _$insert(_el$, () => lines().map(line => (() => {
152
154
  var _el$2 = _$createElement("text");
153
- _$setProp(_el$2, "fg", terminalForeground);
154
155
  _$insert(_el$2, line);
156
+ _$effect(_$p => _$setProp(_el$2, "fg", props.context.theme.text.muted, _$p));
155
157
  return _el$2;
156
158
  })()));
157
159
  return _el$;
@@ -164,6 +166,244 @@ function reportFailure(error) {
164
166
  console.warn(`[opencode-quota] failed to load quota: ${rpcErrorMessage(error)}${hint}`);
165
167
  }
166
168
 
169
+ /** OpenCode's dialog widths (dialogWidth in its ui/dialog.tsx). */
170
+ const DIALOG_WIDTHS = {
171
+ medium: 60,
172
+ large: 88,
173
+ xlarge: 116
174
+ };
175
+ function sanitizeQuotaRow(row) {
176
+ return {
177
+ ...row,
178
+ label: sanitizeDisplayText(row.label),
179
+ value: sanitizeDisplayText(row.value),
180
+ usage: row.usage === undefined ? undefined : sanitizeDisplayText(row.usage),
181
+ reset: row.reset === undefined ? undefined : sanitizeDisplayText(row.reset),
182
+ notes: row.notes.map(sanitizeDisplayText)
183
+ };
184
+ }
185
+
186
+ /** A report's /quota blocks, sanitized, for layoutQuotaTables. */
187
+ function quotaBlocks(section) {
188
+ return section.blocks.flatMap(block => block.kind === "quota" ? [{
189
+ ...block,
190
+ provider: sanitizeDisplayText(block.provider),
191
+ rows: block.rows.map(sanitizeQuotaRow)
192
+ }] : []);
193
+ }
194
+
195
+ /**
196
+ * A /quota table (see layoutQuotaTables): its title bold, like a section title, then its
197
+ * header bold in OpenCode's primary accent, like the token tables, and its providers bold.
198
+ * In the rows, labels, reset times, and notes are muted; bars, percents, and values base.
199
+ */
200
+ function QuotaTableView(props) {
201
+ return (() => {
202
+ var _el$3 = _$createElement("box"),
203
+ _el$4 = _$createElement("text");
204
+ _$insertNode(_el$3, _el$4);
205
+ _$setProp(_el$3, "flexDirection", "column");
206
+ _$insert(_el$4, () => props.table.title);
207
+ _$insert(_el$3, () => props.table.lines.map(line => line.style === "row" ? (() => {
208
+ var _el$5 = _$createElement("text");
209
+ _$setProp(_el$5, "wrapMode", "none");
210
+ _$insert(_el$5, () => line.segments.map(segment => (() => {
211
+ var _el$6 = _$createElement("span");
212
+ _$insert(_el$6, () => segment.text);
213
+ _$effect(_$p => _$setProp(_el$6, "style", {
214
+ fg: segment.muted ? props.theme.text.muted : props.theme.text.base
215
+ }, _$p));
216
+ return _el$6;
217
+ })()));
218
+ _$effect(_$p => _$setProp(_el$5, "fg", props.theme.text.base, _$p));
219
+ return _el$5;
220
+ })() : (() => {
221
+ var _el$7 = _$createElement("text");
222
+ _$insert(_el$7, () => line.segments.map(segment => segment.text).join(""));
223
+ _$effect(_p$ => {
224
+ var _v$3 = TextAttributes.BOLD,
225
+ _v$4 = line.style === "header" ? props.theme.text.action.primary.selected : props.theme.text.base,
226
+ _v$5 = line.style === "header" ? "none" : undefined;
227
+ _v$3 !== _p$.e && (_p$.e = _$setProp(_el$7, "attributes", _v$3, _p$.e));
228
+ _v$4 !== _p$.t && (_p$.t = _$setProp(_el$7, "fg", _v$4, _p$.t));
229
+ _v$5 !== _p$.a && (_p$.a = _$setProp(_el$7, "wrapMode", _v$5, _p$.a));
230
+ return _p$;
231
+ }, {
232
+ e: undefined,
233
+ t: undefined,
234
+ a: undefined
235
+ });
236
+ return _el$7;
237
+ })()), null);
238
+ _$effect(_p$ => {
239
+ var _v$ = TextAttributes.BOLD,
240
+ _v$2 = props.theme.text.base;
241
+ _v$ !== _p$.e && (_p$.e = _$setProp(_el$4, "attributes", _v$, _p$.e));
242
+ _v$2 !== _p$.t && (_p$.t = _$setProp(_el$4, "fg", _v$2, _p$.t));
243
+ return _p$;
244
+ }, {
245
+ e: undefined,
246
+ t: undefined
247
+ });
248
+ return _el$3;
249
+ })();
250
+ }
251
+
252
+ /**
253
+ * One block of a report. Lines, and the chat lines of /quota rows the dialog's tables do not
254
+ * fit, and key-value rows wrap at words. A table keeps each row on one line and spans `width`
255
+ * columns when it fits (see fitTableToWidth), with the header row bold in OpenCode's primary
256
+ * accent (its text color for a selected tab). Like OpenCode's sidebar, labels are muted and
257
+ * values are base.
258
+ */
259
+ function ReportBlockView(props) {
260
+ const block = props.block;
261
+ if (block.kind === "lines" || block.kind === "quota") {
262
+ return (() => {
263
+ var _el$8 = _$createElement("text");
264
+ _$insert(_el$8, () => sanitizeDisplayText(block.lines.join("\n")));
265
+ _$effect(_$p => _$setProp(_el$8, "fg", props.theme.text.muted, _$p));
266
+ return _el$8;
267
+ })();
268
+ }
269
+ if (block.kind === "kv") {
270
+ return (() => {
271
+ var _el$9 = _$createElement("box");
272
+ _$setProp(_el$9, "flexDirection", "column");
273
+ _$insert(_el$9, () => block.rows.map(({
274
+ value,
275
+ ...key
276
+ }) => value === undefined ? (() => {
277
+ var _el$0 = _$createElement("text");
278
+ _$insert(_el$0, () => sanitizeDisplayText(renderKvRow(key)));
279
+ _$effect(_$p => _$setProp(_el$0, "fg", props.theme.text.muted, _$p));
280
+ return _el$0;
281
+ })() : (() => {
282
+ var _el$1 = _$createElement("text"),
283
+ _el$10 = _$createElement("span"),
284
+ _el$11 = _$createTextNode(` `);
285
+ _$insertNode(_el$1, _el$10);
286
+ _$insert(_el$1, () => sanitizeDisplayText(renderKvRow({
287
+ ...key,
288
+ trailingColon: true
289
+ })), _el$10);
290
+ _$insertNode(_el$10, _el$11);
291
+ _$insert(_el$10, () => sanitizeDisplayText(value), null);
292
+ _$effect(_p$ => {
293
+ var _v$6 = props.theme.text.muted,
294
+ _v$7 = {
295
+ fg: props.theme.text.base
296
+ };
297
+ _v$6 !== _p$.e && (_p$.e = _$setProp(_el$1, "fg", _v$6, _p$.e));
298
+ _v$7 !== _p$.t && (_p$.t = _$setProp(_el$10, "style", _v$7, _p$.t));
299
+ return _p$;
300
+ }, {
301
+ e: undefined,
302
+ t: undefined
303
+ });
304
+ return _el$1;
305
+ })()));
306
+ return _el$9;
307
+ })();
308
+ }
309
+ // Read in the JSX, so the table is laid out again when the terminal is resized.
310
+ const table = () => fitTableToWidth({
311
+ headers: block.headers.map(sanitizeDisplayText),
312
+ fullHeaders: block.fullHeaders?.map(sanitizeDisplayText),
313
+ rows: block.rows.map(row => row.map(sanitizeDisplayText)),
314
+ aligns: block.aligns,
315
+ width: props.width
316
+ });
317
+ return (() => {
318
+ var _el$12 = _$createElement("box"),
319
+ _el$13 = _$createElement("text");
320
+ _$insertNode(_el$12, _el$13);
321
+ _$setProp(_el$12, "flexDirection", "column");
322
+ _$setProp(_el$13, "wrapMode", "none");
323
+ _$insert(_el$13, () => table().header);
324
+ _$insert(_el$12, () => table().rows.map(row => (() => {
325
+ var _el$14 = _$createElement("text");
326
+ _$setProp(_el$14, "wrapMode", "none");
327
+ _$insert(_el$14, row);
328
+ _$effect(_$p => _$setProp(_el$14, "fg", props.theme.text.base, _$p));
329
+ return _el$14;
330
+ })()), null);
331
+ _$effect(_p$ => {
332
+ var _v$8 = TextAttributes.BOLD,
333
+ _v$9 = props.theme.text.action.primary.selected;
334
+ _v$8 !== _p$.e && (_p$.e = _$setProp(_el$13, "attributes", _v$8, _p$.e));
335
+ _v$9 !== _p$.t && (_p$.t = _$setProp(_el$13, "fg", _v$9, _p$.t));
336
+ return _p$;
337
+ }, {
338
+ e: undefined,
339
+ t: undefined
340
+ });
341
+ return _el$12;
342
+ })();
343
+ }
344
+
345
+ /**
346
+ * Draws a report document's sections with the same spacing as its plain text: one blank row
347
+ * between the sections and between the blocks of a section, and a section title directly
348
+ * above its first block. Section titles are bold. A /quota report's provider sections come
349
+ * first; when their rows fit the dialog's tables (see layoutQuotaTables), the tables take
350
+ * their place. The dialog shows the heading's subtitle under its own title, so the heading
351
+ * line is left out.
352
+ */
353
+ function ReportDocumentView(props) {
354
+ const sections = renderableSections(props.document);
355
+ const blocks = sections.flatMap(quotaBlocks);
356
+ // Read in the JSX, so the tables are laid out again when the terminal is resized.
357
+ const tables = () => blocks.length > 0 ? layoutQuotaTables(blocks, props.width) : undefined;
358
+ return (() => {
359
+ var _el$15 = _$createElement("box");
360
+ _$setProp(_el$15, "flexDirection", "column");
361
+ _$setProp(_el$15, "gap", 1);
362
+ _$insert(_el$15, () => [...(tables() ?? []).map(table => _$createComponent(QuotaTableView, {
363
+ table: table,
364
+ get theme() {
365
+ return props.theme;
366
+ }
367
+ })), ...sections.filter(section => !tables() || !section.blocks.some(block => block.kind === "quota")).map(section => (() => {
368
+ var _el$16 = _$createElement("box"),
369
+ _el$17 = _$createElement("box");
370
+ _$insertNode(_el$16, _el$17);
371
+ _$setProp(_el$16, "flexDirection", "column");
372
+ _$insert(_el$16, (() => {
373
+ var _c$ = _$memo(() => !!section.title);
374
+ return () => _c$() ? (() => {
375
+ var _el$18 = _$createElement("text");
376
+ _$insert(_el$18, () => sanitizeDisplayText(section.title));
377
+ _$effect(_p$ => {
378
+ var _v$0 = TextAttributes.BOLD,
379
+ _v$1 = props.theme.text.base;
380
+ _v$0 !== _p$.e && (_p$.e = _$setProp(_el$18, "attributes", _v$0, _p$.e));
381
+ _v$1 !== _p$.t && (_p$.t = _$setProp(_el$18, "fg", _v$1, _p$.t));
382
+ return _p$;
383
+ }, {
384
+ e: undefined,
385
+ t: undefined
386
+ });
387
+ return _el$18;
388
+ })() : null;
389
+ })(), _el$17);
390
+ _$setProp(_el$17, "flexDirection", "column");
391
+ _$setProp(_el$17, "gap", 1);
392
+ _$insert(_el$17, () => section.blocks.map(block => _$createComponent(ReportBlockView, {
393
+ block: block,
394
+ get theme() {
395
+ return props.theme;
396
+ },
397
+ get width() {
398
+ return props.width;
399
+ }
400
+ })));
401
+ return _el$16;
402
+ })())]);
403
+ return _el$15;
404
+ })();
405
+ }
406
+
167
407
  /**
168
408
  * Shows command output like OpenCode's alert dialog, but inside a scrollbox so long
169
409
  * reports (/quota_status, /tokens_*) stay reachable. The mouse wheel scrolls the box;
@@ -173,9 +413,15 @@ function reportFailure(error) {
173
413
  function QuotaOutputDialog(props) {
174
414
  const theme = () => props.context.theme.surface("dialog");
175
415
  const dimensions = useTerminalDimensions();
416
+ // OpenCode's dialog is its size's width, at most the terminal width minus 2. Inside it
417
+ // come this box's paddings (2 + 2) and one column for the scrollbox's scrollbar, which
418
+ // shows when the report is taller than the box.
419
+ const tableWidth = () => Math.min(DIALOG_WIDTHS[props.size], dimensions().width - 2) - 4 - 1;
420
+ const subtitle = props.document.heading?.subtitle;
176
421
  // The host dialog starts a quarter of the way down the terminal. The remaining
177
- // 8 rows cover the title, ok button, paddings, gaps, and one spare row.
178
- const maxHeight = () => Math.max(1, Math.floor(dimensions().height * 0.75) - 8);
422
+ // 8 rows cover the title, ok button, paddings, gaps, and one spare row; the subtitle
423
+ // takes one more.
424
+ const maxHeight = () => Math.max(1, Math.floor(dimensions().height * 0.75) - 8 - (subtitle ? 1 : 0));
179
425
  let scroll;
180
426
  const close = () => props.context.ui.dialog.clear();
181
427
  props.context.keymap.layer(() => ({
@@ -218,60 +464,74 @@ function QuotaOutputDialog(props) {
218
464
  }]
219
465
  }));
220
466
  return (() => {
221
- var _el$3 = _$createElement("box"),
222
- _el$4 = _$createElement("box"),
223
- _el$5 = _$createElement("text"),
224
- _el$6 = _$createElement("text"),
225
- _el$8 = _$createElement("box"),
226
- _el$9 = _$createElement("scrollbox"),
227
- _el$0 = _$createElement("text"),
228
- _el$1 = _$createElement("box"),
229
- _el$10 = _$createElement("box"),
230
- _el$11 = _$createElement("text");
231
- _$insertNode(_el$3, _el$4);
232
- _$insertNode(_el$3, _el$8);
233
- _$insertNode(_el$3, _el$1);
234
- _$setProp(_el$3, "paddingLeft", 2);
235
- _$setProp(_el$3, "paddingRight", 2);
236
- _$setProp(_el$3, "gap", 1);
237
- _$insertNode(_el$4, _el$5);
238
- _$insertNode(_el$4, _el$6);
239
- _$setProp(_el$4, "flexDirection", "row");
240
- _$setProp(_el$4, "justifyContent", "space-between");
241
- _$insert(_el$5, () => props.title);
242
- _$insertNode(_el$6, _$createTextNode(`esc`));
243
- _$setProp(_el$6, "onMouseUp", close);
244
- _$insertNode(_el$8, _el$9);
245
- _$setProp(_el$8, "paddingBottom", 1);
246
- _$insertNode(_el$9, _el$0);
467
+ var _el$19 = _$createElement("box"),
468
+ _el$20 = _$createElement("box"),
469
+ _el$21 = _$createElement("box"),
470
+ _el$22 = _$createElement("text"),
471
+ _el$23 = _$createElement("text"),
472
+ _el$25 = _$createElement("box"),
473
+ _el$26 = _$createElement("scrollbox"),
474
+ _el$27 = _$createElement("box"),
475
+ _el$28 = _$createElement("box"),
476
+ _el$29 = _$createElement("text");
477
+ _$insertNode(_el$19, _el$20);
478
+ _$insertNode(_el$19, _el$25);
479
+ _$insertNode(_el$19, _el$27);
480
+ _$setProp(_el$19, "paddingLeft", 2);
481
+ _$setProp(_el$19, "paddingRight", 2);
482
+ _$setProp(_el$19, "gap", 1);
483
+ _$insertNode(_el$20, _el$21);
484
+ _$insertNode(_el$21, _el$22);
485
+ _$insertNode(_el$21, _el$23);
486
+ _$setProp(_el$21, "flexDirection", "row");
487
+ _$setProp(_el$21, "justifyContent", "space-between");
488
+ _$insert(_el$22, () => props.title);
489
+ _$insertNode(_el$23, _$createTextNode(`esc`));
490
+ _$setProp(_el$23, "onMouseUp", close);
491
+ _$insert(_el$20, subtitle ? (() => {
492
+ var _el$31 = _$createElement("text");
493
+ _$insert(_el$31, () => sanitizeDisplayText(subtitle));
494
+ _$effect(_$p => _$setProp(_el$31, "fg", theme().text.muted, _$p));
495
+ return _el$31;
496
+ })() : null, null);
497
+ _$insertNode(_el$25, _el$26);
498
+ _$setProp(_el$25, "paddingBottom", 1);
247
499
  _$use(element => {
248
500
  scroll = element;
249
- }, _el$9);
250
- _$insert(_el$0, () => props.message);
251
- _$insertNode(_el$1, _el$10);
252
- _$setProp(_el$1, "flexDirection", "row");
253
- _$setProp(_el$1, "justifyContent", "flex-end");
254
- _$setProp(_el$1, "paddingBottom", 1);
255
- _$insertNode(_el$10, _el$11);
256
- _$setProp(_el$10, "paddingLeft", 3);
257
- _$setProp(_el$10, "paddingRight", 3);
258
- _$setProp(_el$10, "onMouseUp", close);
259
- _$insertNode(_el$11, _$createTextNode(`ok`));
501
+ }, _el$26);
502
+ _$insert(_el$26, _$createComponent(ReportDocumentView, {
503
+ get document() {
504
+ return props.document;
505
+ },
506
+ get theme() {
507
+ return theme();
508
+ },
509
+ get width() {
510
+ return tableWidth();
511
+ }
512
+ }));
513
+ _$insertNode(_el$27, _el$28);
514
+ _$setProp(_el$27, "flexDirection", "row");
515
+ _$setProp(_el$27, "justifyContent", "flex-end");
516
+ _$setProp(_el$27, "paddingBottom", 1);
517
+ _$insertNode(_el$28, _el$29);
518
+ _$setProp(_el$28, "paddingLeft", 3);
519
+ _$setProp(_el$28, "paddingRight", 3);
520
+ _$setProp(_el$28, "onMouseUp", close);
521
+ _$insertNode(_el$29, _$createTextNode(`ok`));
260
522
  _$effect(_p$ => {
261
- var _v$ = TextAttributes.BOLD,
262
- _v$2 = theme().text.base,
263
- _v$3 = theme().text.muted,
264
- _v$4 = maxHeight(),
265
- _v$5 = theme().text.muted,
266
- _v$6 = theme().background.action.primary.focused,
267
- _v$7 = theme().text.action.primary.focused;
268
- _v$ !== _p$.e && (_p$.e = _$setProp(_el$5, "attributes", _v$, _p$.e));
269
- _v$2 !== _p$.t && (_p$.t = _$setProp(_el$5, "fg", _v$2, _p$.t));
270
- _v$3 !== _p$.a && (_p$.a = _$setProp(_el$6, "fg", _v$3, _p$.a));
271
- _v$4 !== _p$.o && (_p$.o = _$setProp(_el$9, "maxHeight", _v$4, _p$.o));
272
- _v$5 !== _p$.i && (_p$.i = _$setProp(_el$0, "fg", _v$5, _p$.i));
273
- _v$6 !== _p$.n && (_p$.n = _$setProp(_el$10, "backgroundColor", _v$6, _p$.n));
274
- _v$7 !== _p$.s && (_p$.s = _$setProp(_el$11, "fg", _v$7, _p$.s));
523
+ var _v$10 = TextAttributes.BOLD,
524
+ _v$11 = theme().text.base,
525
+ _v$12 = theme().text.muted,
526
+ _v$13 = maxHeight(),
527
+ _v$14 = theme().background.action.primary.focused,
528
+ _v$15 = theme().text.action.primary.focused;
529
+ _v$10 !== _p$.e && (_p$.e = _$setProp(_el$22, "attributes", _v$10, _p$.e));
530
+ _v$11 !== _p$.t && (_p$.t = _$setProp(_el$22, "fg", _v$11, _p$.t));
531
+ _v$12 !== _p$.a && (_p$.a = _$setProp(_el$23, "fg", _v$12, _p$.a));
532
+ _v$13 !== _p$.o && (_p$.o = _$setProp(_el$26, "maxHeight", _v$13, _p$.o));
533
+ _v$14 !== _p$.i && (_p$.i = _$setProp(_el$28, "backgroundColor", _v$14, _p$.i));
534
+ _v$15 !== _p$.n && (_p$.n = _$setProp(_el$29, "fg", _v$15, _p$.n));
275
535
  return _p$;
276
536
  }, {
277
537
  e: undefined,
@@ -279,10 +539,9 @@ function QuotaOutputDialog(props) {
279
539
  a: undefined,
280
540
  o: undefined,
281
541
  i: undefined,
282
- n: undefined,
283
- s: undefined
542
+ n: undefined
284
543
  });
285
- return _el$3;
544
+ return _el$19;
286
545
  })();
287
546
  }
288
547
  function showQuotaOutputDialog(context, output) {
@@ -292,8 +551,11 @@ function showQuotaOutputDialog(context, output) {
292
551
  get title() {
293
552
  return output.title;
294
553
  },
295
- get message() {
296
- return output.message;
554
+ get document() {
555
+ return output.document;
556
+ },
557
+ get size() {
558
+ return output.dialogSize;
297
559
  }
298
560
  }), resolve);
299
561
  context.ui.dialog.set({
@@ -327,7 +589,7 @@ async function runQuotaCommand(context, command, sessionID, typed) {
327
589
  if (result.state === "noop") return;
328
590
  await showQuotaOutputDialog(context, {
329
591
  title: result.title,
330
- message: result.output,
592
+ document: result.document,
331
593
  dialogSize: result.dialogSize
332
594
  });
333
595
  } catch (error) {
@@ -375,7 +637,7 @@ async function showPostedQuotaReport(context, event) {
375
637
  });
376
638
  void showQuotaOutputDialog(context, {
377
639
  title: report.title,
378
- message: readQuotaReport(item.payload.text),
640
+ document: report.document,
379
641
  dialogSize: spec.dialogSize
380
642
  });
381
643
  }
@@ -453,46 +715,45 @@ function SidebarQuotaView(props) {
453
715
  unsubscribe();
454
716
  });
455
717
  return (() => {
456
- var _el$13 = _$createElement("box"),
457
- _el$14 = _$createElement("box"),
458
- _el$16 = _$createElement("text"),
459
- _el$17 = _$createElement("b");
460
- _$insertNode(_el$13, _el$14);
461
- _$setProp(_el$13, "flexDirection", "column");
462
- _$insertNode(_el$14, _el$16);
463
- _$setProp(_el$14, "flexDirection", "row");
464
- _$setProp(_el$14, "gap", 1);
465
- _$setProp(_el$14, "onMouseDown", () => expandable() && setOpen(value => !value));
466
- _$insert(_el$14, _$createComponent(Show, {
718
+ var _el$32 = _$createElement("box"),
719
+ _el$33 = _$createElement("box"),
720
+ _el$35 = _$createElement("text"),
721
+ _el$36 = _$createElement("b");
722
+ _$insertNode(_el$32, _el$33);
723
+ _$setProp(_el$32, "flexDirection", "column");
724
+ _$insertNode(_el$33, _el$35);
725
+ _$setProp(_el$33, "flexDirection", "row");
726
+ _$setProp(_el$33, "gap", 1);
727
+ _$setProp(_el$33, "onMouseDown", () => expandable() && setOpen(value => !value));
728
+ _$insert(_el$33, _$createComponent(Show, {
467
729
  get when() {
468
730
  return expandable();
469
731
  },
470
732
  get children() {
471
- var _el$15 = _$createElement("text");
472
- _$setProp(_el$15, "fg", terminalForeground);
473
- _$insert(_el$15, () => open() ? "▼" : "▶");
474
- return _el$15;
733
+ var _el$34 = _$createElement("text");
734
+ _$insert(_el$34, () => open() ? "▼" : "▶");
735
+ _$effect(_$p => _$setProp(_el$34, "fg", props.context.theme.text.base, _$p));
736
+ return _el$34;
475
737
  }
476
- }), _el$16);
477
- _$insertNode(_el$16, _el$17);
478
- _$setProp(_el$16, "fg", terminalForeground);
479
- _$insertNode(_el$17, _$createTextNode(`Quota`));
480
- _$insert(_el$16, _$createComponent(Show, {
738
+ }), _el$35);
739
+ _$insertNode(_el$35, _el$36);
740
+ _$insertNode(_el$36, _$createTextNode(`Quota`));
741
+ _$insert(_el$35, _$createComponent(Show, {
481
742
  get when() {
482
743
  return _$memo(() => !!(expandable() && !open()))() && quota()?.activeProviderCount;
483
744
  },
484
745
  children: count => ` (${count()} active)`
485
746
  }), null);
486
- _$insert(_el$13, _$createComponent(Show, {
747
+ _$insert(_el$32, _$createComponent(Show, {
487
748
  get when() {
488
749
  return quota();
489
750
  },
490
751
  get fallback() {
491
752
  return (() => {
492
- var _el$19 = _$createElement("text");
493
- _$insertNode(_el$19, _$createTextNode(`No quota data available`));
494
- _$setProp(_el$19, "fg", terminalForeground);
495
- return _el$19;
753
+ var _el$38 = _$createElement("text");
754
+ _$insertNode(_el$38, _$createTextNode(`No quota data available`));
755
+ _$effect(_$p => _$setProp(_el$38, "fg", props.context.theme.text.muted, _$p));
756
+ return _el$38;
496
757
  })();
497
758
  },
498
759
  get children() {
@@ -501,18 +762,29 @@ function SidebarQuotaView(props) {
501
762
  return !expandable() || open();
502
763
  },
503
764
  get children() {
504
- return lines().map(line => (() => {
505
- var _el$21 = _$createElement("text");
506
- _$setProp(_el$21, "fg", terminalForeground);
507
- _$setProp(_el$21, "wrapMode", "none");
508
- _$insert(_el$21, line || " ");
509
- return _el$21;
765
+ return lines().map(line =>
766
+ // Provider headers such as "[Copilot] (individual)" are bold, like section titles.
767
+ line.startsWith("[") ? (() => {
768
+ var _el$40 = _$createElement("text"),
769
+ _el$41 = _$createElement("b");
770
+ _$insertNode(_el$40, _el$41);
771
+ _$setProp(_el$40, "wrapMode", "none");
772
+ _$insert(_el$41, line);
773
+ _$effect(_$p => _$setProp(_el$40, "fg", props.context.theme.text.base, _$p));
774
+ return _el$40;
775
+ })() : (() => {
776
+ var _el$42 = _$createElement("text");
777
+ _$setProp(_el$42, "wrapMode", "none");
778
+ _$insert(_el$42, line || " ");
779
+ _$effect(_$p => _$setProp(_el$42, "fg", props.context.theme.text.muted, _$p));
780
+ return _el$42;
510
781
  })());
511
782
  }
512
783
  });
513
784
  }
514
785
  }), null);
515
- return _el$13;
786
+ _$effect(_$p => _$setProp(_el$35, "fg", props.context.theme.text.base, _$p));
787
+ return _el$32;
516
788
  })();
517
789
  }
518
790
  const plugin = Plugin.define({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slkiser/opencode-quota",
3
- "version": "5.0.0-beta.1",
3
+ "version": "5.0.0",
4
4
  "description": "OpenCode quota & tokens usage with zero context window pollution. Supports GitHub Copilot, OpenAI (Plus/Pro), Chutes AI, Synthetic, Google AGY, Z.ai coding plan and more.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",