@cortexkit/opencode-anthropic-auth 2.4.7 → 2.4.9
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/dist/cli.js +43 -43
- package/dist/index-vr5kh0cd.js +45 -0
- package/dist/index.js +1 -1
- package/dist/routing-relay.d.ts +3 -3
- package/dist/tui/claude-sidebar.d.ts +1 -1
- package/dist/v2/server.js +6 -6
- package/package.json +2 -2
- package/src/tui/claude-sidebar.tsx +321 -274
- package/src/tui/quota-sidebar.d.ts +11 -0
- package/src/tui/quota-sidebar.js +909 -0
- package/src/tui-compiled/quota-sidebar.js +909 -0
- package/src/tui-compiled/tui/claude-sidebar.tsx +431 -539
- package/dist/index-61yvrgwy.js +0 -45
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
quotaDisplayProjection,
|
|
14
14
|
quotaWindowParts,
|
|
15
15
|
} from '@cortexkit/anthropic-auth-core'
|
|
16
|
-
import {
|
|
16
|
+
import type { BoxRenderable, RGBA } from '@opentui/core'
|
|
17
17
|
import {
|
|
18
18
|
createEffect,
|
|
19
19
|
createMemo,
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
onCleanup,
|
|
25
25
|
Show,
|
|
26
26
|
} from 'solid-js'
|
|
27
|
+
import { createStore, reconcile } from 'solid-js/store'
|
|
27
28
|
import {
|
|
28
29
|
type AccountQuota,
|
|
29
30
|
computeQuotaPacing,
|
|
@@ -32,7 +33,6 @@ import {
|
|
|
32
33
|
fitPaceLine,
|
|
33
34
|
fitQuotaRow,
|
|
34
35
|
formatPrimeAccountValue,
|
|
35
|
-
formatQuotaRowLabel,
|
|
36
36
|
getCollapsedAccounts,
|
|
37
37
|
getCollapsedQuotaSummary,
|
|
38
38
|
getFableRecoverySummary,
|
|
@@ -52,6 +52,13 @@ import {
|
|
|
52
52
|
resolveAnthropicAuthPrefs,
|
|
53
53
|
watchTuiPreferences,
|
|
54
54
|
} from '../tui-preferences.js'
|
|
55
|
+
import {
|
|
56
|
+
type AccountView,
|
|
57
|
+
type PaceView,
|
|
58
|
+
type Tone as SharedTone,
|
|
59
|
+
SidebarFrame,
|
|
60
|
+
SidebarTextRow,
|
|
61
|
+
} from './quota-sidebar.js'
|
|
55
62
|
|
|
56
63
|
// Read package metadata from either the raw src/ entry or its generated
|
|
57
64
|
// src/tui-compiled/tui/ counterpart. Avoid a JSON import because package.json sits
|
|
@@ -114,6 +121,18 @@ function usageTone(usedPct: number, appearance: AppearancePrefs): Tone {
|
|
|
114
121
|
return 'err'
|
|
115
122
|
}
|
|
116
123
|
|
|
124
|
+
function sharedTone(tone: Tone): SharedTone {
|
|
125
|
+
return tone === 'ok'
|
|
126
|
+
? 'success'
|
|
127
|
+
: tone === 'warn'
|
|
128
|
+
? 'warning'
|
|
129
|
+
: tone === 'err'
|
|
130
|
+
? 'error'
|
|
131
|
+
: tone === 'muted'
|
|
132
|
+
? 'textMuted'
|
|
133
|
+
: tone
|
|
134
|
+
}
|
|
135
|
+
|
|
117
136
|
interface BarSegment {
|
|
118
137
|
text: string
|
|
119
138
|
tone: Tone
|
|
@@ -242,77 +261,6 @@ function CollapsedRow(props: {
|
|
|
242
261
|
)
|
|
243
262
|
}
|
|
244
263
|
|
|
245
|
-
function CollapsedAccountRow(props: {
|
|
246
|
-
theme: SidebarTheme
|
|
247
|
-
account: ReturnType<typeof getCollapsedAccounts>[number]
|
|
248
|
-
tone: Tone
|
|
249
|
-
now: number
|
|
250
|
-
}) {
|
|
251
|
-
const [width, setWidth] = createSignal(40)
|
|
252
|
-
let row: BoxRenderable | undefined
|
|
253
|
-
const summary = () => getCollapsedQuotaSummary(props.account.quota, props.now)
|
|
254
|
-
const value = () => summary().text ?? '—'
|
|
255
|
-
// Keep the quota and its two-cell status suffix intact; only the label shrinks.
|
|
256
|
-
const labelWidth = () =>
|
|
257
|
-
Math.max(0, width() - 1 - Bun.stringWidth(value()) - 2)
|
|
258
|
-
const dim = () =>
|
|
259
|
-
!props.account.active || props.account.quotaDated || summary().stale
|
|
260
|
-
const attributes = () => (dim() ? TextAttributes.DIM : TextAttributes.BOLD)
|
|
261
|
-
const color = () =>
|
|
262
|
-
dim() ? props.theme.textMuted : toneColor(props.theme, props.tone)
|
|
263
|
-
return (
|
|
264
|
-
<box
|
|
265
|
-
width='100%'
|
|
266
|
-
height={1}
|
|
267
|
-
flexDirection='row'
|
|
268
|
-
ref={(element: BoxRenderable) => {
|
|
269
|
-
row = element
|
|
270
|
-
setWidth(element.width || 40)
|
|
271
|
-
}}
|
|
272
|
-
onSizeChange={() => setWidth(row?.width || 40)}
|
|
273
|
-
>
|
|
274
|
-
<Show when={labelWidth() > 0}>
|
|
275
|
-
<text
|
|
276
|
-
width={labelWidth()}
|
|
277
|
-
flexShrink={0}
|
|
278
|
-
wrapMode='none'
|
|
279
|
-
fg={color()}
|
|
280
|
-
attributes={attributes()}
|
|
281
|
-
>
|
|
282
|
-
{formatQuotaRowLabel(props.account.name, labelWidth())}
|
|
283
|
-
</text>
|
|
284
|
-
<text width={1} flexShrink={0}>
|
|
285
|
-
{' '}
|
|
286
|
-
</text>
|
|
287
|
-
</Show>
|
|
288
|
-
<text
|
|
289
|
-
width={Bun.stringWidth(value())}
|
|
290
|
-
flexShrink={0}
|
|
291
|
-
wrapMode='none'
|
|
292
|
-
fg={color()}
|
|
293
|
-
attributes={attributes()}
|
|
294
|
-
>
|
|
295
|
-
{value()}
|
|
296
|
-
</text>
|
|
297
|
-
<text
|
|
298
|
-
width={2}
|
|
299
|
-
flexShrink={0}
|
|
300
|
-
fg={toneColor(
|
|
301
|
-
props.theme,
|
|
302
|
-
props.account.blocked
|
|
303
|
-
? 'warn'
|
|
304
|
-
: props.account.active
|
|
305
|
-
? 'ok'
|
|
306
|
-
: 'muted',
|
|
307
|
-
)}
|
|
308
|
-
attributes={attributes()}
|
|
309
|
-
>
|
|
310
|
-
{props.account.blocked ? ' ⊘' : ' ●'}
|
|
311
|
-
</text>
|
|
312
|
-
</box>
|
|
313
|
-
)
|
|
314
|
-
}
|
|
315
|
-
|
|
316
264
|
// One cell run of a single-line row: drawn at its own width, never shrunk and
|
|
317
265
|
// never wrapped, so the row's layout is exactly what `fitQuotaRow` measured.
|
|
318
266
|
function Cell(props: { fg: SidebarColor; text: string }) {
|
|
@@ -618,6 +566,54 @@ export function AccountBlock(props: {
|
|
|
618
566
|
)
|
|
619
567
|
}
|
|
620
568
|
|
|
569
|
+
function AccountExtras(props: {
|
|
570
|
+
quota: AccountQuota | null
|
|
571
|
+
now: number
|
|
572
|
+
theme: SidebarTheme
|
|
573
|
+
}) {
|
|
574
|
+
const [width, setWidth] = createSignal<number>()
|
|
575
|
+
let block: BoxRenderable | undefined
|
|
576
|
+
const budget = () =>
|
|
577
|
+
quotaWindowParts(quotaDisplayProjection(props.quota, props.now), {
|
|
578
|
+
now: props.now,
|
|
579
|
+
}).windows.find((part) => part.kind === 'budget')
|
|
580
|
+
return (
|
|
581
|
+
<box
|
|
582
|
+
width='100%'
|
|
583
|
+
flexDirection='column'
|
|
584
|
+
ref={(element: BoxRenderable) => {
|
|
585
|
+
block = element
|
|
586
|
+
setWidth(element.width || undefined)
|
|
587
|
+
}}
|
|
588
|
+
onSizeChange={() => setWidth(block?.width || undefined)}
|
|
589
|
+
>
|
|
590
|
+
<Show when={budget()}>
|
|
591
|
+
{(part: () => QuotaWindowPart) => (
|
|
592
|
+
<box width='100%' height={1} flexDirection='row'>
|
|
593
|
+
<text
|
|
594
|
+
wrapMode='none'
|
|
595
|
+
fg={toneColor(
|
|
596
|
+
props.theme,
|
|
597
|
+
props.quota?.extraUsage?.severity === 'critical'
|
|
598
|
+
? 'err'
|
|
599
|
+
: props.quota?.extraUsage?.severity === 'warning' ||
|
|
600
|
+
props.quota?.extraUsage?.exhausted
|
|
601
|
+
? 'warn'
|
|
602
|
+
: 'muted',
|
|
603
|
+
)}
|
|
604
|
+
>
|
|
605
|
+
{creditLine(part().figure, props.quota?.extraUsage, width())}
|
|
606
|
+
</text>
|
|
607
|
+
</box>
|
|
608
|
+
)}
|
|
609
|
+
</Show>
|
|
610
|
+
<Show when={props.quota?.fallbackAdvised === true}>
|
|
611
|
+
<SidebarTextRow text='→ fallback advised' />
|
|
612
|
+
</Show>
|
|
613
|
+
</box>
|
|
614
|
+
)
|
|
615
|
+
}
|
|
616
|
+
|
|
621
617
|
export interface SidebarController {
|
|
622
618
|
prefs: () => AnthropicAuthTuiPrefs
|
|
623
619
|
collapsed: () => boolean
|
|
@@ -721,22 +717,6 @@ export function ClaudeSidebar(props: {
|
|
|
721
717
|
state().main?.quota != null ||
|
|
722
718
|
state().main?.stateLine != null ||
|
|
723
719
|
enabledFallbacks().length > 0
|
|
724
|
-
// One label column for every expanded account, so all their bars line up.
|
|
725
|
-
const quotaLabelWidth = () =>
|
|
726
|
-
quotaLabelColumnWidth(
|
|
727
|
-
[
|
|
728
|
-
state().main?.quota,
|
|
729
|
-
...(prefs().sections.fallbackAccounts
|
|
730
|
-
? enabledFallbacks().map((fallback) => fallback.quota)
|
|
731
|
-
: []),
|
|
732
|
-
],
|
|
733
|
-
now(),
|
|
734
|
-
)
|
|
735
|
-
|
|
736
|
-
const headerLabel = () => {
|
|
737
|
-
const name = prefs().header.label
|
|
738
|
-
return !hasData() ? name : collapsed() ? `\u25b6 ${name}` : `\u25bc ${name}`
|
|
739
|
-
}
|
|
740
720
|
const activeAccount = () => resolveActiveAccount(state())
|
|
741
721
|
const activeQuotaSummary = () =>
|
|
742
722
|
getCollapsedQuotaSummary(activeAccount().quota, now())
|
|
@@ -812,217 +792,284 @@ export function ClaudeSidebar(props: {
|
|
|
812
792
|
return `${r.transport} \u00b7 ${r.enabled ? 'on' : 'off'}`
|
|
813
793
|
}
|
|
814
794
|
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
795
|
+
const accountDetail = (id: string) =>
|
|
796
|
+
id === 'main'
|
|
797
|
+
? state().main
|
|
798
|
+
: state().fallbacks?.find((account) => account.id === id)
|
|
799
|
+
const accountViews = createMemo((): AccountView[] => {
|
|
800
|
+
if (!hasData()) return []
|
|
801
|
+
return getCollapsedAccounts(state(), prefs().sections.fallbackAccounts).map(
|
|
802
|
+
(account): AccountView => {
|
|
803
|
+
const detail = accountDetail(account.id)
|
|
804
|
+
const quota = quotaDisplayProjection(account.quota, now())
|
|
805
|
+
const needsReauth =
|
|
806
|
+
detail && 'needsReauth' in detail && detail.needsReauth
|
|
807
|
+
const vaultReauth =
|
|
808
|
+
detail && 'vaultReauth' in detail && detail.vaultReauth
|
|
809
|
+
const active = state().activeId === account.id
|
|
810
|
+
const status =
|
|
811
|
+
account.quotaDated && account.quota
|
|
812
|
+
? 'dated'
|
|
813
|
+
: needsReauth
|
|
814
|
+
? 're-login'
|
|
815
|
+
: vaultReauth
|
|
816
|
+
? 'needs sign-in in the vault'
|
|
817
|
+
: active
|
|
818
|
+
? 'active'
|
|
819
|
+
: 'idle'
|
|
820
|
+
const groups = [
|
|
821
|
+
{
|
|
822
|
+
rows: quotaWindowParts(quota, { now: now() })
|
|
823
|
+
.windows.filter((part) => part.kind === 'window')
|
|
824
|
+
.map((part) => {
|
|
825
|
+
const scoped = account.quota?.scoped?.find(
|
|
826
|
+
(window) => window.title === part.name,
|
|
827
|
+
)
|
|
828
|
+
const window =
|
|
829
|
+
part.name === '5h'
|
|
830
|
+
? account.quota?.five_hour
|
|
831
|
+
: part.name === '7d'
|
|
832
|
+
? account.quota?.seven_day
|
|
833
|
+
: scoped
|
|
834
|
+
const pacing =
|
|
835
|
+
prefs().sections.pacing && !account.quotaDated && window
|
|
836
|
+
? computeQuotaPacing(
|
|
837
|
+
window,
|
|
838
|
+
part.name === '5h' ? FIVE_HOUR_MS : SEVEN_DAY_MS,
|
|
839
|
+
now(),
|
|
840
|
+
)
|
|
841
|
+
: null
|
|
842
|
+
const text = fitPaceLine(
|
|
843
|
+
pacing,
|
|
844
|
+
undefined,
|
|
845
|
+
pacing?.runsOutAt
|
|
846
|
+
? formatResetIn(pacing.runsOutAt, now())
|
|
847
|
+
: undefined,
|
|
848
|
+
part.resetIn,
|
|
849
|
+
part.remainingPercent,
|
|
850
|
+
)
|
|
851
|
+
const pace: PaceView | undefined = pacing
|
|
852
|
+
? {
|
|
853
|
+
text: text ?? '',
|
|
854
|
+
tone:
|
|
855
|
+
pacing.state === 'deficit' ? 'warning' : 'textMuted',
|
|
856
|
+
overlay: {
|
|
857
|
+
kind: pacing.deltaPercent > 0 ? 'deficit' : 'reserve',
|
|
858
|
+
percent: pacing.pacePercent,
|
|
859
|
+
},
|
|
860
|
+
}
|
|
861
|
+
: undefined
|
|
862
|
+
const binding =
|
|
863
|
+
account.quota?.bindingWindow ===
|
|
864
|
+
(part.name === '5h'
|
|
865
|
+
? 'five_hour'
|
|
866
|
+
: part.name === '7d'
|
|
867
|
+
? 'seven_day'
|
|
868
|
+
: scoped?.id)
|
|
869
|
+
return {
|
|
870
|
+
part: {
|
|
871
|
+
...part,
|
|
872
|
+
name: part.short,
|
|
873
|
+
figure: `${part.figure}${binding ? ' •' : ''}`,
|
|
874
|
+
},
|
|
875
|
+
pace,
|
|
876
|
+
}
|
|
877
|
+
}),
|
|
878
|
+
},
|
|
879
|
+
]
|
|
880
|
+
return {
|
|
881
|
+
id: account.id,
|
|
882
|
+
label: account.name,
|
|
883
|
+
titleSuffix: detail?.tierLabel,
|
|
884
|
+
quota,
|
|
885
|
+
groups,
|
|
886
|
+
status: {
|
|
887
|
+
text: status,
|
|
888
|
+
tone:
|
|
889
|
+
needsReauth || vaultReauth || detail?.stateLine
|
|
890
|
+
? 'warning'
|
|
891
|
+
: active
|
|
892
|
+
? 'success'
|
|
893
|
+
: 'textMuted',
|
|
894
|
+
},
|
|
895
|
+
blocked: Boolean(account.blocked),
|
|
896
|
+
indicatorTone: account.blocked
|
|
897
|
+
? 'warning'
|
|
898
|
+
: account.active
|
|
899
|
+
? 'success'
|
|
900
|
+
: 'textMuted',
|
|
901
|
+
collapsed: {
|
|
902
|
+
muted: Boolean(
|
|
903
|
+
!account.active ||
|
|
904
|
+
account.quotaDated ||
|
|
905
|
+
getCollapsedQuotaSummary(account.quota, now()).stale,
|
|
906
|
+
),
|
|
907
|
+
quotaTone: sharedTone(activeQuotaTone()),
|
|
908
|
+
},
|
|
909
|
+
emptyQuotaText: { expanded: 'checking…', collapsed: '—' },
|
|
910
|
+
}
|
|
911
|
+
},
|
|
912
|
+
)
|
|
913
|
+
})
|
|
914
|
+
// Polls rebuild snapshots, but account ids keep the shared frame's rows mounted.
|
|
915
|
+
const [accounts, setAccounts] = createStore<AccountView[]>([])
|
|
916
|
+
createEffect(() => setAccounts(reconcile(accountViews(), { key: 'id' })))
|
|
837
917
|
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
)}
|
|
857
|
-
</Index>
|
|
858
|
-
<Show when={state().fastMode}>
|
|
859
|
-
<CollapsedRow theme={theme()} label='Mode'>
|
|
860
|
-
<text fg={toneColor(theme(), 'accent')}>
|
|
861
|
-
<b>{'fast'}</b>
|
|
862
|
-
</text>
|
|
863
|
-
</CollapsedRow>
|
|
918
|
+
return (
|
|
919
|
+
<SidebarFrame
|
|
920
|
+
header={{
|
|
921
|
+
badge: prefs().header.label,
|
|
922
|
+
version:
|
|
923
|
+
prefs().header.showVersion && PLUGIN_VERSION !== ''
|
|
924
|
+
? `v${PLUGIN_VERSION}`
|
|
925
|
+
: undefined,
|
|
926
|
+
}}
|
|
927
|
+
accounts={accounts}
|
|
928
|
+
theme={theme()}
|
|
929
|
+
appearance={prefs().appearance}
|
|
930
|
+
showQuota={prefs().sections.quota}
|
|
931
|
+
now={now()}
|
|
932
|
+
collapsed={collapsed()}
|
|
933
|
+
onToggle={props.controller.toggleCollapsed}
|
|
934
|
+
beforeQuota={(account) => (
|
|
935
|
+
<Show when={accountDetail(account.id)?.stateLine}>
|
|
936
|
+
{(text: () => string) => <SidebarTextRow text={text()} />}
|
|
864
937
|
</Show>
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
938
|
+
)}
|
|
939
|
+
afterQuota={(account) => (
|
|
940
|
+
<AccountExtras
|
|
941
|
+
quota={accountDetail(account.id)?.quota ?? null}
|
|
942
|
+
now={now()}
|
|
943
|
+
theme={theme()}
|
|
944
|
+
/>
|
|
945
|
+
)}
|
|
946
|
+
collapsedSections={() => (
|
|
947
|
+
<>
|
|
948
|
+
<Show when={state().fastMode}>
|
|
949
|
+
<CollapsedRow theme={theme()} label='Mode'>
|
|
950
|
+
<text fg={toneColor(theme(), 'accent')}>
|
|
951
|
+
<b>{'fast'}</b>
|
|
870
952
|
</text>
|
|
871
953
|
</CollapsedRow>
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
954
|
+
</Show>
|
|
955
|
+
<Show when={fableRecoverySummary()}>
|
|
956
|
+
{(summary: () => string) => (
|
|
957
|
+
<CollapsedRow theme={theme()} label='Recovery'>
|
|
958
|
+
<text fg={toneColor(theme(), fableRecoveryTone())}>
|
|
959
|
+
<b>{summary()}</b>
|
|
960
|
+
</text>
|
|
961
|
+
</CollapsedRow>
|
|
962
|
+
)}
|
|
963
|
+
</Show>
|
|
964
|
+
</>
|
|
965
|
+
)}
|
|
966
|
+
sections={() => (
|
|
967
|
+
<>
|
|
968
|
+
<Show when={!hasData()}>
|
|
882
969
|
<box marginTop={1} width='100%'>
|
|
883
970
|
<text fg={theme().textMuted}>{'Waiting for quota\u2026'}</text>
|
|
884
971
|
</box>
|
|
885
|
-
}
|
|
886
|
-
>
|
|
887
|
-
{/* Quota */}
|
|
888
|
-
<Show when={prefs().sections.quota}>
|
|
889
|
-
<SectionHeader theme={theme()} title='Quota' />
|
|
890
|
-
<AccountBlock
|
|
891
|
-
theme={theme()}
|
|
892
|
-
appearance={prefs().appearance}
|
|
893
|
-
name='main'
|
|
894
|
-
quota={state().main?.quota}
|
|
895
|
-
active={state().activeId === 'main'}
|
|
896
|
-
pacingEnabled={prefs().sections.pacing}
|
|
897
|
-
tierLabel={state().main?.tierLabel}
|
|
898
|
-
labelWidth={quotaLabelWidth()}
|
|
899
|
-
stateLine={state().main?.stateLine}
|
|
900
|
-
quotaDated={state().main?.quotaDated}
|
|
901
|
-
now={now()}
|
|
902
|
-
/>
|
|
903
|
-
<Show when={prefs().sections.fallbackAccounts}>
|
|
904
|
-
<For each={enabledFallbacks()}>
|
|
905
|
-
{(fb) => (
|
|
906
|
-
<AccountBlock
|
|
907
|
-
theme={theme()}
|
|
908
|
-
appearance={prefs().appearance}
|
|
909
|
-
name={fb.label ?? fb.id}
|
|
910
|
-
quota={fb.quota}
|
|
911
|
-
active={state().activeId === fb.id}
|
|
912
|
-
pacingEnabled={prefs().sections.pacing}
|
|
913
|
-
tierLabel={fb.tierLabel}
|
|
914
|
-
labelWidth={quotaLabelWidth()}
|
|
915
|
-
needsReauth={fb.needsReauth}
|
|
916
|
-
vaultReauth={fb.vaultReauth}
|
|
917
|
-
stateLine={fb.stateLine}
|
|
918
|
-
quotaDated={fb.quotaDated}
|
|
919
|
-
marginTop={1}
|
|
920
|
-
now={now()}
|
|
921
|
-
/>
|
|
922
|
-
)}
|
|
923
|
-
</For>
|
|
924
|
-
</Show>
|
|
925
972
|
</Show>
|
|
926
|
-
</Show>
|
|
927
|
-
|
|
928
|
-
{/* Routing */}
|
|
929
|
-
<Show when={prefs().sections.routing}>
|
|
930
|
-
<SectionHeader theme={theme()} title='Routing' />
|
|
931
|
-
<StatRow
|
|
932
|
-
theme={theme()}
|
|
933
|
-
label='Route'
|
|
934
|
-
value={state().route}
|
|
935
|
-
tone='accent'
|
|
936
|
-
/>
|
|
937
|
-
<StatRow
|
|
938
|
-
theme={theme()}
|
|
939
|
-
label='Mode'
|
|
940
|
-
value={state().fastMode ? 'fast' : 'std'}
|
|
941
|
-
tone={state().fastMode ? 'accent' : 'muted'}
|
|
942
|
-
/>
|
|
943
|
-
<StatRow
|
|
944
|
-
theme={theme()}
|
|
945
|
-
label='Relay'
|
|
946
|
-
value={relayValue()}
|
|
947
|
-
tone={state().relay?.enabled ? 'ok' : 'muted'}
|
|
948
|
-
/>
|
|
949
|
-
</Show>
|
|
950
973
|
|
|
951
|
-
|
|
952
|
-
{(
|
|
974
|
+
{/* Routing */}
|
|
975
|
+
<Show when={prefs().sections.routing}>
|
|
976
|
+
<SectionHeader theme={theme()} title='Routing' />
|
|
953
977
|
<StatRow
|
|
954
978
|
theme={theme()}
|
|
955
|
-
label='
|
|
956
|
-
value={
|
|
957
|
-
tone=
|
|
979
|
+
label='Route'
|
|
980
|
+
value={state().route}
|
|
981
|
+
tone='accent'
|
|
958
982
|
/>
|
|
959
|
-
)}
|
|
960
|
-
</Show>
|
|
961
|
-
|
|
962
|
-
{/* Cache */}
|
|
963
|
-
<Show when={showCache()}>
|
|
964
|
-
<SectionHeader theme={theme()} title='Cache' />
|
|
965
|
-
<StatRow
|
|
966
|
-
theme={theme()}
|
|
967
|
-
label='1h cache'
|
|
968
|
-
value={`${cacheKeep()?.window} \u00b7 ${cacheKeep()?.enabled ? 'on' : 'off'}`}
|
|
969
|
-
tone={cacheKeep()?.enabled ? 'ok' : 'muted'}
|
|
970
|
-
/>
|
|
971
|
-
<Show when={(cacheKeep()?.trackedSessions ?? 0) > 0}>
|
|
972
983
|
<StatRow
|
|
973
984
|
theme={theme()}
|
|
974
|
-
label='
|
|
975
|
-
value={
|
|
976
|
-
tone='
|
|
985
|
+
label='Mode'
|
|
986
|
+
value={state().fastMode ? 'fast' : 'std'}
|
|
987
|
+
tone={state().fastMode ? 'accent' : 'muted'}
|
|
988
|
+
/>
|
|
989
|
+
<StatRow
|
|
990
|
+
theme={theme()}
|
|
991
|
+
label='Relay'
|
|
992
|
+
value={relayValue()}
|
|
993
|
+
tone={state().relay?.enabled ? 'ok' : 'muted'}
|
|
977
994
|
/>
|
|
978
995
|
</Show>
|
|
979
|
-
</Show>
|
|
980
996
|
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
const value = () => formatPrimeAccountValue(account, now())
|
|
992
|
-
return (
|
|
993
|
-
<StatRow
|
|
994
|
-
theme={theme()}
|
|
995
|
-
label={account.label}
|
|
996
|
-
value={value().text}
|
|
997
|
-
tone={value().hasError ? 'warn' : 'text'}
|
|
998
|
-
/>
|
|
999
|
-
)
|
|
1000
|
-
}}
|
|
1001
|
-
</For>
|
|
1002
|
-
</Show>
|
|
997
|
+
<Show when={fableRecoverySummary()}>
|
|
998
|
+
{(summary: () => string) => (
|
|
999
|
+
<StatRow
|
|
1000
|
+
theme={theme()}
|
|
1001
|
+
label='Recovery'
|
|
1002
|
+
value={summary()}
|
|
1003
|
+
tone={fableRecoveryTone()}
|
|
1004
|
+
/>
|
|
1005
|
+
)}
|
|
1006
|
+
</Show>
|
|
1003
1007
|
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
<Show when={quotaBackedOff()}>
|
|
1008
|
+
{/* Cache */}
|
|
1009
|
+
<Show when={showCache()}>
|
|
1010
|
+
<SectionHeader theme={theme()} title='Cache' />
|
|
1008
1011
|
<StatRow
|
|
1009
1012
|
theme={theme()}
|
|
1010
|
-
label='
|
|
1011
|
-
value={
|
|
1012
|
-
tone='
|
|
1013
|
+
label='1h cache'
|
|
1014
|
+
value={`${cacheKeep()?.window} \u00b7 ${cacheKeep()?.enabled ? 'on' : 'off'}`}
|
|
1015
|
+
tone={cacheKeep()?.enabled ? 'ok' : 'muted'}
|
|
1013
1016
|
/>
|
|
1017
|
+
<Show when={(cacheKeep()?.trackedSessions ?? 0) > 0}>
|
|
1018
|
+
<StatRow
|
|
1019
|
+
theme={theme()}
|
|
1020
|
+
label='Tracked'
|
|
1021
|
+
value={String(cacheKeep()?.trackedSessions)}
|
|
1022
|
+
tone='text'
|
|
1023
|
+
/>
|
|
1024
|
+
</Show>
|
|
1014
1025
|
</Show>
|
|
1015
|
-
|
|
1026
|
+
|
|
1027
|
+
{/* Prime — only when enabled. Expanded view only (no collapsed surface). */}
|
|
1028
|
+
<Show when={showPrime()}>
|
|
1016
1029
|
<StatRow
|
|
1017
1030
|
theme={theme()}
|
|
1018
|
-
label='
|
|
1019
|
-
value={
|
|
1020
|
-
tone='warn'
|
|
1031
|
+
label='Prime'
|
|
1032
|
+
value={primeHasError() ? 'err' : 'on'}
|
|
1033
|
+
tone={primeHasError() ? 'warn' : 'ok'}
|
|
1021
1034
|
/>
|
|
1035
|
+
<For each={prime()?.accounts ?? []}>
|
|
1036
|
+
{(account) => {
|
|
1037
|
+
const value = () => formatPrimeAccountValue(account, now())
|
|
1038
|
+
return (
|
|
1039
|
+
<StatRow
|
|
1040
|
+
theme={theme()}
|
|
1041
|
+
label={account.label}
|
|
1042
|
+
value={value().text}
|
|
1043
|
+
tone={value().hasError ? 'warn' : 'text'}
|
|
1044
|
+
/>
|
|
1045
|
+
)
|
|
1046
|
+
}}
|
|
1047
|
+
</For>
|
|
1022
1048
|
</Show>
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1049
|
+
|
|
1050
|
+
{/* Health — only when something is wrong */}
|
|
1051
|
+
<Show when={degraded() && prefs().sections.health}>
|
|
1052
|
+
<SectionHeader theme={theme()} title='Health' />
|
|
1053
|
+
<Show when={quotaBackedOff()}>
|
|
1054
|
+
<StatRow
|
|
1055
|
+
theme={theme()}
|
|
1056
|
+
label='Quota API'
|
|
1057
|
+
value={`backoff ${formatUntil(state().main?.quotaBackoffUntil, now())}`}
|
|
1058
|
+
tone='warn'
|
|
1059
|
+
/>
|
|
1060
|
+
</Show>
|
|
1061
|
+
<Show when={refreshBackedOff()}>
|
|
1062
|
+
<StatRow
|
|
1063
|
+
theme={theme()}
|
|
1064
|
+
label='Token refresh'
|
|
1065
|
+
value={`backoff ${formatUntil(state().main?.refreshBackoffUntil, now())}`}
|
|
1066
|
+
tone='warn'
|
|
1067
|
+
/>
|
|
1068
|
+
</Show>
|
|
1069
|
+
</Show>
|
|
1070
|
+
</>
|
|
1071
|
+
)}
|
|
1072
|
+
/>
|
|
1026
1073
|
)
|
|
1027
1074
|
}
|
|
1028
1075
|
|