oc-go-usage-display 2.0.0 → 2.1.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 +3 -2
- package/dist/helpers.d.ts +1 -0
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +19 -5
- package/dist/helpers.js.map +1 -1
- package/dist/plugins/oc-go-usage-display.kilo.ts +40 -19
- package/dist/plugins/oc-go-usage-display.kilo.tsx +125 -33
- package/dist/plugins/oc-go-usage-display.ts +40 -19
- package/dist/plugins/oc-go-usage-display.tsx +113 -23
- package/dist/shared.d.ts +12 -1
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +168 -26
- package/dist/shared.js.map +1 -1
- package/dist/tui-shared.d.ts.map +1 -1
- package/dist/tui-shared.js +54 -18
- package/dist/tui-shared.js.map +1 -1
- package/dist/tui.kilo.d.ts.map +1 -1
- package/dist/tui.kilo.js +2 -2
- package/dist/tui.kilo.js.map +1 -1
- package/package.json +6 -4
- package/src/helpers.ts +20 -4
- package/src/shared.ts +168 -24
- package/src/tui-shared.tsx +75 -25
- package/src/tui.kilo.tsx +2 -1
package/src/tui-shared.tsx
CHANGED
|
@@ -51,6 +51,7 @@ import type { JSX } from "@opentui/solid/jsx-runtime";
|
|
|
51
51
|
import type { TuiTheme } from "@opencode-ai/plugin/tui";
|
|
52
52
|
import { For, Show, createEffect, createMemo, createSignal } from "solid-js";
|
|
53
53
|
import {
|
|
54
|
+
GO_SHARE_LABEL_WIDTH,
|
|
54
55
|
PLAN_LABEL_WIDTH,
|
|
55
56
|
buildPlanRows,
|
|
56
57
|
formatPercentCell,
|
|
@@ -76,6 +77,9 @@ import {
|
|
|
76
77
|
hostEnv,
|
|
77
78
|
isRecord,
|
|
78
79
|
mockSnapshot,
|
|
80
|
+
parseMockLimited,
|
|
81
|
+
parseMockPercents,
|
|
82
|
+
parseMockResets,
|
|
79
83
|
readAuthJsonApiKey,
|
|
80
84
|
toNonEmptyString,
|
|
81
85
|
unavailableSnapshot,
|
|
@@ -85,7 +89,7 @@ import {
|
|
|
85
89
|
SIDEBAR_COLLAPSED_GLYPH,
|
|
86
90
|
SIDEBAR_EXPANDED_GLYPH,
|
|
87
91
|
} from "./shared.js";
|
|
88
|
-
import type { UsageHost, UsageSnapshot } from "./shared.js";
|
|
92
|
+
import type { MockSnapshotOverrides, UsageHost, UsageSnapshot } from "./shared.js";
|
|
89
93
|
|
|
90
94
|
// ---------------------------------------------------------------------------
|
|
91
95
|
// Constants
|
|
@@ -178,7 +182,16 @@ async function fetchJsonWithTimeout(url: string, apiKey: string): Promise<unknow
|
|
|
178
182
|
}
|
|
179
183
|
|
|
180
184
|
async function loadUsageSnapshot(host: UsageHost): Promise<UsageSnapshot | null> {
|
|
181
|
-
if (hostEnv(host, "MOCK") === "1")
|
|
185
|
+
if (hostEnv(host, "MOCK") === "1") {
|
|
186
|
+
const overrides: MockSnapshotOverrides = {};
|
|
187
|
+
const percents = parseMockPercents(hostEnv(host, "MOCK_PERCENTS"));
|
|
188
|
+
if (percents !== null) overrides.percents = percents;
|
|
189
|
+
const resets = parseMockResets(hostEnv(host, "MOCK_RESETS"));
|
|
190
|
+
if (resets !== null) overrides.resets = resets;
|
|
191
|
+
const limited = parseMockLimited(hostEnv(host, "MOCK_LIMITED"));
|
|
192
|
+
if (limited !== null) overrides.limited = limited;
|
|
193
|
+
return mockSnapshot(overrides);
|
|
194
|
+
}
|
|
182
195
|
|
|
183
196
|
const apiKey = toNonEmptyString(hostEnv(host, "API_KEY")) ?? readAuthJsonApiKey(host);
|
|
184
197
|
if (apiKey === null) return null;
|
|
@@ -457,20 +470,55 @@ export function LabeledValueRow(props: { theme: TuiTheme; row: UsageRow }) {
|
|
|
457
470
|
// a filled-vs-empty split at a glance.
|
|
458
471
|
export const MIN_METER_WIDTH = 6;
|
|
459
472
|
|
|
460
|
-
//
|
|
461
|
-
//
|
|
462
|
-
//
|
|
463
|
-
//
|
|
464
|
-
//
|
|
473
|
+
// The meter's two parts, as a SPLIT rather than as two flexible boxes.
|
|
474
|
+
//
|
|
475
|
+
// The FILL and the TRACK are flex WEIGHTS that sum to 100, on a zero basis, and a
|
|
476
|
+
// part that comes out at zero is not rendered at all. That is the whole point, and
|
|
477
|
+
// each of the two obvious alternatives moved the block:
|
|
478
|
+
//
|
|
479
|
+
// - two `flexGrow` parts on their default basis: the track keeps a one-cell
|
|
480
|
+
// basis that flex will not shrink away, so at 100% the fill wants the whole
|
|
481
|
+
// meter and the track still claims a cell. It paints itself into the padding
|
|
482
|
+
// beside the percent -- the cell of the wrong color to the right of a full bar
|
|
483
|
+
// -- and makes the meter a cell wider at 100% than anywhere else.
|
|
484
|
+
// - both parts as `width: "<n>%"`: no overflow, but two independent
|
|
485
|
+
// percentage-to-cell roundings that need not agree. At 50% of a 27-cell meter
|
|
486
|
+
// the fill took 13 and the track 13, so the pair was 26 and the bar sat a cell
|
|
487
|
+
// short. A percentage cannot express "the rest of this box".
|
|
488
|
+
//
|
|
489
|
+
// A zero basis makes each part's width purely its share of the space the row gave
|
|
490
|
+
// the meter, so the two add up to the meter at every reading.
|
|
491
|
+
//
|
|
492
|
+
// The meter is two BOXES rather than a string of block glyphs. A glyph string has a
|
|
493
|
+
// fixed cell count, and a plugin cannot measure the sidebar it renders into
|
|
494
|
+
// (opentui resolves a text node's `width` as a wrapping bound, and no layout
|
|
495
|
+
// callback reaches a plugin), so a glyph meter is either too short for a wide
|
|
496
|
+
// sidebar or clipped by a narrow one. Boxes fill whatever the row gives them,
|
|
497
|
+
// which is why the meter reaches the sidebar's right edge on any host at any width.
|
|
465
498
|
//
|
|
466
499
|
// The track is a surface tone rather than the severity colour, so an empty meter
|
|
467
500
|
// reads as an empty track instead of blank space.
|
|
468
501
|
export function MeterBar(props: { theme: TuiTheme; percent: number; severity: UsageMeterSeverity }) {
|
|
469
502
|
const fill = createMemo(() => meterFillPercent(props.percent));
|
|
503
|
+
const track = createMemo(() => 100 - fill());
|
|
470
504
|
return (
|
|
471
505
|
<box flexDirection="row" flexGrow={1} flexShrink={1} minWidth={MIN_METER_WIDTH}>
|
|
472
|
-
<
|
|
473
|
-
|
|
506
|
+
<Show when={fill() > 0}>
|
|
507
|
+
<box
|
|
508
|
+
flexGrow={fill()}
|
|
509
|
+
flexBasis={0}
|
|
510
|
+
flexShrink={0}
|
|
511
|
+
backgroundColor={meterColor(props.theme, props.severity)}
|
|
512
|
+
/>
|
|
513
|
+
</Show>
|
|
514
|
+
<Show when={track() > 0}>
|
|
515
|
+
<box
|
|
516
|
+
flexGrow={track()}
|
|
517
|
+
flexBasis={0}
|
|
518
|
+
flexShrink={0}
|
|
519
|
+
backgroundColor={props.theme.current.backgroundElement}
|
|
520
|
+
/>
|
|
521
|
+
</Show>
|
|
474
522
|
</box>
|
|
475
523
|
);
|
|
476
524
|
}
|
|
@@ -585,25 +633,27 @@ export function CollapsibleSection(props: {
|
|
|
585
633
|
);
|
|
586
634
|
}
|
|
587
635
|
|
|
588
|
-
// A model's share
|
|
589
|
-
//
|
|
590
|
-
//
|
|
591
|
-
//
|
|
592
|
-
//
|
|
593
|
-
//
|
|
594
|
-
//
|
|
636
|
+
// A model's Go share: the bar and the percent pinned to the RIGHT edge of the row,
|
|
637
|
+
// so they line up with the Steps/Cost columns of the table above instead of
|
|
638
|
+
// trailing the label wherever it happens to end.
|
|
639
|
+
//
|
|
640
|
+
// The label sits in a FIXED-width cell and only the meter grows -- the same shape
|
|
641
|
+
// as `GoPlanRow`, and deliberately not a `flexGrow` label with a `gap`. Two
|
|
642
|
+
// flexible children divide the slack between them, and the share meter was doing
|
|
643
|
+
// exactly that: 18 cells wide at 50% and 20 at 100%, with its percent sliding a
|
|
644
|
+
// column each time. This row's number is a share of tokens, not of the plan, so
|
|
645
|
+
// its bar is not comparable with the plan meters' -- but its own geometry must
|
|
646
|
+
// still not depend on its own value.
|
|
595
647
|
export function GoShareRow(props: { theme: TuiTheme; percent: number }) {
|
|
596
648
|
const rounded = Math.round(props.percent);
|
|
597
649
|
return (
|
|
598
|
-
<box flexDirection="row"
|
|
599
|
-
<
|
|
600
|
-
{
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
severity={meterSeverityForPercent(rounded)}
|
|
606
|
-
/>
|
|
650
|
+
<box flexDirection="row">
|
|
651
|
+
<box width={GO_SHARE_LABEL_WIDTH} marginRight={1} flexShrink={0} flexDirection="row">
|
|
652
|
+
<text fg={props.theme.current.textMuted} wrapMode="none">
|
|
653
|
+
{INTEGRATED_GO_SHARE_LABEL}
|
|
654
|
+
</text>
|
|
655
|
+
</box>
|
|
656
|
+
<MeterBar theme={props.theme} percent={rounded} severity={meterSeverityForPercent(rounded)} />
|
|
607
657
|
<text fg={props.theme.current.textMuted} wrapMode="none" flexShrink={0}>
|
|
608
658
|
{formatPercentCell(rounded)}
|
|
609
659
|
</text>
|
package/src/tui.kilo.tsx
CHANGED
|
@@ -107,6 +107,7 @@ import {
|
|
|
107
107
|
KILO_SLOT_ORDER,
|
|
108
108
|
KILO_STEPS_COLUMN_WIDTH,
|
|
109
109
|
KILO_USAGE_PANEL_PLUGIN_ID,
|
|
110
|
+
mockGoShare,
|
|
110
111
|
} from "./shared.js";
|
|
111
112
|
import type { ModelUsage, SessionModelUsage } from "./shared.js";
|
|
112
113
|
import {
|
|
@@ -376,7 +377,7 @@ async function initializeTui(api: TuiPluginApi, options: PluginOptions | undefin
|
|
|
376
377
|
model={model}
|
|
377
378
|
isGo={isGo}
|
|
378
379
|
expanded={() => props.expandedModels().has(key)}
|
|
379
|
-
goShare={() => goSharePercent(usageTokenCount(model.tokens), props.goTotal)}
|
|
380
|
+
goShare={() => mockGoShare(HOST) ?? goSharePercent(usageTokenCount(model.tokens), props.goTotal)}
|
|
380
381
|
onToggle={() => props.onToggleModel(key)}
|
|
381
382
|
/>
|
|
382
383
|
);
|