@tzyc/dsh-model-visibility 1.10.2 → 1.12.1
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 +64 -15
- package/client-src/catalog-store.mjs +95 -18
- package/client-src/checker-view.mjs +158 -65
- package/client-src/css.mjs +153 -221
- package/client-src/dict.mjs +119 -81
- package/client-src/format.mjs +9 -2
- package/client-src/pure.mjs +24 -7
- package/client-src/settings-section.mjs +242 -137
- package/client-src/usage-view.mjs +69 -65
- package/client.js +869 -596
- package/index.js +4 -2
- package/lib/check-catalog.js +868 -108
- package/lib/export-csv.js +48 -14
- package/lib/learned-catalog.js +10 -2
- package/lib/model-info.js +4 -2
- package/lib/model-overrides.js +9 -4
- package/lib/provider-probe.js +138 -22
- package/lib/provider-write.js +31 -7
- package/package.json +2 -2
package/client.js
CHANGED
|
@@ -111,7 +111,10 @@ window.__ModuleLoader__.load({
|
|
|
111
111
|
groups: Array.isArray(value.groups) ? value.groups : [],
|
|
112
112
|
failures: Array.isArray(value.failures) ? value.failures : [],
|
|
113
113
|
providers: Array.isArray(value.providers) ? value.providers : [],
|
|
114
|
-
|
|
114
|
+
// The section reads `state.learned` (the learned-snapshot meta with
|
|
115
|
+
// its feature flags); this store is its only source, so the field
|
|
116
|
+
// name must stay in lock-step with lib/model-info.js's response.
|
|
117
|
+
learned: value.learned && typeof value.learned === 'object' ? value.learned : null,
|
|
115
118
|
error: null,
|
|
116
119
|
detailsError: null,
|
|
117
120
|
source: 'host',
|
|
@@ -128,7 +131,7 @@ window.__ModuleLoader__.load({
|
|
|
128
131
|
groups: value.groups,
|
|
129
132
|
failures: value.failures,
|
|
130
133
|
providers: [],
|
|
131
|
-
|
|
134
|
+
learned: null,
|
|
132
135
|
error: null,
|
|
133
136
|
detailsError: detail,
|
|
134
137
|
source: 'catalog',
|
|
@@ -309,6 +312,7 @@ window.__ModuleLoader__.load({
|
|
|
309
312
|
let state = { status: 'idle', snapshot: null, error: null }
|
|
310
313
|
let inflight = null
|
|
311
314
|
let timer = null
|
|
315
|
+
let timerMs = null
|
|
312
316
|
|
|
313
317
|
const publish = (next) => {
|
|
314
318
|
state = next
|
|
@@ -340,16 +344,33 @@ window.__ModuleLoader__.load({
|
|
|
340
344
|
return run
|
|
341
345
|
}
|
|
342
346
|
|
|
343
|
-
/**
|
|
347
|
+
/**
|
|
348
|
+
* Poll cadence follows the ledger's own progress: a cold scan of the session
|
|
349
|
+
* tree takes seconds, so the strip asks again quickly until it settles
|
|
350
|
+
* instead of showing em dashes for half a minute.
|
|
351
|
+
*/
|
|
352
|
+
const LIVE_POLL_MS = 3_000
|
|
344
353
|
const POLL_MS = 30_000
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
|
|
354
|
+
const rearm = () => {
|
|
355
|
+
// Same rule as the verify store: no observer, no armed timer.
|
|
356
|
+
if (listeners.size === 0) {
|
|
357
|
+
stopTimer()
|
|
358
|
+
return
|
|
359
|
+
}
|
|
360
|
+
const wantMs = state.snapshot?.computing === true ? LIVE_POLL_MS : POLL_MS
|
|
361
|
+
if (timer !== null && timerMs === wantMs) return
|
|
362
|
+
if (timer !== null) clearInterval(timer)
|
|
363
|
+
timerMs = wantMs
|
|
364
|
+
timer = setInterval(() => {
|
|
365
|
+
void read().then(rearm, rearm)
|
|
366
|
+
}, wantMs)
|
|
367
|
+
if (typeof timer === 'object' && timer !== null && typeof timer.unref === 'function') timer.unref()
|
|
348
368
|
}
|
|
349
369
|
const stopTimer = () => {
|
|
350
370
|
if (timer !== null) {
|
|
351
371
|
clearInterval(timer)
|
|
352
372
|
timer = null
|
|
373
|
+
timerMs = null
|
|
353
374
|
}
|
|
354
375
|
}
|
|
355
376
|
|
|
@@ -357,7 +378,11 @@ window.__ModuleLoader__.load({
|
|
|
357
378
|
subscribe(fn) {
|
|
358
379
|
const first = listeners.size === 0
|
|
359
380
|
listeners.add(fn)
|
|
360
|
-
|
|
381
|
+
// Read once immediately: waiting a full poll interval is why the strip
|
|
382
|
+
// looked empty on every first visit.
|
|
383
|
+
if (first) {
|
|
384
|
+
void read().then(rearm, rearm)
|
|
385
|
+
}
|
|
361
386
|
return () => {
|
|
362
387
|
listeners.delete(fn)
|
|
363
388
|
if (listeners.size === 0) stopTimer()
|
|
@@ -367,12 +392,21 @@ window.__ModuleLoader__.load({
|
|
|
367
392
|
return state
|
|
368
393
|
},
|
|
369
394
|
load() {
|
|
370
|
-
return read()
|
|
395
|
+
return read().then(
|
|
396
|
+
(value) => {
|
|
397
|
+
rearm()
|
|
398
|
+
return value
|
|
399
|
+
},
|
|
400
|
+
(error) => {
|
|
401
|
+
rearm()
|
|
402
|
+
throw error
|
|
403
|
+
},
|
|
404
|
+
)
|
|
371
405
|
},
|
|
372
406
|
refresh() {
|
|
373
407
|
// A POST rescan is never needed: the Host rescan throttle refreshes on
|
|
374
408
|
// reads anyway. A plain read is enough here.
|
|
375
|
-
return
|
|
409
|
+
return this.load()
|
|
376
410
|
},
|
|
377
411
|
}
|
|
378
412
|
}
|
|
@@ -387,6 +421,8 @@ window.__ModuleLoader__.load({
|
|
|
387
421
|
let state = { status: 'idle', snapshot: null, error: null }
|
|
388
422
|
let inflight = null
|
|
389
423
|
let timer = null
|
|
424
|
+
/** Cadence the armed timer currently runs at, so rearm can compare. */
|
|
425
|
+
let timerMs = null
|
|
390
426
|
|
|
391
427
|
const publish = (next) => {
|
|
392
428
|
state = next
|
|
@@ -448,16 +484,30 @@ window.__ModuleLoader__.load({
|
|
|
448
484
|
|
|
449
485
|
const SWEEP_POLL_MS = 2_000
|
|
450
486
|
const IDLE_POLL_MS = 60_000
|
|
451
|
-
|
|
452
|
-
|
|
487
|
+
/** Re-arm the timer at the cadence the *current* snapshot needs. */
|
|
488
|
+
const rearm = () => {
|
|
489
|
+
// Never arm a timer nobody will observe: a bare `load()` (a test, an
|
|
490
|
+
// explicit refresh) must not leave an interval holding the page alive.
|
|
491
|
+
if (listeners.size === 0) {
|
|
492
|
+
stopTimer()
|
|
493
|
+
return
|
|
494
|
+
}
|
|
495
|
+
const wantFast = state.snapshot?.state?.running === true
|
|
496
|
+
const wantMs = wantFast ? SWEEP_POLL_MS : IDLE_POLL_MS
|
|
497
|
+
if (timer !== null && timerMs === wantMs) return
|
|
498
|
+
if (timer !== null) clearInterval(timer)
|
|
499
|
+
timerMs = wantMs
|
|
453
500
|
timer = setInterval(() => {
|
|
454
|
-
void read()
|
|
455
|
-
},
|
|
501
|
+
void read().then(rearm, rearm)
|
|
502
|
+
}, wantMs)
|
|
503
|
+
// A polling strip must not keep a non-browser process alive.
|
|
504
|
+
if (typeof timer === 'object' && timer !== null && typeof timer.unref === 'function') timer.unref()
|
|
456
505
|
}
|
|
457
506
|
const stopTimer = () => {
|
|
458
507
|
if (timer !== null) {
|
|
459
508
|
clearInterval(timer)
|
|
460
509
|
timer = null
|
|
510
|
+
timerMs = null
|
|
461
511
|
}
|
|
462
512
|
}
|
|
463
513
|
|
|
@@ -466,8 +516,8 @@ window.__ModuleLoader__.load({
|
|
|
466
516
|
const first = listeners.size === 0
|
|
467
517
|
listeners.add(fn)
|
|
468
518
|
if (first) {
|
|
469
|
-
void read()
|
|
470
|
-
|
|
519
|
+
void read().then(rearm, rearm)
|
|
520
|
+
rearm()
|
|
471
521
|
}
|
|
472
522
|
return () => {
|
|
473
523
|
listeners.delete(fn)
|
|
@@ -478,13 +528,40 @@ window.__ModuleLoader__.load({
|
|
|
478
528
|
return state
|
|
479
529
|
},
|
|
480
530
|
load() {
|
|
481
|
-
return read()
|
|
531
|
+
return read().then(
|
|
532
|
+
(value) => {
|
|
533
|
+
rearm()
|
|
534
|
+
return value
|
|
535
|
+
},
|
|
536
|
+
(error) => {
|
|
537
|
+
rearm()
|
|
538
|
+
throw error
|
|
539
|
+
},
|
|
540
|
+
)
|
|
482
541
|
},
|
|
483
542
|
start(payload) {
|
|
484
|
-
return post(payload)
|
|
543
|
+
return post(payload).then(
|
|
544
|
+
(value) => {
|
|
545
|
+
rearm()
|
|
546
|
+
return value
|
|
547
|
+
},
|
|
548
|
+
(error) => {
|
|
549
|
+
rearm()
|
|
550
|
+
throw error
|
|
551
|
+
},
|
|
552
|
+
)
|
|
485
553
|
},
|
|
486
554
|
cancel() {
|
|
487
|
-
return post({ cancel: true })
|
|
555
|
+
return post({ cancel: true }).then(
|
|
556
|
+
(value) => {
|
|
557
|
+
rearm()
|
|
558
|
+
return value
|
|
559
|
+
},
|
|
560
|
+
(error) => {
|
|
561
|
+
rearm()
|
|
562
|
+
throw error
|
|
563
|
+
},
|
|
564
|
+
)
|
|
488
565
|
},
|
|
489
566
|
}
|
|
490
567
|
}
|
|
@@ -501,12 +578,13 @@ window.__ModuleLoader__.load({
|
|
|
501
578
|
/**
|
|
502
579
|
* The online capability verification panel.
|
|
503
580
|
*
|
|
504
|
-
*
|
|
505
|
-
*
|
|
506
|
-
*
|
|
507
|
-
*
|
|
581
|
+
* Collapsed to a single summary line: this is a maintenance action, not the
|
|
582
|
+
* point of the page. Expanding it reveals the checker picker, the write-back
|
|
583
|
+
* switch and — the part that matters when a sweep fails — the per-batch errors
|
|
584
|
+
* with their real messages instead of a bare count.
|
|
508
585
|
*
|
|
509
|
-
*
|
|
586
|
+
* The last route choice is kept in browser storage only: the Host persists run
|
|
587
|
+
* facts, not UI selections.
|
|
510
588
|
*/
|
|
511
589
|
|
|
512
590
|
const React = require('react')
|
|
@@ -516,6 +594,10 @@ window.__ModuleLoader__.load({
|
|
|
516
594
|
const { useSource } = require("hooks")
|
|
517
595
|
|
|
518
596
|
const PICK_KEY = 'dsh-model-visibility:checker-pick:v1'
|
|
597
|
+
/** How many failure lines to show before summarising the rest. */
|
|
598
|
+
const MAX_ERROR_LINES = 6
|
|
599
|
+
/** Concurrency choices offered in the panel; mirrors lib/check-catalog MAX_CONCURRENCY. */
|
|
600
|
+
const CONCURRENCY_CHOICES = [1, 2, 3, 4, 6, 8]
|
|
519
601
|
|
|
520
602
|
function readPick() {
|
|
521
603
|
try {
|
|
@@ -525,10 +607,14 @@ window.__ModuleLoader__.load({
|
|
|
525
607
|
provider: typeof parsed.provider === 'string' ? parsed.provider : '',
|
|
526
608
|
model: typeof parsed.model === 'string' ? parsed.model : '',
|
|
527
609
|
writeBack: parsed.writeBack !== false,
|
|
610
|
+
concurrency:
|
|
611
|
+
Number.isFinite(parsed.concurrency) && parsed.concurrency > 0
|
|
612
|
+
? Math.min(8, Math.max(1, Math.trunc(parsed.concurrency)))
|
|
613
|
+
: 2,
|
|
528
614
|
}
|
|
529
|
-
: { provider: '', model: '', writeBack: true }
|
|
615
|
+
: { provider: '', model: '', writeBack: true, concurrency: 2 }
|
|
530
616
|
} catch (_error) {
|
|
531
|
-
return { provider: '', model: '', writeBack: true }
|
|
617
|
+
return { provider: '', model: '', writeBack: true, concurrency: 2 }
|
|
532
618
|
}
|
|
533
619
|
}
|
|
534
620
|
|
|
@@ -559,10 +645,12 @@ window.__ModuleLoader__.load({
|
|
|
559
645
|
.find((group) => group.id === pick.provider)?.models ?? []
|
|
560
646
|
|
|
561
647
|
if (!canUse) {
|
|
562
|
-
return h('div', { className: '
|
|
648
|
+
return h('div', { className: 'mvp_banner' }, t('checker.hostStale'))
|
|
563
649
|
}
|
|
564
650
|
|
|
565
|
-
const tokensSpent = progress
|
|
651
|
+
const tokensSpent = progress
|
|
652
|
+
? formatTokens((progress.tokenUsage?.[0] ?? 0) + (progress.tokenUsage?.[1] ?? 0))
|
|
653
|
+
: null
|
|
566
654
|
|
|
567
655
|
const commit = (next) => {
|
|
568
656
|
setPick(next)
|
|
@@ -572,11 +660,11 @@ window.__ModuleLoader__.load({
|
|
|
572
660
|
const run = () => {
|
|
573
661
|
setMessage(null)
|
|
574
662
|
verify
|
|
575
|
-
.start({
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
663
|
+
.start({
|
|
664
|
+
provider: pick.provider,
|
|
665
|
+
model: pick.model,
|
|
666
|
+
writeBack: pick.writeBack,
|
|
667
|
+
concurrency: pick.concurrency,
|
|
580
668
|
})
|
|
581
669
|
.catch((error) => setMessage(String(error?.message ?? error)))
|
|
582
670
|
}
|
|
@@ -585,6 +673,24 @@ window.__ModuleLoader__.load({
|
|
|
585
673
|
verify.cancel().catch((error) => setMessage(String(error?.message ?? error)))
|
|
586
674
|
}
|
|
587
675
|
|
|
676
|
+
const summary = running
|
|
677
|
+
? t('checker.running', {
|
|
678
|
+
phase: t('checker.phase.' + (progress?.phase ?? 'verifying')),
|
|
679
|
+
done: progress?.done ?? 0,
|
|
680
|
+
total: progress?.total ?? 0,
|
|
681
|
+
tokens: tokensSpent,
|
|
682
|
+
})
|
|
683
|
+
: finished
|
|
684
|
+
? t('checker.finished', {
|
|
685
|
+
done: progress?.done ?? 0,
|
|
686
|
+
total: progress?.total ?? 0,
|
|
687
|
+
tokens: tokensSpent,
|
|
688
|
+
})
|
|
689
|
+
: t('checker.idle')
|
|
690
|
+
|
|
691
|
+
const errors = Array.isArray(progress?.errors) ? progress.errors : []
|
|
692
|
+
const visibleErrors = errors.slice(0, MAX_ERROR_LINES)
|
|
693
|
+
|
|
588
694
|
return h(
|
|
589
695
|
'div',
|
|
590
696
|
{ className: 'mvp_checker' + (open ? '' : ' mvp_checker--closed') },
|
|
@@ -596,26 +702,16 @@ window.__ModuleLoader__.load({
|
|
|
596
702
|
'aria-expanded': open,
|
|
597
703
|
onClick: () => setOpen(!open),
|
|
598
704
|
},
|
|
705
|
+
h('span', { className: 'mvp_chevron' }, open ? '▾' : '▸'),
|
|
599
706
|
h('span', { className: 'mvp_checkerTitle' }, t('checker.title')),
|
|
600
|
-
h(
|
|
601
|
-
|
|
602
|
-
{ className: 'mvp_checkerSummary' },
|
|
603
|
-
running
|
|
604
|
-
? t('checker.running', {
|
|
605
|
-
done: progress?.done ?? 0,
|
|
606
|
-
total: progress?.total ?? 0,
|
|
607
|
-
phase: t(`checker.phase.${progress?.phase ?? 'checker'}`),
|
|
608
|
-
})
|
|
609
|
-
: finished
|
|
610
|
-
? t('checker.finished', { done: progress?.done ?? 0, total: progress?.total ?? 0, tokens: tokensSpent })
|
|
611
|
-
: t('checker.idle'),
|
|
612
|
-
),
|
|
707
|
+
h('span', { className: 'mvp_checkerSummary' }, summary),
|
|
708
|
+
errors.length > 0 ? h('span', { className: 'mvp_badge' }, t('checker.errorBadge', { count: errors.length })) : null,
|
|
613
709
|
),
|
|
614
710
|
open
|
|
615
711
|
? h(
|
|
616
712
|
'div',
|
|
617
713
|
{ className: 'mvp_checkerBody' },
|
|
618
|
-
h('
|
|
714
|
+
h('p', { className: 'mvp_checkerHint' }, t('checker.description')),
|
|
619
715
|
h(
|
|
620
716
|
'div',
|
|
621
717
|
{ className: 'mvp_checkerForm' },
|
|
@@ -654,67 +750,141 @@ window.__ModuleLoader__.load({
|
|
|
654
750
|
),
|
|
655
751
|
),
|
|
656
752
|
),
|
|
657
|
-
),
|
|
658
|
-
h(
|
|
659
|
-
'div',
|
|
660
|
-
{ className: 'mvp_checkerActions' },
|
|
661
753
|
h(
|
|
662
|
-
'
|
|
663
|
-
{
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
754
|
+
'label',
|
|
755
|
+
{ className: 'mvp_field', title: t('checker.concurrencyTitle') },
|
|
756
|
+
h('span', { className: 'mvp_fieldLabel' }, t('checker.concurrency')),
|
|
757
|
+
h(
|
|
758
|
+
'select',
|
|
759
|
+
{
|
|
760
|
+
className: 'mvp_select',
|
|
761
|
+
value: String(pick.concurrency),
|
|
762
|
+
'aria-label': t('checker.concurrency'),
|
|
763
|
+
disabled: running,
|
|
764
|
+
onChange: (event) =>
|
|
765
|
+
commit({ ...pick, concurrency: Number(event.target.value) }),
|
|
766
|
+
},
|
|
767
|
+
CONCURRENCY_CHOICES.map((value) =>
|
|
768
|
+
h('option', { key: value, value: String(value) }, t('checker.concurrencyValue', { count: value })),
|
|
769
|
+
),
|
|
770
|
+
),
|
|
673
771
|
),
|
|
674
772
|
h(
|
|
675
|
-
'
|
|
676
|
-
{
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
773
|
+
'label',
|
|
774
|
+
{ className: 'mvp_check', title: t('checker.writeBackTitle') },
|
|
775
|
+
h('input', {
|
|
776
|
+
type: 'checkbox',
|
|
777
|
+
checked: pick.writeBack,
|
|
778
|
+
disabled: running,
|
|
779
|
+
onChange: (event) => commit({ ...pick, writeBack: event.target.checked }),
|
|
780
|
+
}),
|
|
781
|
+
h('span', null, t('checker.writeBack')),
|
|
683
782
|
),
|
|
684
783
|
h(
|
|
685
784
|
'button',
|
|
686
785
|
{
|
|
687
786
|
type: 'button',
|
|
688
787
|
className: running ? 'mvp_button' : 'mvp_button mvp_button--accent',
|
|
689
|
-
disabled: running
|
|
788
|
+
disabled: !running && (pick.provider === '' || pick.model === ''),
|
|
690
789
|
title: running ? t('checker.cancelTitle') : t('checker.startTitle'),
|
|
691
790
|
onClick: running ? cancel : run,
|
|
692
791
|
},
|
|
693
792
|
running ? t('checker.cancel') : t('checker.start'),
|
|
694
793
|
),
|
|
695
794
|
),
|
|
696
|
-
|
|
795
|
+
running || finished
|
|
697
796
|
? h(
|
|
698
797
|
'div',
|
|
699
798
|
{ className: 'mvp_checkerProgress' },
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
799
|
+
h('div', {
|
|
800
|
+
className: 'mvp_progressBar',
|
|
801
|
+
role: 'progressbar',
|
|
802
|
+
'aria-valuemin': 0,
|
|
803
|
+
'aria-valuemax': progress?.total ?? 0,
|
|
804
|
+
'aria-valuenow': progress?.done ?? 0,
|
|
805
|
+
}, h('span', {
|
|
806
|
+
className: 'mvp_progressFill',
|
|
807
|
+
style: {
|
|
808
|
+
width:
|
|
809
|
+
(progress?.total ?? 0) > 0
|
|
810
|
+
? Math.min(100, Math.round(((progress?.done ?? 0) / progress.total) * 100)) + '%'
|
|
811
|
+
: '0%',
|
|
812
|
+
},
|
|
813
|
+
})),
|
|
814
|
+
h(
|
|
815
|
+
'span',
|
|
816
|
+
{ className: 'mvp_checkerProgressText' },
|
|
817
|
+
t('checker.progress', {
|
|
818
|
+
done: progress?.done ?? 0,
|
|
819
|
+
total: progress?.total ?? 0,
|
|
820
|
+
tokens: tokensSpent,
|
|
821
|
+
}),
|
|
822
|
+
),
|
|
823
|
+
h(
|
|
824
|
+
'span',
|
|
825
|
+
{ className: 'mvp_checkerProgressText' },
|
|
826
|
+
t('checker.threads', { count: progress?.concurrency ?? pick.concurrency }),
|
|
827
|
+
),
|
|
705
828
|
)
|
|
706
829
|
: null,
|
|
707
|
-
|
|
830
|
+
// Which public catalogs were read, and which were not. Without this
|
|
831
|
+
// "the catalogs returned nothing" and "the catalogs errored" look
|
|
832
|
+
// identical, which is exactly how a broken fetch stayed invisible.
|
|
833
|
+
(progress?.sources?.length ?? 0) > 0
|
|
708
834
|
? h(
|
|
709
835
|
'div',
|
|
710
|
-
{
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
836
|
+
{ className: 'mvp_sources' },
|
|
837
|
+
h('div', { className: 'mvp_sourcesTitle' }, t('checker.sourcesTitle')),
|
|
838
|
+
h(
|
|
839
|
+
'ul',
|
|
840
|
+
{ className: 'mvp_sourceList' },
|
|
841
|
+
progress.sources.map((source, index) =>
|
|
842
|
+
h(
|
|
843
|
+
'li',
|
|
844
|
+
{ key: index, className: source.status === 'ok' ? 'mvp_source--ok' : 'mvp_source--bad' },
|
|
845
|
+
h('span', { className: 'mvp_sourceName' }, String(source.name)),
|
|
846
|
+
h('span', { className: 'mvp_sourceState' }, t('checker.source.' + String(source.status))),
|
|
847
|
+
source.models > 0
|
|
848
|
+
? h('span', { className: 'mvp_sourceCount' }, t('checker.sourceModels', { count: source.models }))
|
|
849
|
+
: null,
|
|
850
|
+
source.message
|
|
851
|
+
? h('span', { className: 'mvp_sourceMsg' }, String(source.message))
|
|
852
|
+
: null,
|
|
853
|
+
),
|
|
854
|
+
),
|
|
855
|
+
),
|
|
715
856
|
)
|
|
716
857
|
: null,
|
|
717
|
-
|
|
858
|
+
(progress?.publicOnly ?? 0) > 0
|
|
859
|
+
? h(
|
|
860
|
+
'div',
|
|
861
|
+
{ className: 'mvp_usageHint' },
|
|
862
|
+
t('checker.publicOnly', { count: progress.publicOnly }),
|
|
863
|
+
)
|
|
864
|
+
: null,
|
|
865
|
+
errors.length > 0
|
|
866
|
+
? h(
|
|
867
|
+
'div',
|
|
868
|
+
{ className: 'mvp_checkerErrors' },
|
|
869
|
+
h('div', { className: 'mvp_checkerErrorsTitle' }, t('checker.errorTitle')),
|
|
870
|
+
h(
|
|
871
|
+
'ul',
|
|
872
|
+
{ className: 'mvp_errorList' },
|
|
873
|
+
visibleErrors.map((entry, index) =>
|
|
874
|
+
h('li', { key: index }, typeof entry === 'string' ? entry : String(entry)),
|
|
875
|
+
),
|
|
876
|
+
),
|
|
877
|
+
errors.length > visibleErrors.length
|
|
878
|
+
? h(
|
|
879
|
+
'div',
|
|
880
|
+
{ className: 'mvp_usageHint' },
|
|
881
|
+
t('checker.errorMore', { count: errors.length - visibleErrors.length }),
|
|
882
|
+
)
|
|
883
|
+
: null,
|
|
884
|
+
)
|
|
885
|
+
: null,
|
|
886
|
+
message !== null ? h('div', { className: 'mvp_banner mvp_banner--error' }, message) : null,
|
|
887
|
+
state.error !== null ? h('div', { className: 'mvp_banner mvp_banner--error' }, state.error) : null,
|
|
718
888
|
)
|
|
719
889
|
: null,
|
|
720
890
|
)
|
|
@@ -919,47 +1089,116 @@ window.__ModuleLoader__.load({
|
|
|
919
1089
|
// -------------------------------------------------------- css
|
|
920
1090
|
define("css", function (require, module, exports) {
|
|
921
1091
|
const CSS = `
|
|
1092
|
+
/* ---------------------------------------------------------------- shell */
|
|
922
1093
|
.mvp_page{box-sizing:border-box;width:100%;height:100%;min-height:0;flex-direction:column;display:flex;overflow-y:auto;padding-bottom:24px}
|
|
923
|
-
|
|
1094
|
+
|
|
1095
|
+
/* --------------------------------------------------------------- header */
|
|
1096
|
+
.mvp_head{flex:none;align-items:center;gap:12px;padding:2px 0 10px;display:flex;flex-wrap:wrap}
|
|
1097
|
+
.mvp_headMain{flex:1;min-width:0;align-items:baseline;gap:10px;display:flex;flex-wrap:wrap}
|
|
924
1098
|
.mvp_title{color:var(--dsw-alias-label-primary);margin:0;font-size:16px;font-weight:500;line-height:24px}
|
|
925
|
-
.
|
|
926
|
-
.
|
|
927
|
-
.
|
|
928
|
-
.
|
|
929
|
-
.
|
|
1099
|
+
.mvp_summary{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px;white-space:nowrap}
|
|
1100
|
+
.mvp_summary--dim{color:var(--dsw-alias-label-tertiary)}
|
|
1101
|
+
.mvp_headActions{flex:none;align-items:center;gap:6px;display:flex;flex-wrap:wrap}
|
|
1102
|
+
.mvp_about{border:.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-sm,6px);margin-bottom:10px;padding:8px 12px;background:transparent;background:color-mix(in srgb, var(--dsw-alias-label-primary) 2%, transparent)}
|
|
1103
|
+
.mvp_aboutP{color:var(--dsw-alias-label-secondary);margin:0 0 6px;font-size:12px;line-height:18px}
|
|
1104
|
+
.mvp_aboutP:last-child{margin-bottom:0}
|
|
1105
|
+
|
|
1106
|
+
/* -------------------------------------------------------------- buttons */
|
|
1107
|
+
.mvp_button{box-sizing:border-box;height:26px;color:var(--dsw-alias-label-primary);cursor:pointer;white-space:nowrap;background:0 0;border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-sm,6px);padding:0 9px;font-family:inherit;font-size:12px;line-height:18px}
|
|
1108
|
+
.mvp_button--quiet{color:var(--dsw-alias-label-secondary);border-color:transparent}
|
|
1109
|
+
.mvp_button--quiet:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l2)}
|
|
930
1110
|
.mvp_button--accent{color:var(--dsw-alias-brand-primary);border-color:color-mix(in srgb, var(--dsw-alias-brand-primary) 55%, transparent)}
|
|
931
1111
|
.mvp_button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(127,127,127,.12))}
|
|
932
1112
|
.mvp_button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}
|
|
933
|
-
.mvp_button:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}
|
|
1113
|
+
.mvp_button:disabled{color:var(--dsw-alias-label-tertiary);cursor:default;opacity:.6}
|
|
1114
|
+
|
|
1115
|
+
/* ---------------------------------------------------------------- usage */
|
|
1116
|
+
.mvp_usage{flex:none;color:var(--dsw-alias-label-secondary);align-items:center;gap:14px;border-bottom:.5px solid var(--dsw-alias-border-l1);padding:6px 0 10px;font-size:12px;line-height:18px;display:flex;flex-wrap:wrap}
|
|
1117
|
+
.mvp_usage--off{color:var(--dsw-alias-label-tertiary)}
|
|
1118
|
+
.mvp_usageLabel{color:var(--dsw-alias-label-tertiary);align-items:baseline;gap:6px;flex:none;display:inline-flex}
|
|
1119
|
+
.mvp_usageComputing{color:var(--dsw-alias-brand-primary);font-size:11px}
|
|
1120
|
+
.mvp_usageRanges{flex:1;min-width:0;align-items:baseline;gap:16px;display:flex;flex-wrap:wrap}
|
|
1121
|
+
.mvp_stat{align-items:baseline;gap:5px;display:inline-flex;white-space:nowrap}
|
|
1122
|
+
.mvp_statLabel{color:var(--dsw-alias-label-tertiary);font-size:11px}
|
|
1123
|
+
.mvp_statValue{color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;font-size:13px;font-weight:500}
|
|
1124
|
+
.mvp_stat--total .mvp_statValue{color:var(--dsw-alias-brand-primary)}
|
|
1125
|
+
.mvp_usageActions{flex:none;align-items:center;gap:4px;display:flex}
|
|
1126
|
+
.mvp_usageHint{width:100%;color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}
|
|
1127
|
+
.mvp_usageHint--warn{color:var(--dsw-alias-state-error-primary)}
|
|
1128
|
+
|
|
1129
|
+
/* --------------------------------------------------------------- filters */
|
|
1130
|
+
.mvp_filters{flex:none;align-items:center;gap:8px;border-bottom:.5px solid var(--dsw-alias-border-l2);padding:8px 0;display:flex;flex-wrap:wrap}
|
|
1131
|
+
.mvp_searchInput{box-sizing:border-box;flex:1 1 180px;min-width:150px;max-width:320px;height:26px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-sm,6px);padding:0 8px;font-family:inherit;font-size:12px}
|
|
1132
|
+
.mvp_searchInput:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}
|
|
1133
|
+
.mvp_filterGroup{align-items:center;gap:4px;display:flex;flex-wrap:wrap}
|
|
1134
|
+
.mvp_filterGroup--push{margin-left:auto}
|
|
1135
|
+
.mvp_filter{box-sizing:border-box;height:24px;color:var(--dsw-alias-label-secondary);cursor:pointer;white-space:nowrap;background:0 0;border:.5px solid var(--dsw-alias-border-l2);border-radius:999px;padding:0 9px;font-family:inherit;font-size:11px;line-height:16px}
|
|
1136
|
+
.mvp_filter[aria-pressed="true"]{color:var(--dsw-alias-brand-primary);background:transparent;background:color-mix(in srgb, var(--dsw-alias-brand-primary) 10%, transparent);border-color:color-mix(in srgb, var(--dsw-alias-brand-primary) 50%, transparent)}
|
|
1137
|
+
.mvp_filter:hover:not([aria-pressed="true"]){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-tertiary)}
|
|
1138
|
+
.mvp_filter:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}
|
|
1139
|
+
.mvp_contextSelect{box-sizing:border-box;height:24px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-base);border:.5px solid var(--dsw-alias-border-l2);border-radius:999px;padding:0 6px;font-family:inherit;font-size:11px}
|
|
1140
|
+
|
|
1141
|
+
/* ------------------------------------------------------- applyInput row */
|
|
1142
|
+
.mvp_applyRow{flex:none;align-items:center;gap:10px;border-bottom:.5px solid var(--dsw-alias-border-l1);padding:8px 0;display:flex}
|
|
1143
|
+
.mvp_applyText{flex:1;min-width:0;align-items:baseline;gap:8px;display:flex;flex-wrap:wrap}
|
|
1144
|
+
.mvp_applyTitle{color:var(--dsw-alias-label-primary);font-size:12px;line-height:18px}
|
|
1145
|
+
.mvp_applyHint{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}
|
|
1146
|
+
|
|
1147
|
+
/* --------------------------------------------------------------- banners */
|
|
1148
|
+
.mvp_banner{flex:none;color:var(--dsw-alias-label-secondary);border:.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-sm,6px);margin:8px 0;padding:6px 10px;font-size:12px;line-height:18px}
|
|
1149
|
+
.mvp_banner--warn{color:var(--dsw-alias-state-warn-primary);border-color:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 40%, transparent);background:transparent;background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 7%, transparent)}
|
|
1150
|
+
.mvp_banner--error{color:var(--dsw-alias-state-error-primary);border-color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 40%, transparent);background:transparent;background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 7%, transparent)}
|
|
1151
|
+
.mvp_note{flex:none;color:var(--dsw-alias-label-tertiary);padding:6px 0;font-size:11px;line-height:16px}
|
|
1152
|
+
|
|
1153
|
+
/* ---------------------------------------------------------------- groups */
|
|
1154
|
+
.mvp_groups{flex:none;flex-direction:column;display:flex}
|
|
934
1155
|
.mvp_group{flex:none;flex-direction:column;display:flex}
|
|
935
|
-
.mvp_groupHead{justify-content:space-between;align-items:center;gap:12px;padding:
|
|
936
|
-
.mvp_groupTitle{color:var(--dsw-alias-label-primary);align-items:baseline;row-gap:2px;
|
|
937
|
-
.mvp_groupName{
|
|
938
|
-
.
|
|
939
|
-
.
|
|
940
|
-
.
|
|
941
|
-
.mvp_groupCount{color:var(--dsw-alias-label-tertiary);font-size:
|
|
942
|
-
.
|
|
943
|
-
.
|
|
944
|
-
.
|
|
945
|
-
.
|
|
946
|
-
.
|
|
947
|
-
.
|
|
948
|
-
.
|
|
949
|
-
.
|
|
1156
|
+
.mvp_groupHead{justify-content:space-between;align-items:center;gap:12px;border-bottom:.5px solid var(--dsw-alias-border-l2);padding:10px 0 6px;display:flex}
|
|
1157
|
+
.mvp_groupTitle{flex:1;min-width:0;color:var(--dsw-alias-label-primary);cursor:pointer;text-align:left;background:0 0;border:0;align-items:baseline;column-gap:8px;row-gap:2px;padding:0;font-family:inherit;font-size:13px;font-weight:500;line-height:20px;display:flex;flex-wrap:wrap}
|
|
1158
|
+
.mvp_groupTitle:hover .mvp_groupName{color:var(--dsw-alias-brand-primary)}
|
|
1159
|
+
.mvp_groupTitle:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px;border-radius:4px}
|
|
1160
|
+
.mvp_chevron{flex:none;align-self:center;width:12px;color:var(--dsw-alias-label-tertiary);font-size:10px;line-height:16px}
|
|
1161
|
+
.mvp_groupName{min-width:0;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}
|
|
1162
|
+
.mvp_groupCount{color:var(--dsw-alias-label-tertiary);font-size:11px;font-weight:400;font-variant-numeric:tabular-nums}
|
|
1163
|
+
.mvp_groupCap{color:var(--dsw-alias-label-tertiary);align-items:baseline;gap:3px;font-size:11px;font-weight:400;display:inline-flex}
|
|
1164
|
+
.mvp_sep{opacity:.5}
|
|
1165
|
+
.mvp_kindCount{font-variant-numeric:tabular-nums}
|
|
1166
|
+
.mvp_kindCount--image{color:var(--dsw-alias-state-business-primary)}
|
|
1167
|
+
.mvp_kindCount--video{color:var(--dsw-alias-state-success-primary)}
|
|
1168
|
+
.mvp_kindCount--other{color:var(--dsw-alias-state-warn-primary)}
|
|
1169
|
+
.mvp_groupProbe{color:var(--dsw-alias-label-tertiary);border:.5px dashed var(--dsw-alias-border-l2);border-radius:4px;max-width:100%;white-space:nowrap;text-overflow:ellipsis;padding:0 5px;font-size:10px;line-height:15px;overflow:hidden}
|
|
1170
|
+
.mvp_groupProbe--warn{color:var(--dsw-alias-state-warn-primary);border-color:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 45%, transparent)}
|
|
1171
|
+
|
|
1172
|
+
/* ------------------------------------------------------------------ rows */
|
|
1173
|
+
.mvp_row{justify-content:space-between;align-items:center;gap:16px;border-bottom:.5px solid var(--dsw-alias-border-l1);padding:8px 0 8px 6px;display:flex}
|
|
1174
|
+
.mvp_row--off{opacity:.62}
|
|
1175
|
+
.mvp_row--gen{background:transparent;background:color-mix(in srgb, var(--dsw-alias-brand-primary) 3%, transparent)}
|
|
1176
|
+
.mvp_row--kind-image{box-shadow:inset 2px 0 0 color-mix(in srgb, var(--dsw-alias-state-business-primary) 70%, transparent)}
|
|
1177
|
+
.mvp_row--kind-video{box-shadow:inset 2px 0 0 color-mix(in srgb, var(--dsw-alias-state-success-primary) 70%, transparent)}
|
|
1178
|
+
.mvp_row--kind-embedding{box-shadow:inset 2px 0 0 color-mix(in srgb, var(--dsw-alias-state-warn-primary) 70%, transparent)}
|
|
1179
|
+
.mvp_row--kind-rerank{box-shadow:inset 2px 0 0 color-mix(in srgb, var(--dsw-alias-state-error-primary) 60%, transparent)}
|
|
1180
|
+
.mvp_row--kind-audio{box-shadow:inset 2px 0 0 color-mix(in srgb, var(--dsw-alias-state-business-primary) 50%, var(--dsw-alias-state-error-primary))}
|
|
1181
|
+
.mvp_row--kind-classifier{box-shadow:inset 2px 0 0 color-mix(in srgb, var(--dsw-alias-state-error-secondary) 60%, transparent)}
|
|
950
1182
|
.mvp_row--kind-translation{box-shadow:inset 2px 0 0 var(--dsw-alias-border-l2)}
|
|
951
|
-
.mvp_rowText{flex-direction:column;gap:
|
|
952
|
-
.
|
|
953
|
-
.
|
|
954
|
-
.
|
|
955
|
-
.
|
|
956
|
-
.
|
|
1183
|
+
.mvp_rowText{flex-direction:column;gap:3px;min-width:0;display:flex}
|
|
1184
|
+
.mvp_rowHead{align-items:baseline;gap:8px;min-width:0;display:flex;flex-wrap:wrap}
|
|
1185
|
+
.mvp_rowTitle{color:var(--dsw-alias-label-primary);font-size:13px;line-height:20px}
|
|
1186
|
+
.mvp_rowMeta{color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;min-width:0;max-width:100%;font-size:11px;line-height:18px;overflow:hidden}
|
|
1187
|
+
.mvp_rowActions{flex:none;align-items:center;gap:6px;display:flex}
|
|
1188
|
+
.mvp_editButton{height:24px;padding:0 8px;font-size:11px}
|
|
1189
|
+
|
|
1190
|
+
/* ---------------------------------------------------------------- switch */
|
|
1191
|
+
.mvp_switch{position:relative;flex:none;box-sizing:border-box;width:34px;height:19px;padding:0;border:0;border-radius:999px;background:var(--dsw-alias-border-l2);cursor:pointer;transition:background-color .15s ease}
|
|
1192
|
+
.mvp_switch[aria-checked="true"]{background:var(--dsw-alias-brand-primary)}
|
|
957
1193
|
.mvp_switch:disabled{opacity:.45;cursor:default}
|
|
958
1194
|
.mvp_switch:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}
|
|
959
|
-
.mvp_thumb{position:absolute;top:3px;left:3px;width:
|
|
960
|
-
.mvp_switch[aria-checked="true"] .mvp_thumb{
|
|
961
|
-
.
|
|
962
|
-
|
|
1195
|
+
.mvp_thumb{position:absolute;top:3px;left:3px;width:13px;height:13px;border-radius:50%;background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.25);transition:transform .15s ease}
|
|
1196
|
+
.mvp_switch[aria-checked="true"] .mvp_thumb{transform:translateX(15px)}
|
|
1197
|
+
.mvp_switch[aria-checked="false"] .mvp_thumb{background:var(--dsw-alias-label-secondary)}
|
|
1198
|
+
|
|
1199
|
+
/* ------------------------------------------------------------------ tags */
|
|
1200
|
+
.mvp_tags{flex-wrap:wrap;align-items:center;gap:4px;display:flex}
|
|
1201
|
+
.mvp_tag{box-sizing:border-box;max-width:100%;height:17px;color:var(--dsw-alias-label-secondary);white-space:nowrap;background:0 0;border:.5px solid var(--dsw-alias-border-l2);border-radius:4px;align-items:center;padding:0 5px;font-size:10px;line-height:15px;display:inline-flex;overflow:hidden;text-overflow:ellipsis}
|
|
963
1202
|
.mvp_tag--accent{color:var(--dsw-alias-brand-primary);background:transparent;background:color-mix(in srgb, var(--dsw-alias-brand-primary) 10%, transparent);border-color:color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, transparent)}
|
|
964
1203
|
.mvp_tag--vision{color:var(--dsw-alias-state-business-primary);background:transparent;background:color-mix(in srgb, var(--dsw-alias-state-business-primary) 10%, transparent);border-color:color-mix(in srgb, var(--dsw-alias-state-business-primary) 45%, transparent)}
|
|
965
1204
|
.mvp_tag--kind{font-weight:500}
|
|
@@ -973,198 +1212,61 @@ window.__ModuleLoader__.load({
|
|
|
973
1212
|
.mvp_tag--kind-unknown{color:var(--dsw-alias-label-secondary);border-color:var(--dsw-alias-border-l2)}
|
|
974
1213
|
.mvp_tag--inferred{border-style:dashed}
|
|
975
1214
|
.mvp_tag--muted{color:var(--dsw-alias-label-tertiary);border-color:var(--dsw-alias-border-l1)}
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
.
|
|
979
|
-
.
|
|
980
|
-
.
|
|
981
|
-
.mvp_applyText{flex-direction:column;gap:2px;min-width:0;display:flex}
|
|
982
|
-
.mvp_applyTitle{color:var(--dsw-alias-label-primary);font-size:12px;font-weight:500;line-height:18px}
|
|
983
|
-
.mvp_applyHint{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}
|
|
984
|
-
.mvp_applyRow .mvp_switch{margin-top:1px}
|
|
985
|
-
.mvp_groupProbe{color:var(--dsw-alias-label-tertiary);border:.5px dashed var(--dsw-alias-border-l2);border-radius:4px;max-width:100%;white-space:nowrap;text-overflow:ellipsis;padding:0 5px;font-size:10px;line-height:15px;overflow:hidden}
|
|
986
|
-
.mvp_filters{align-items:center;gap:6px;padding:8px 0 2px;display:flex;flex-wrap:wrap}
|
|
987
|
-
.mvp_filterLabel{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}
|
|
988
|
-
.mvp_search{align-items:center;gap:6px;display:flex;min-width:220px;flex:1 1 220px}
|
|
989
|
-
.mvp_searchInput{box-sizing:border-box;width:100%;height:28px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-sm,6px);padding:0 8px;font-family:inherit;font-size:12px;line-height:18px}
|
|
990
|
-
.mvp_searchInput:focus-visible,.mvp_contextSelect:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}
|
|
991
|
-
.mvp_context{align-items:center;gap:6px;display:flex}
|
|
992
|
-
.mvp_contextSelect{box-sizing:border-box;height:24px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-base);border:.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-sm,6px);padding:0 6px;font-family:inherit;font-size:11px;line-height:18px}
|
|
993
|
-
.mvp_filter{box-sizing:border-box;height:24px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:.5px solid var(--dsw-alias-border-l2);border-radius:999px;padding:0 9px;font-family:inherit;font-size:11px;line-height:18px}
|
|
994
|
-
.mvp_filter:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(127,127,127,.12))}
|
|
995
|
-
.mvp_filter[aria-pressed="true"]{color:var(--dsw-alias-brand-primary);border-color:color-mix(in srgb, var(--dsw-alias-brand-primary) 55%, transparent);background:transparent;background:color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent)}
|
|
996
|
-
.mvp_filter:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}
|
|
997
|
-
.mvp_note{color:var(--dsw-alias-label-tertiary);flex:none;padding:6px 0 0;font-size:10px;line-height:15px}
|
|
998
|
-
.mvp_legend{color:var(--dsw-alias-label-tertiary);flex:none;padding:8px 0 2px;font-size:11px;line-height:16px}
|
|
999
|
-
.mvp_fallback{color:var(--dsw-alias-label-secondary);background:transparent;background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 10%, transparent);border-radius:var(--dsw-radius-sm,6px);padding:6px 8px;font-size:11px;line-height:16px;margin-top:8px}
|
|
1000
|
-
.mvp_rowActions{flex:none;align-items:center;gap:8px;display:flex}
|
|
1001
|
-
.mvp_editButton{height:24px;padding:0 8px;font-size:11px;line-height:18px}
|
|
1002
|
-
.mvp_editButton[aria-pressed="true"]{color:var(--dsw-alias-brand-primary);border-color:color-mix(in srgb, var(--dsw-alias-brand-primary) 55%, transparent)}
|
|
1003
|
-
.mvp_editor{box-sizing:border-box;border:.5px solid color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, transparent);border-radius:var(--dsw-radius-sm,6px);background:transparent;background:color-mix(in srgb, var(--dsw-alias-brand-primary) 4%, transparent);margin:2px 0 12px;padding:10px 12px;flex-direction:column;gap:10px;display:flex}
|
|
1004
|
-
.mvp_editorHead{flex-direction:column;gap:2px;display:flex}
|
|
1005
|
-
.mvp_editorTitle{color:var(--dsw-alias-label-primary);font-size:12px;font-weight:500;line-height:18px}
|
|
1215
|
+
|
|
1216
|
+
/* --------------------------------------------------------------- editor */
|
|
1217
|
+
.mvp_editor{border:.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-sm,6px);margin:4px 0 8px;padding:10px 12px;background:transparent;background:color-mix(in srgb, var(--dsw-alias-label-primary) 2%, transparent)}
|
|
1218
|
+
.mvp_editorHead{margin-bottom:8px}
|
|
1219
|
+
.mvp_editorTitle{color:var(--dsw-alias-label-primary);font-size:13px;line-height:20px}
|
|
1006
1220
|
.mvp_editorSub{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}
|
|
1007
|
-
.mvp_editorGrid{flex-wrap:wrap;align-items:flex-
|
|
1008
|
-
.mvp_field{flex-direction:column;gap:
|
|
1221
|
+
.mvp_editorGrid{flex-wrap:wrap;align-items:flex-start;gap:8px 14px;display:flex}
|
|
1222
|
+
.mvp_field{flex-direction:column;gap:3px;min-width:140px;display:flex}
|
|
1009
1223
|
.mvp_field--wide{flex:1 1 100%}
|
|
1010
|
-
.mvp_fieldLabel{color:var(--dsw-alias-label-
|
|
1011
|
-
.mvp_select,.mvp_number{box-sizing:border-box;height:26px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-sm,6px);padding:0
|
|
1012
|
-
.
|
|
1013
|
-
.
|
|
1014
|
-
.
|
|
1015
|
-
.mvp_chip{box-sizing:border-box;height:24px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:.5px solid var(--dsw-alias-border-l2);border-radius:999px;padding:0 10px;font-family:inherit;font-size:11px;line-height:18px}
|
|
1016
|
-
.mvp_chip:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(127,127,127,.12))}
|
|
1017
|
-
.mvp_chip[aria-pressed="true"]{color:var(--dsw-alias-brand-primary);border-color:color-mix(in srgb, var(--dsw-alias-brand-primary) 55%, transparent);background:transparent;background:color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent)}
|
|
1224
|
+
.mvp_fieldLabel{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}
|
|
1225
|
+
.mvp_select,.mvp_number{box-sizing:border-box;height:26px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-sm,6px);padding:0 6px;font-family:inherit;font-size:12px}
|
|
1226
|
+
.mvp_chips{flex-wrap:wrap;gap:4px;display:flex}
|
|
1227
|
+
.mvp_chip{box-sizing:border-box;height:23px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:.5px solid var(--dsw-alias-border-l2);border-radius:999px;padding:0 9px;font-family:inherit;font-size:11px}
|
|
1228
|
+
.mvp_chip[aria-pressed="true"]{color:var(--dsw-alias-brand-primary);background:transparent;background:color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);border-color:color-mix(in srgb, var(--dsw-alias-brand-primary) 50%, transparent)}
|
|
1018
1229
|
.mvp_chip:disabled{opacity:.45;cursor:default}
|
|
1019
|
-
.
|
|
1020
|
-
.mvp_editorNote{color:var(--dsw-alias-label-tertiary);font-size:
|
|
1021
|
-
.
|
|
1022
|
-
.
|
|
1023
|
-
.mvp_editorActions{
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
.
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
.
|
|
1036
|
-
|
|
1037
|
-
}
|
|
1038
|
-
|
|
1039
|
-
.
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
.
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
.
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
.mvp_usageRanges {
|
|
1062
|
-
display: flex;
|
|
1063
|
-
gap: 18px;
|
|
1064
|
-
flex-wrap: wrap;
|
|
1065
|
-
margin-top: 8px;
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
.mvp_stat {
|
|
1069
|
-
display: flex;
|
|
1070
|
-
flex-direction: column;
|
|
1071
|
-
gap: 2px;
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
.mvp_statLabel {
|
|
1075
|
-
font-size: 11px;
|
|
1076
|
-
color: var(--dsw-alias-text-tertiary, currentColor);
|
|
1077
|
-
}
|
|
1078
|
-
|
|
1079
|
-
.mvp_statValue {
|
|
1080
|
-
font-size: 12px;
|
|
1081
|
-
font-variant-numeric: tabular-nums;
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
|
-
.mvp_stat--total .mvp_statValue {
|
|
1085
|
-
font-weight: 600;
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
.mvp_usageHint {
|
|
1089
|
-
margin-top: 8px;
|
|
1090
|
-
font-size: 11px;
|
|
1091
|
-
color: var(--dsw-alias-text-tertiary, currentColor);
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
.mvp_usageHint--warn {
|
|
1095
|
-
color: var(--dsw-alias-danger, #d0605e);
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
.mvp_checker {
|
|
1099
|
-
margin: 0 0 12px;
|
|
1100
|
-
padding: 10px 12px;
|
|
1101
|
-
border: 1px solid var(--dsw-alias-border, transparent);
|
|
1102
|
-
border-radius: 10px;
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
|
-
.mvp_checkerHead {
|
|
1106
|
-
display: flex;
|
|
1107
|
-
width: 100%;
|
|
1108
|
-
align-items: center;
|
|
1109
|
-
gap: 10px;
|
|
1110
|
-
padding: 0;
|
|
1111
|
-
border: 0;
|
|
1112
|
-
background: transparent;
|
|
1113
|
-
color: inherit;
|
|
1114
|
-
text-align: left;
|
|
1115
|
-
cursor: pointer;
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
|
-
.mvp_checkerTitle {
|
|
1119
|
-
font-weight: 600;
|
|
1120
|
-
font-size: 12px;
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
|
-
.mvp_checkerSummary {
|
|
1124
|
-
margin-left: auto;
|
|
1125
|
-
font-size: 11px;
|
|
1126
|
-
color: var(--dsw-alias-text-tertiary, currentColor);
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
.mvp_checker--closed.mvp_checker {
|
|
1130
|
-
padding: 8px 12px;
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
|
-
.mvp_checkerBody {
|
|
1134
|
-
margin-top: 10px;
|
|
1135
|
-
display: flex;
|
|
1136
|
-
flex-direction: column;
|
|
1137
|
-
gap: 10px;
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
.mvp_checkerHint {
|
|
1141
|
-
font-size: 11px;
|
|
1142
|
-
color: var(--dsw-alias-text-tertiary, currentColor);
|
|
1143
|
-
}
|
|
1144
|
-
|
|
1145
|
-
.mvp_checkerForm {
|
|
1146
|
-
display: grid;
|
|
1147
|
-
grid-template-columns: 1fr 1fr;
|
|
1148
|
-
gap: 10px;
|
|
1149
|
-
max-width: 560px;
|
|
1150
|
-
}
|
|
1151
|
-
|
|
1152
|
-
.mvp_checkerActions {
|
|
1153
|
-
display: flex;
|
|
1154
|
-
align-items: center;
|
|
1155
|
-
gap: 10px;
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
|
-
.mvp_checkerSwitchText {
|
|
1159
|
-
font-size: 12px;
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
.mvp_checkerProgress {
|
|
1163
|
-
font-size: 11px;
|
|
1164
|
-
font-variant-numeric: tabular-nums;
|
|
1165
|
-
color: var(--dsw-alias-text-secondary, currentColor);
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1230
|
+
.mvp_check{color:var(--dsw-alias-label-secondary);align-items:center;gap:6px;font-size:12px;line-height:18px;display:inline-flex;cursor:pointer}
|
|
1231
|
+
.mvp_editorNote{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}
|
|
1232
|
+
.mvp_editorError{color:var(--dsw-alias-state-error-primary);font-size:11px;line-height:16px;margin-top:6px}
|
|
1233
|
+
.mvp_editorFooter{justify-content:space-between;align-items:flex-end;gap:12px;margin-top:10px;display:flex;flex-wrap:wrap}
|
|
1234
|
+
.mvp_editorActions{align-items:center;gap:6px;display:flex}
|
|
1235
|
+
|
|
1236
|
+
/* --------------------------------------------------------------- checker */
|
|
1237
|
+
.mvp_checker{flex:none;border:.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-sm,6px);margin-top:12px;overflow:hidden}
|
|
1238
|
+
.mvp_checker--closed{background:transparent}
|
|
1239
|
+
.mvp_checkerHead{box-sizing:border-box;width:100%;color:var(--dsw-alias-label-primary);cursor:pointer;text-align:left;background:0 0;border:0;align-items:baseline;gap:8px;padding:7px 10px;font-family:inherit;font-size:12px;line-height:18px;display:flex;flex-wrap:wrap}
|
|
1240
|
+
.mvp_checkerHead:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(127,127,127,.08))}
|
|
1241
|
+
.mvp_checkerHead:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}
|
|
1242
|
+
.mvp_checkerTitle{font-weight:500;flex:none}
|
|
1243
|
+
.mvp_checkerSummary{color:var(--dsw-alias-label-tertiary);flex:1;min-width:0;font-size:11px}
|
|
1244
|
+
.mvp_badge{flex:none;color:var(--dsw-alias-state-warn-primary);border:.5px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary) 45%, transparent);border-radius:999px;padding:0 6px;font-size:10px;line-height:15px}
|
|
1245
|
+
.mvp_checkerBody{border-top:.5px solid var(--dsw-alias-border-l2);padding:10px}
|
|
1246
|
+
.mvp_checkerHint{color:var(--dsw-alias-label-tertiary);margin:0 0 8px;font-size:11px;line-height:16px}
|
|
1247
|
+
.mvp_checkerForm{align-items:flex-end;gap:10px;display:flex;flex-wrap:wrap}
|
|
1248
|
+
.mvp_checkerProgress{align-items:center;gap:10px;margin-top:10px;display:flex;flex-wrap:wrap}
|
|
1249
|
+
.mvp_progressBar{flex:1 1 160px;min-width:120px;height:4px;border-radius:999px;background:var(--dsw-alias-border-l2);overflow:hidden}
|
|
1250
|
+
.mvp_progressFill{height:100%;background:var(--dsw-alias-brand-primary);transition:width .2s ease;display:block}
|
|
1251
|
+
.mvp_checkerProgressText{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}
|
|
1252
|
+
.mvp_checkerErrors{margin-top:10px}
|
|
1253
|
+
.mvp_checkerErrorsTitle{color:var(--dsw-alias-state-warn-primary);margin-bottom:4px;font-size:11px;line-height:16px}
|
|
1254
|
+
.mvp_errorList{max-height:150px;color:var(--dsw-alias-label-secondary);margin:0;padding-left:18px;font-size:11px;line-height:16px;overflow-y:auto}
|
|
1255
|
+
.mvp_errorList li{margin-bottom:3px;word-break:break-word}
|
|
1256
|
+
.mvp_sources{margin-top:10px}
|
|
1257
|
+
.mvp_sourcesTitle{color:var(--dsw-alias-label-tertiary);margin-bottom:4px;font-size:11px;line-height:16px}
|
|
1258
|
+
.mvp_sourceList{max-height:150px;margin:0;padding:0;list-style:none;font-size:11px;line-height:16px;overflow-y:auto}
|
|
1259
|
+
.mvp_sourceList li{flex-wrap:wrap;align-items:baseline;gap:6px;padding:2px 0;display:flex}
|
|
1260
|
+
.mvp_sourceName{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums}
|
|
1261
|
+
.mvp_sourceState{flex:none;border-radius:4px;padding:0 4px;font-size:10px;line-height:14px;border:.5px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary)}
|
|
1262
|
+
.mvp_source--ok .mvp_sourceState{color:var(--dsw-alias-state-success-primary);border-color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 45%, transparent)}
|
|
1263
|
+
.mvp_source--bad .mvp_sourceState{color:var(--dsw-alias-state-warn-primary);border-color:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 45%, transparent)}
|
|
1264
|
+
.mvp_sourceCount{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums}
|
|
1265
|
+
.mvp_sourceMsg{flex:1 1 100%;color:var(--dsw-alias-label-tertiary);font-size:10px;line-height:15px}
|
|
1266
|
+
|
|
1267
|
+
/* ---------------------------------------------------------------- footer */
|
|
1268
|
+
.mvp_footer{flex:none;align-items:center;gap:14px;color:var(--dsw-alias-label-tertiary);border-top:.5px solid var(--dsw-alias-border-l1);margin-top:12px;padding-top:8px;font-size:11px;line-height:16px;display:flex;flex-wrap:wrap}
|
|
1269
|
+
.mvp_footerItem{min-width:0;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}
|
|
1168
1270
|
`
|
|
1169
1271
|
|
|
1170
1272
|
|
|
@@ -1178,10 +1280,12 @@ window.__ModuleLoader__.load({
|
|
|
1178
1280
|
'nav': '模型显示',
|
|
1179
1281
|
'page.title': '模型显示',
|
|
1180
1282
|
'page.description':
|
|
1181
|
-
'
|
|
1182
|
-
'page.
|
|
1183
|
-
'page.
|
|
1184
|
-
'page.
|
|
1283
|
+
'关闭的模型不会出现在聊天输入框的模型下拉列表中;这里的改动立即生效,之后仍可随时重新打开。每个模型标注真实能力:适配器声明优先,其次读中转站模型列表,最后用校验结果或模型名补全。',
|
|
1284
|
+
'page.about': '说明',
|
|
1285
|
+
'page.aboutTitle': '展开标签含义、来源与颜色说明',
|
|
1286
|
+
'page.summary': '已显示 {visible} / {total}',
|
|
1287
|
+
'page.hiddenSummary': '已关闭 {count}',
|
|
1288
|
+
'page.kindSummary': '生图/生视频 {count}',
|
|
1185
1289
|
'page.loading': '正在加载模型列表…',
|
|
1186
1290
|
'page.empty': '暂无可用模型。',
|
|
1187
1291
|
'page.filterEmpty': '当前筛选下没有模型。',
|
|
@@ -1189,31 +1293,33 @@ window.__ModuleLoader__.load({
|
|
|
1189
1293
|
'page.saveFailed': '保存失败,请重试。',
|
|
1190
1294
|
'page.refresh': '刷新',
|
|
1191
1295
|
'page.refreshing': '刷新中…',
|
|
1296
|
+
'page.refreshTitle': '重新读取适配器与中转站的能力信息(沿用缓存,10 分钟内的探测不会重发请求)',
|
|
1192
1297
|
'page.reload': '重新获取能力',
|
|
1193
1298
|
'page.reloading': '正在获取…',
|
|
1194
1299
|
'page.reloadTitle':
|
|
1195
1300
|
'清空本插件的能力缓存和正在进行的读取,重新询问适配器与所有中转站;不会改动任何模型开关。适配器没有公布的思考强度仍不会凭空出现。',
|
|
1196
1301
|
'page.reloadUnavailable': 'Host 半不支持强制重新获取;重启 DSH 后可用。',
|
|
1197
|
-
'page.collapseGroups': '
|
|
1198
|
-
'page.expandGroups': '
|
|
1302
|
+
'page.collapseGroups': '折叠全部',
|
|
1303
|
+
'page.expandGroups': '展开全部',
|
|
1199
1304
|
'page.collapseGroupsTitle': '只保留分组标题,方便跳到其他分组',
|
|
1200
1305
|
'page.expandGroupsTitle': '展开所有被折叠的分组',
|
|
1201
1306
|
'page.showAll': '全部显示',
|
|
1202
1307
|
'page.legend':
|
|
1203
|
-
'
|
|
1308
|
+
'标签:推理 = 可调推理等级;上下文 = 输入窗口;多模态 = 可输入图像。实线来自适配器、中转站或厂商文档,虚线来自联网校验或模型名推断。生图 / 生视频 / 嵌入等只标类型,不显示思考强度与文本标签。',
|
|
1204
1309
|
'page.metaFallback':
|
|
1205
1310
|
'模型能力信息暂不可用:上下文与模态显示为未知,推理档位仍来自官方目录。',
|
|
1206
1311
|
'page.hostStale':
|
|
1207
1312
|
'Host 半仍是旧版本:颜色与开关已更新,但「放行图像」和官方校正要等 DSH 重启(或 profile 重组)后才会生效。',
|
|
1208
|
-
'page.providerFailures': '
|
|
1209
|
-
'page.
|
|
1210
|
-
'page.
|
|
1211
|
-
'page.
|
|
1212
|
-
'page.
|
|
1213
|
-
'
|
|
1313
|
+
'page.providerFailures': '部分提供方读不到模型:{names}',
|
|
1314
|
+
'page.probeFailures': '中转站读取失败:{names}',
|
|
1315
|
+
'page.probeSummary': '探测:{ok} 个中转站可读',
|
|
1316
|
+
'page.probeSummaryTitle': '中转站探测结果;悬停分组上的标记可看具体诊断',
|
|
1317
|
+
'page.probeSummaryNone': '探测:未配置可探测的路由',
|
|
1318
|
+
'page.learnedNote': '学习快照 {models} 条({date})',
|
|
1319
|
+
'usage.title': 'token',
|
|
1214
1320
|
'usage.computing': '统计中…',
|
|
1215
1321
|
'usage.none': '—',
|
|
1216
|
-
'usage.
|
|
1322
|
+
'usage.detail': '输入 {input} · 输出 {output} · 缓存读 {cacheRead} · 缓存写 {cacheWrite}',
|
|
1217
1323
|
'usage.today': '今天',
|
|
1218
1324
|
'usage.todayTitle': '本地自然日(今天 0 点起)',
|
|
1219
1325
|
'usage.week': '本周',
|
|
@@ -1222,47 +1328,62 @@ window.__ModuleLoader__.load({
|
|
|
1222
1328
|
'usage.monthTitle': '本月 1 日 0 点起',
|
|
1223
1329
|
'usage.total': '总计',
|
|
1224
1330
|
'usage.totalTitle': '全部历史(更早的按会话创建日粗算)',
|
|
1225
|
-
'usage.exportUsage': '
|
|
1331
|
+
'usage.exportUsage': '用量 CSV',
|
|
1226
1332
|
'usage.exportUsageTitle': '下载按天分桶、含每模型 token 分解的 CSV',
|
|
1227
|
-
'usage.exportModels': '
|
|
1333
|
+
'usage.exportModels': '模型 CSV',
|
|
1228
1334
|
'usage.exportModelsTitle': '下载全部提供商模型的可见性与能力表',
|
|
1229
1335
|
'usage.hostStale': '宿主半尚未提供用量统计,重启 DSH 即可启用。',
|
|
1230
|
-
'checker.title': '
|
|
1336
|
+
'checker.title': '联网校验',
|
|
1231
1337
|
'checker.description':
|
|
1232
|
-
'
|
|
1338
|
+
'选定一个校验员模型:先拉 models.dev / OpenRouter / Hugging Face 三个公开目录做基准,再把其余模型分批交给它核对。公开目录已经知道的能力会直接采用,校验员答不上时也不例外。校验请求会消耗所选模型的额度,其 token 计入上方统计。提示:思考型模型会先花掉输出预算,若批次普遍失败,换一个非思考模型或降低其思考档位。',
|
|
1233
1339
|
'checker.provider': '校验员提供商',
|
|
1234
1340
|
'checker.model': '校验员模型',
|
|
1235
1341
|
'checker.start': '开始校验',
|
|
1236
1342
|
'checker.startTitle': '在宿主侧后台跑一轮全模型校验',
|
|
1237
1343
|
'checker.cancel': '取消',
|
|
1238
1344
|
'checker.cancelTitle': '中止正在进行的校验',
|
|
1239
|
-
'checker.writeBack': '
|
|
1345
|
+
'checker.writeBack': '同时写入提供商配置',
|
|
1240
1346
|
'checker.writeBackTitle':
|
|
1241
|
-
'开启:判定到的 input / contextWindow / 推理档位直接写进各提供商该模型条目(learned.json
|
|
1347
|
+
'开启:判定到的 input / contextWindow / 推理档位直接写进各提供商该模型条目(learned.json 只留写不进去的字段);关闭:只更新本页与快照。',
|
|
1242
1348
|
'checker.idle': '未运行',
|
|
1243
|
-
'checker.running': '进行中
|
|
1244
|
-
'checker.finished': '已完成 {done}/{total} ·
|
|
1349
|
+
'checker.running': '进行中{phase} · {done}/{total} · 已耗 {tokens}',
|
|
1350
|
+
'checker.finished': '已完成 {done}/{total} · 耗 {tokens}',
|
|
1245
1351
|
'checker.progress': '{done}/{total} 已核对 · 校验本身消耗 {tokens} token',
|
|
1246
|
-
'checker.
|
|
1247
|
-
'checker.
|
|
1248
|
-
'checker.
|
|
1352
|
+
'checker.errorBadge': '{count} 个批次失败',
|
|
1353
|
+
'checker.errorTitle': '失败批次',
|
|
1354
|
+
'checker.errorMore': '另有 {count} 条未显示',
|
|
1355
|
+
'checker.concurrency': '并发线程',
|
|
1356
|
+
'checker.concurrencyTitle': '同时发出的校验请求数。中转站限流时调低,网络快时调高(上限 8)。',
|
|
1357
|
+
'checker.concurrencyValue': '{count} 路',
|
|
1358
|
+
'checker.threads': '并发 {count} 路',
|
|
1359
|
+
'checker.sourcesTitle': '公开目录',
|
|
1360
|
+
'checker.source.ok': '已读取',
|
|
1361
|
+
'checker.source.empty': '无匹配',
|
|
1362
|
+
'checker.source.http': 'HTTP 错误',
|
|
1363
|
+
'checker.source.failed': '请求失败',
|
|
1364
|
+
'checker.source.bad-json': '非 JSON',
|
|
1365
|
+
'checker.source.too-large': '响应过大',
|
|
1366
|
+
'checker.source.pending': '等待中',
|
|
1367
|
+
'checker.sourceModels': '{count} 条',
|
|
1368
|
+
'checker.publicOnly': '其中 {count} 个模型的能力来自公开目录(校验员未作答)。',
|
|
1249
1369
|
'checker.hostStale': '宿主半不支持联网校验,请重启 DSH 后使用。',
|
|
1250
1370
|
'checker.phase.public': '(读取公开目录)',
|
|
1251
1371
|
'checker.phase.checker': '(读取公开目录)',
|
|
1252
1372
|
'checker.phase.verifying': '(分批校验)',
|
|
1253
1373
|
'source.snapshot': '已校验',
|
|
1254
1374
|
'page.applyInput': '让输入框按能力标注放行图像',
|
|
1375
|
+
'page.applyInputShort': '多模态中转站模型自动补 image;关闭则只按 Provider 的 input 声明',
|
|
1255
1376
|
'page.applyInputHint':
|
|
1256
1377
|
'开启后,被标注为「多模态」的中转站对话模型会在发送请求时自动补上图像输入能力;关闭则完全按 Provider 配置里的 input 声明。只影响中转站(pi-ai)路由,不改任何配置文件;若某个上游实际拒绝图像,关闭此开关即可。',
|
|
1257
1378
|
'page.kindLegend':
|
|
1258
|
-
'
|
|
1259
|
-
'page.
|
|
1260
|
-
'page.searchPlaceholder': '搜索模型名称或 ID',
|
|
1261
|
-
'page.capabilityLabel': '能力',
|
|
1379
|
+
'类型配色:生图 蓝 · 生视频 绿 · 嵌入 橙 · 音频 紫 · 重排 红 · 审核 玫红 · 翻译 灰。「官方校正」表示目录与厂商文档不一致时以文档为准。',
|
|
1380
|
+
'page.searchPlaceholder': '搜索名称或 ID',
|
|
1262
1381
|
'page.capability.multimodal': '多模态',
|
|
1382
|
+
'page.capability.multimodalTitle': '只看可输入图像的模型',
|
|
1263
1383
|
'page.capability.reasoning': '支持推理',
|
|
1384
|
+
'page.capability.reasoningTitle': '只看公布了推理能力的模型',
|
|
1264
1385
|
'page.contextLabel': '上下文至少',
|
|
1265
|
-
'page.context.any': '
|
|
1386
|
+
'page.context.any': '上下文不限',
|
|
1266
1387
|
'page.context.128k': '128K+',
|
|
1267
1388
|
'page.context.256k': '256K+',
|
|
1268
1389
|
'page.context.512k': '512K+',
|
|
@@ -1274,15 +1395,15 @@ window.__ModuleLoader__.load({
|
|
|
1274
1395
|
'page.filter.other': '其它',
|
|
1275
1396
|
'group.showAll': '全组显示',
|
|
1276
1397
|
'group.hideAll': '全组隐藏',
|
|
1277
|
-
'group.showFiltered': '
|
|
1278
|
-
'group.hideFiltered': '
|
|
1398
|
+
'group.showFiltered': '显示结果',
|
|
1399
|
+
'group.hideFiltered': '隐藏结果',
|
|
1279
1400
|
'group.collapseNamed': '折叠 {name} 分组',
|
|
1280
1401
|
'group.expandNamed': '展开 {name} 分组',
|
|
1281
|
-
'group.
|
|
1282
|
-
'group.
|
|
1283
|
-
'group.
|
|
1284
|
-
'group.probeFailed': '
|
|
1285
|
-
'group.probeSkipped': '
|
|
1402
|
+
'group.probeOk': '{models} 个模型 · {caps} 带能力',
|
|
1403
|
+
'group.probeNoCaps': '{models} 个模型 · 无能力字段',
|
|
1404
|
+
'group.probePartial': '价格接口 {models} 个',
|
|
1405
|
+
'group.probeFailed': '读取失败',
|
|
1406
|
+
'group.probeSkipped': '未探测',
|
|
1286
1407
|
'tag.reasoning': '支持推理',
|
|
1287
1408
|
'tag.noReasoning': '不支持推理',
|
|
1288
1409
|
'tag.reasoningUnknown': '推理未知',
|
|
@@ -1305,7 +1426,6 @@ window.__ModuleLoader__.load({
|
|
|
1305
1426
|
'tag.kindVideoTitle': '视频生成模型(输出视频);类型来源:{source}',
|
|
1306
1427
|
'tag.correction': '官方校正',
|
|
1307
1428
|
'tag.correctionTitle': '{note}\n来源:{source}',
|
|
1308
|
-
'tag.resolution': '分辨率',
|
|
1309
1429
|
'tag.resolutionTitle': '模型名标注了 {value} 输出档位',
|
|
1310
1430
|
'tag.imageInput': '可改图',
|
|
1311
1431
|
'tag.imageInputTitle': '支持图像输入(图生图 / 参考图)',
|
|
@@ -1388,10 +1508,12 @@ window.__ModuleLoader__.load({
|
|
|
1388
1508
|
'nav': 'Model visibility',
|
|
1389
1509
|
'page.title': 'Model visibility',
|
|
1390
1510
|
'page.description':
|
|
1391
|
-
'Models turned off here disappear from the chat composer model list. Changes apply immediately and can be turned back on at any time. Every model shows its real capabilities: adapter declarations first, then the relay model list, then
|
|
1392
|
-
'page.
|
|
1511
|
+
'Models turned off here disappear from the chat composer model list. Changes apply immediately and can be turned back on at any time. Every model shows its real capabilities: adapter declarations first, then the relay model list, then verification results or the model name.',
|
|
1512
|
+
'page.about': 'About',
|
|
1513
|
+
'page.aboutTitle': 'Show what the tags, sources and colours mean',
|
|
1514
|
+
'page.summary': 'Showing {visible} of {total}',
|
|
1393
1515
|
'page.hiddenSummary': '{count} hidden',
|
|
1394
|
-
'page.kindSummary': '{count} image/video
|
|
1516
|
+
'page.kindSummary': '{count} image/video',
|
|
1395
1517
|
'page.loading': 'Loading model list…',
|
|
1396
1518
|
'page.empty': 'No models available.',
|
|
1397
1519
|
'page.filterEmpty': 'No models match this filter.',
|
|
@@ -1399,80 +1521,99 @@ window.__ModuleLoader__.load({
|
|
|
1399
1521
|
'page.saveFailed': 'Could not save this change. Try again.',
|
|
1400
1522
|
'page.refresh': 'Refresh',
|
|
1401
1523
|
'page.refreshing': 'Refreshing…',
|
|
1524
|
+
'page.refreshTitle': 'Read adapter and relay capabilities again (honours caches; a probe younger than 10 minutes is not repeated)',
|
|
1402
1525
|
'page.reload': 'Re-fetch capabilities',
|
|
1403
1526
|
'page.reloading': 'Re-fetching…',
|
|
1404
1527
|
'page.reloadTitle':
|
|
1405
1528
|
'Drops this plugin’s capability caches and any in-flight read, then asks the adapter and every relay again. Model switches are untouched; effort levels the adapter does not publish still cannot appear.',
|
|
1406
1529
|
'page.reloadUnavailable': 'The Host half does not support a hard re-fetch; restart DSH to enable it.',
|
|
1407
|
-
'page.collapseGroups': 'Collapse all
|
|
1408
|
-
'page.expandGroups': 'Expand all
|
|
1530
|
+
'page.collapseGroups': 'Collapse all',
|
|
1531
|
+
'page.expandGroups': 'Expand all',
|
|
1409
1532
|
'page.collapseGroupsTitle': 'Keep only group headers so other groups are easy to reach',
|
|
1410
1533
|
'page.expandGroupsTitle': 'Expand every collapsed group',
|
|
1411
1534
|
'page.showAll': 'Show all',
|
|
1412
1535
|
'page.legend':
|
|
1413
|
-
'Tags: Reasoning = adjustable levels; Context = input window; Multimodal = accepts images. Solid tags come from the adapter, the relay or vendor documentation; dashed tags come from
|
|
1536
|
+
'Tags: Reasoning = adjustable levels; Context = input window; Multimodal = accepts images. Solid tags come from the adapter, the relay or vendor documentation; dashed tags come from online verification or the model name. Image, video, embedding and other models show only their kind.',
|
|
1414
1537
|
'page.metaFallback':
|
|
1415
1538
|
'Model capability details are unavailable right now: context and modalities show as unknown, while effort levels still come from the shipped catalog.',
|
|
1416
1539
|
'page.hostStale':
|
|
1417
1540
|
'The Host half is still the previous version: the colours and switch are live, but image admission and vendor corrections take effect after DSH restarts (or the profile recomposes).',
|
|
1418
1541
|
'page.providerFailures': 'Some providers could not be read: {names}',
|
|
1419
|
-
'page.
|
|
1420
|
-
'page.
|
|
1421
|
-
'page.
|
|
1422
|
-
'page.
|
|
1423
|
-
'
|
|
1542
|
+
'page.probeFailures': 'Relay read failed: {names}',
|
|
1543
|
+
'page.probeSummary': 'Probe: {ok} relays readable',
|
|
1544
|
+
'page.probeSummaryTitle': 'Relay probe result; hover a group badge for its diagnostics',
|
|
1545
|
+
'page.probeSummaryNone': 'Probe: no route configured to probe',
|
|
1546
|
+
'page.learnedNote': 'Learned {models} facts ({date})',
|
|
1547
|
+
'usage.title': 'tokens',
|
|
1424
1548
|
'usage.computing': 'scanning…',
|
|
1425
1549
|
'usage.none': '—',
|
|
1426
|
-
'usage.
|
|
1550
|
+
'usage.detail': 'input {input} · output {output} · cache read {cacheRead} · cache write {cacheWrite}',
|
|
1427
1551
|
'usage.today': 'Today',
|
|
1552
|
+
'usage.week': 'Week',
|
|
1553
|
+
'usage.month': 'Month',
|
|
1554
|
+
'usage.total': 'Total',
|
|
1428
1555
|
'usage.todayTitle': 'Local calendar day',
|
|
1429
|
-
'usage.week': 'This week',
|
|
1430
1556
|
'usage.weekTitle': 'Since Monday 00:00 local',
|
|
1431
|
-
'usage.month': 'This month',
|
|
1432
1557
|
'usage.monthTitle': 'Since the 1st local',
|
|
1433
|
-
'usage.total': 'Total',
|
|
1434
1558
|
'usage.totalTitle': 'All history (older months attributed by session start day)',
|
|
1435
|
-
'usage.exportUsage': '
|
|
1559
|
+
'usage.exportUsage': 'Usage CSV',
|
|
1436
1560
|
'usage.exportUsageTitle': 'Download a day-bucketed CSV with per-model breakdown',
|
|
1437
|
-
'usage.exportModels': '
|
|
1561
|
+
'usage.exportModels': 'Models CSV',
|
|
1438
1562
|
'usage.exportModelsTitle': 'Download visibility and capability table of every provider model',
|
|
1439
1563
|
'usage.hostStale': 'The Host half does not provide usage stats yet; restart DSH to enable it.',
|
|
1440
|
-
'checker.title': '
|
|
1564
|
+
'checker.title': 'Online verification',
|
|
1441
1565
|
'checker.description':
|
|
1442
|
-
'Pick one model as the checker:
|
|
1566
|
+
'Pick one model as the checker: three public catalogs (models.dev / OpenRouter / Hugging Face) are fetched first, then the remaining models are verified in batches. Whatever the catalogs already know is applied directly, even when the checker has no answer. A sweep spends the checker route quota and its tokens count in the strip above. Note: a thinking model spends the output budget before it answers, so if batches keep failing pick a non-thinking checker or lower its effort.',
|
|
1443
1567
|
'checker.provider': 'Checker provider',
|
|
1444
1568
|
'checker.model': 'Checker model',
|
|
1445
|
-
'checker.start': '
|
|
1569
|
+
'checker.start': 'Start',
|
|
1446
1570
|
'checker.startTitle': 'Run a background verification sweep in the Host',
|
|
1447
1571
|
'checker.cancel': 'Cancel',
|
|
1448
1572
|
'checker.cancelTitle': 'Abort the running sweep',
|
|
1449
|
-
'checker.writeBack': '
|
|
1573
|
+
'checker.writeBack': 'Also write into provider settings',
|
|
1450
1574
|
'checker.writeBackTitle':
|
|
1451
|
-
'On: the judged input / contextWindow / efforts are written into each provider entry (learned.json keeps only what cannot be mapped). Off: only this page and the snapshot update
|
|
1575
|
+
'On: the judged input / contextWindow / efforts are written into each provider entry (learned.json keeps only what cannot be mapped). Off: only this page and the snapshot update.',
|
|
1452
1576
|
'checker.idle': 'not run yet',
|
|
1453
|
-
'checker.running': 'running
|
|
1454
|
-
'checker.finished': 'finished {done}/{total} ·
|
|
1577
|
+
'checker.running': 'running{phase} · {done}/{total} · {tokens} spent',
|
|
1578
|
+
'checker.finished': 'finished {done}/{total} · {tokens} spent',
|
|
1455
1579
|
'checker.progress': '{done}/{total} verified · checker spent {tokens} tokens',
|
|
1456
|
-
'checker.
|
|
1457
|
-
'checker.
|
|
1458
|
-
'checker.
|
|
1580
|
+
'checker.errorBadge': '{count} failed batches',
|
|
1581
|
+
'checker.errorTitle': 'Failed batches',
|
|
1582
|
+
'checker.errorMore': '{count} more not shown',
|
|
1583
|
+
'checker.concurrency': 'Threads',
|
|
1584
|
+
'checker.concurrencyTitle':
|
|
1585
|
+
'How many checker requests may be in flight at once. Lower it if the relay rate-limits you, raise it on a fast link (max 8).',
|
|
1586
|
+
'checker.concurrencyValue': '{count}',
|
|
1587
|
+
'checker.threads': '{count} in parallel',
|
|
1588
|
+
'checker.sourcesTitle': 'Public catalogs',
|
|
1589
|
+
'checker.source.ok': 'read',
|
|
1590
|
+
'checker.source.empty': 'no match',
|
|
1591
|
+
'checker.source.http': 'HTTP error',
|
|
1592
|
+
'checker.source.failed': 'request failed',
|
|
1593
|
+
'checker.source.bad-json': 'not JSON',
|
|
1594
|
+
'checker.source.too-large': 'response too large',
|
|
1595
|
+
'checker.source.pending': 'pending',
|
|
1596
|
+
'checker.sourceModels': '{count}',
|
|
1597
|
+
'checker.publicOnly': '{count} models were resolved from a public catalog alone (the checker had no answer).',
|
|
1459
1598
|
'checker.hostStale': 'The Host half does not support online verification yet; restart DSH.',
|
|
1460
|
-
'checker.phase.public': '(fetching public catalogs)',
|
|
1461
|
-
'checker.phase.checker': '(fetching public catalogs)',
|
|
1462
|
-
'checker.phase.verifying': '(verifying in batches)',
|
|
1599
|
+
'checker.phase.public': ' (fetching public catalogs)',
|
|
1600
|
+
'checker.phase.checker': ' (fetching public catalogs)',
|
|
1601
|
+
'checker.phase.verifying': ' (verifying in batches)',
|
|
1463
1602
|
'source.snapshot': 'verified',
|
|
1464
1603
|
'page.applyInput': 'Let the composer admit images shown as supported',
|
|
1604
|
+
'page.applyInputShort':
|
|
1605
|
+
'Adds image input to multimodal relay models; off means the Provider input declaration only',
|
|
1465
1606
|
'page.applyInputHint':
|
|
1466
1607
|
'When on, relay chat models labelled Multimodal advertise image input to the request path; when off, only the Provider input declaration counts. Relay (pi-ai) routes only — no configuration file is modified. Turn it off if a particular upstream still rejects images.',
|
|
1467
1608
|
'page.kindLegend':
|
|
1468
|
-
'
|
|
1469
|
-
'page.
|
|
1470
|
-
'page.searchPlaceholder': 'Search model name or ID',
|
|
1471
|
-
'page.capabilityLabel': 'Capabilities',
|
|
1609
|
+
'Kind colours: image blue · video green · embedding orange · audio purple · rerank red · moderation rose · translation grey. “Official correction” means vendor documentation overrides a wrong catalog row.',
|
|
1610
|
+
'page.searchPlaceholder': 'Search name or ID',
|
|
1472
1611
|
'page.capability.multimodal': 'Multimodal',
|
|
1612
|
+
'page.capability.multimodalTitle': 'Only models that accept image input',
|
|
1473
1613
|
'page.capability.reasoning': 'Reasoning',
|
|
1614
|
+
'page.capability.reasoningTitle': 'Only models that publish reasoning support',
|
|
1474
1615
|
'page.contextLabel': 'Context at least',
|
|
1475
|
-
'page.context.any': 'Any',
|
|
1616
|
+
'page.context.any': 'Any context',
|
|
1476
1617
|
'page.context.128k': '128K+',
|
|
1477
1618
|
'page.context.256k': '256K+',
|
|
1478
1619
|
'page.context.512k': '512K+',
|
|
@@ -1484,15 +1625,15 @@ window.__ModuleLoader__.load({
|
|
|
1484
1625
|
'page.filter.other': 'Other',
|
|
1485
1626
|
'group.showAll': 'Show all',
|
|
1486
1627
|
'group.hideAll': 'Hide all',
|
|
1487
|
-
'group.showFiltered': 'Show
|
|
1488
|
-
'group.hideFiltered': 'Hide
|
|
1628
|
+
'group.showFiltered': 'Show results',
|
|
1629
|
+
'group.hideFiltered': 'Hide results',
|
|
1489
1630
|
'group.collapseNamed': 'Collapse the {name} group',
|
|
1490
1631
|
'group.expandNamed': 'Expand the {name} group',
|
|
1491
|
-
'group.
|
|
1492
|
-
'group.
|
|
1493
|
-
'group.
|
|
1494
|
-
'group.probeFailed': '
|
|
1495
|
-
'group.probeSkipped': '
|
|
1632
|
+
'group.probeOk': '{models} models · {caps} with caps',
|
|
1633
|
+
'group.probeNoCaps': '{models} models · no cap fields',
|
|
1634
|
+
'group.probePartial': 'pricing {models} models',
|
|
1635
|
+
'group.probeFailed': 'read failed',
|
|
1636
|
+
'group.probeSkipped': 'not probed',
|
|
1496
1637
|
'tag.reasoning': 'Reasoning',
|
|
1497
1638
|
'tag.noReasoning': 'No reasoning',
|
|
1498
1639
|
'tag.reasoningUnknown': 'Reasoning unknown',
|
|
@@ -1515,7 +1656,6 @@ window.__ModuleLoader__.load({
|
|
|
1515
1656
|
'tag.kindVideoTitle': 'Video generation model (outputs video); kind source: {source}',
|
|
1516
1657
|
'tag.correction': 'Official correction',
|
|
1517
1658
|
'tag.correctionTitle': '{note}\nSource: {source}',
|
|
1518
|
-
'tag.resolution': 'Resolution',
|
|
1519
1659
|
'tag.resolutionTitle': 'The model name advertises a {value} output tier',
|
|
1520
1660
|
'tag.imageInput': 'Image edit',
|
|
1521
1661
|
'tag.imageInputTitle': 'Accepts image input (image-to-image / reference images)',
|
|
@@ -1701,9 +1841,16 @@ window.__ModuleLoader__.load({
|
|
|
1701
1841
|
return sources && typeof sources[field] === 'string' ? sources[field] : 'unknown'
|
|
1702
1842
|
}
|
|
1703
1843
|
|
|
1704
|
-
/**
|
|
1844
|
+
/**
|
|
1845
|
+
* Sources whose values are completed or inferred rather than declared.
|
|
1846
|
+
*
|
|
1847
|
+
* `snapshot` is the learned store: facts a checker model or a public catalog
|
|
1848
|
+
* reported. Only the adapter, the settings document, the relay's own listing
|
|
1849
|
+
* and a vendor correction count as declarations, so a verified row must stay
|
|
1850
|
+
* dashed — otherwise an LLM guess would look as trustworthy as a declaration.
|
|
1851
|
+
*/
|
|
1705
1852
|
function isInferredSource(source) {
|
|
1706
|
-
return source === '
|
|
1853
|
+
return source === 'snapshot' || source === 'inferred'
|
|
1707
1854
|
}
|
|
1708
1855
|
|
|
1709
1856
|
/** Kind with a safe fallback for records from an older Host. */
|
|
@@ -2122,6 +2269,8 @@ window.__ModuleLoader__.load({
|
|
|
2122
2269
|
* @module client-src/pure
|
|
2123
2270
|
*/
|
|
2124
2271
|
|
|
2272
|
+
const { bucketOf, kindOf } = require("format")
|
|
2273
|
+
|
|
2125
2274
|
/** Number of one bucket's token sum, guarding partial buckets. */
|
|
2126
2275
|
function bucketTotalOf(buckets) {
|
|
2127
2276
|
if (!Array.isArray(buckets)) return 0
|
|
@@ -2196,11 +2345,9 @@ window.__ModuleLoader__.load({
|
|
|
2196
2345
|
}
|
|
2197
2346
|
|
|
2198
2347
|
function bucket(model) {
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
if (kind === 'chat') return 'chat'
|
|
2203
|
-
return 'other'
|
|
2348
|
+
// One implementation lives in `format.mjs` as `bucketOf`; duplicating the
|
|
2349
|
+
// four branches here meant the filter and the tag colours could drift apart.
|
|
2350
|
+
return bucketOf(kindOf(model))
|
|
2204
2351
|
}
|
|
2205
2352
|
|
|
2206
2353
|
/** Counts per kind bucket for the whole list. */
|
|
@@ -2221,7 +2368,11 @@ window.__ModuleLoader__.load({
|
|
|
2221
2368
|
return routes
|
|
2222
2369
|
}
|
|
2223
2370
|
|
|
2224
|
-
/**
|
|
2371
|
+
/**
|
|
2372
|
+
* Compact label + tooltip for one provider probe diagnostic.
|
|
2373
|
+
* @returns `{ label, title, failed? }` or null; `failed` marks a route the page
|
|
2374
|
+
* should warn about instead of quietly annotating.
|
|
2375
|
+
*/
|
|
2225
2376
|
function probeOf(state, group, t) {
|
|
2226
2377
|
const list = Array.isArray(state.providers) ? state.providers : []
|
|
2227
2378
|
const diagnostic = list.find((item) => item && item.id === group.id)
|
|
@@ -2229,6 +2380,8 @@ window.__ModuleLoader__.load({
|
|
|
2229
2380
|
const details = [
|
|
2230
2381
|
diagnostic.host ? 'host: ' + diagnostic.host : null,
|
|
2231
2382
|
diagnostic.status ? 'status: ' + diagnostic.status : null,
|
|
2383
|
+
diagnostic.models ? 'models: ' + diagnostic.models : null,
|
|
2384
|
+
diagnostic.capabilities ? 'capabilities: ' + diagnostic.capabilities : null,
|
|
2232
2385
|
diagnostic.message ? 'message: ' + diagnostic.message : null,
|
|
2233
2386
|
diagnostic.pricing && diagnostic.pricing !== 'skipped' ? 'pricing: ' + diagnostic.pricing : null,
|
|
2234
2387
|
]
|
|
@@ -2246,8 +2399,19 @@ window.__ModuleLoader__.load({
|
|
|
2246
2399
|
title: details,
|
|
2247
2400
|
}
|
|
2248
2401
|
}
|
|
2402
|
+
// The listing failed but the pricing endpoint still enumerated the route.
|
|
2403
|
+
if (diagnostic.status === 'partial') {
|
|
2404
|
+
return {
|
|
2405
|
+
label: t('group.probePartial', { models: diagnostic.models ?? 0 }),
|
|
2406
|
+
title: details,
|
|
2407
|
+
}
|
|
2408
|
+
}
|
|
2249
2409
|
if (diagnostic.status === 'failed' || diagnostic.status === 'unauthorized') {
|
|
2250
|
-
return {
|
|
2410
|
+
return {
|
|
2411
|
+
label: t('group.probeFailed'),
|
|
2412
|
+
title: details,
|
|
2413
|
+
failed: true,
|
|
2414
|
+
}
|
|
2251
2415
|
}
|
|
2252
2416
|
return { label: t('group.probeSkipped'), title: details }
|
|
2253
2417
|
}
|
|
@@ -2269,6 +2433,7 @@ window.__ModuleLoader__.load({
|
|
|
2269
2433
|
const React = require('react')
|
|
2270
2434
|
const h = React.createElement
|
|
2271
2435
|
|
|
2436
|
+
const { FIELD } = require("constants")
|
|
2272
2437
|
const { keyOf, kindOf, isGenerationKind, providerLabel, countModels, readCollapsedGroups, persistCollapsedGroups, overrideKeyOf } = require("format")
|
|
2273
2438
|
const { computeFilteredGroups, countKinds, probeOf } = require("pure")
|
|
2274
2439
|
const { capabilityTags, tagClassOf } = require("tags")
|
|
@@ -2277,6 +2442,33 @@ window.__ModuleLoader__.load({
|
|
|
2277
2442
|
const { UsageToolbar } = require("usage-view")
|
|
2278
2443
|
const { VerifyPanel } = require("checker-view")
|
|
2279
2444
|
|
|
2445
|
+
/** Browser-local key for the "about this page" disclosure. */
|
|
2446
|
+
const ABOUT_KEY = 'dsh-model-visibility:about-open:v1'
|
|
2447
|
+
|
|
2448
|
+
function readAboutOpen() {
|
|
2449
|
+
try {
|
|
2450
|
+
return window.localStorage.getItem(ABOUT_KEY) === '1'
|
|
2451
|
+
} catch (_error) {
|
|
2452
|
+
return false
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
|
|
2456
|
+
function persistAboutOpen(open) {
|
|
2457
|
+
try {
|
|
2458
|
+
window.localStorage.setItem(ABOUT_KEY, open ? '1' : '0')
|
|
2459
|
+
} catch (_error) {
|
|
2460
|
+
// Storage is optional for a preference like this.
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
|
|
2464
|
+
/**
|
|
2465
|
+
* The settings page.
|
|
2466
|
+
*
|
|
2467
|
+
* Layout, in the order a user needs it: a one-line summary with the two
|
|
2468
|
+
* primary actions, the token strip, the filter bar, the models, then the
|
|
2469
|
+
* diagnostics — and behind a disclosure the long explanation that used to
|
|
2470
|
+
* occupy a third of the viewport before the first model row.
|
|
2471
|
+
*/
|
|
2280
2472
|
function makeSection(form, catalog, overrides, usage, verify) {
|
|
2281
2473
|
return function ModelVisibilitySection({ t }) {
|
|
2282
2474
|
const { hidden, snapshot } = useHidden(form)
|
|
@@ -2287,6 +2479,7 @@ window.__ModuleLoader__.load({
|
|
|
2287
2479
|
const [refreshing, setRefreshing] = React.useState(false)
|
|
2288
2480
|
const [reloading, setReloading] = React.useState(false)
|
|
2289
2481
|
const [collapsed, setCollapsed] = React.useState(readCollapsedGroups)
|
|
2482
|
+
const [aboutOpen, setAboutOpen] = React.useState(readAboutOpen)
|
|
2290
2483
|
const [editing, setEditing] = React.useState(null)
|
|
2291
2484
|
const [providerNote, setProviderNote] = React.useState(null)
|
|
2292
2485
|
|
|
@@ -2336,16 +2529,18 @@ window.__ModuleLoader__.load({
|
|
|
2336
2529
|
if (disabled) return
|
|
2337
2530
|
setBusy(true)
|
|
2338
2531
|
setError(null)
|
|
2339
|
-
Promise.resolve(form.set(field, value))
|
|
2340
|
-
(
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2532
|
+
Promise.resolve(form.set(field, value))
|
|
2533
|
+
.then(
|
|
2534
|
+
(accepted) => {
|
|
2535
|
+
if (accepted === false) setError(t('page.saveFailed'))
|
|
2536
|
+
},
|
|
2537
|
+
() => {
|
|
2538
|
+
setError(t('page.saveFailed'))
|
|
2539
|
+
},
|
|
2540
|
+
)
|
|
2541
|
+
.finally(() => {
|
|
2542
|
+
setBusy(false)
|
|
2543
|
+
})
|
|
2349
2544
|
}
|
|
2350
2545
|
|
|
2351
2546
|
const commit = (next) => writeField(FIELD, Array.from(next))
|
|
@@ -2386,7 +2581,7 @@ window.__ModuleLoader__.load({
|
|
|
2386
2581
|
// A hard reload may supersede a soft refresh that is still running;
|
|
2387
2582
|
// catalog.load() gives it a new run and discards the older result.
|
|
2388
2583
|
const reloadCatalog = () => {
|
|
2389
|
-
if (busy || reloading) return
|
|
2584
|
+
if (busy || reloading || refreshing) return
|
|
2390
2585
|
runCatalogAction(() => catalog.reload(), setReloading)
|
|
2391
2586
|
}
|
|
2392
2587
|
|
|
@@ -2409,24 +2604,31 @@ window.__ModuleLoader__.load({
|
|
|
2409
2604
|
})
|
|
2410
2605
|
}
|
|
2411
2606
|
|
|
2607
|
+
const toggleAbout = () => {
|
|
2608
|
+
setAboutOpen((previous) => {
|
|
2609
|
+
persistAboutOpen(!previous)
|
|
2610
|
+
return !previous
|
|
2611
|
+
})
|
|
2612
|
+
}
|
|
2613
|
+
|
|
2614
|
+
/* ------------------------------------------------------------- groups */
|
|
2615
|
+
|
|
2412
2616
|
const rows = []
|
|
2413
2617
|
for (const group of filteredGroups) {
|
|
2414
2618
|
const allGroupModels =
|
|
2415
2619
|
(groups.find((candidate) => candidate.id === group.id) || group).models || []
|
|
2416
2620
|
const models = group.models || []
|
|
2417
|
-
const shown = allGroupModels.filter(
|
|
2418
|
-
(model) => !hidden.has(keyOf(group.id, model.id)),
|
|
2419
|
-
).length
|
|
2621
|
+
const shown = allGroupModels.filter((model) => !hidden.has(keyOf(group.id, model.id))).length
|
|
2420
2622
|
const allShown = models.every((model) => !hidden.has(keyOf(group.id, model.id)))
|
|
2421
2623
|
const kinds = countKinds(allGroupModels)
|
|
2422
2624
|
const kindParts = []
|
|
2423
|
-
if (kinds.chat > 0) kindParts.push(
|
|
2424
|
-
if (kinds.image > 0) kindParts.push(
|
|
2425
|
-
if (kinds.video > 0) kindParts.push(
|
|
2426
|
-
if (kinds.other > 0) kindParts.push(
|
|
2625
|
+
if (kinds.chat > 0) kindParts.push('chat')
|
|
2626
|
+
if (kinds.image > 0) kindParts.push('image')
|
|
2627
|
+
if (kinds.video > 0) kindParts.push('video')
|
|
2628
|
+
if (kinds.other > 0) kindParts.push('other')
|
|
2427
2629
|
const kindNodes = []
|
|
2428
|
-
for (const [index,
|
|
2429
|
-
if (index > 0) kindNodes.push('
|
|
2630
|
+
for (const [index, kindKey] of kindParts.entries()) {
|
|
2631
|
+
if (index > 0) kindNodes.push(h('span', { key: 'sep-' + kindKey, className: 'mvp_sep' }, '·'))
|
|
2430
2632
|
kindNodes.push(
|
|
2431
2633
|
h(
|
|
2432
2634
|
'span',
|
|
@@ -2435,8 +2637,9 @@ window.__ModuleLoader__.load({
|
|
|
2435
2637
|
className:
|
|
2436
2638
|
'mvp_kindCount' +
|
|
2437
2639
|
(kindKey === 'chat' ? '' : ' mvp_kindCount--' + kindKey),
|
|
2640
|
+
title: t('page.filter.' + kindKey) + ' ' + kinds[kindKey],
|
|
2438
2641
|
},
|
|
2439
|
-
|
|
2642
|
+
kinds[kindKey],
|
|
2440
2643
|
),
|
|
2441
2644
|
)
|
|
2442
2645
|
}
|
|
@@ -2454,34 +2657,37 @@ window.__ModuleLoader__.load({
|
|
|
2454
2657
|
'div',
|
|
2455
2658
|
{ className: 'mvp_groupHead' },
|
|
2456
2659
|
h(
|
|
2457
|
-
'
|
|
2458
|
-
{
|
|
2459
|
-
|
|
2460
|
-
'
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
onClick: () => toggleGroupCollapsed(group.id),
|
|
2468
|
-
},
|
|
2469
|
-
collapsedNow ? '▸' : '▾',
|
|
2470
|
-
),
|
|
2660
|
+
'button',
|
|
2661
|
+
{
|
|
2662
|
+
type: 'button',
|
|
2663
|
+
className: 'mvp_groupTitle',
|
|
2664
|
+
'aria-expanded': !collapsedNow,
|
|
2665
|
+
'aria-label': collapseLabel,
|
|
2666
|
+
title: collapseLabel,
|
|
2667
|
+
onClick: () => toggleGroupCollapsed(group.id),
|
|
2668
|
+
},
|
|
2669
|
+
h('span', { className: 'mvp_chevron' }, collapsedNow ? '▸' : '▾'),
|
|
2471
2670
|
h('span', { className: 'mvp_groupName' }, groupName),
|
|
2472
2671
|
h('span', { className: 'mvp_groupCount' }, shown + '/' + allGroupModels.length),
|
|
2473
2672
|
kindParts.length > 0
|
|
2474
2673
|
? h('span', { className: 'mvp_groupCap' }, kindNodes)
|
|
2475
2674
|
: null,
|
|
2476
2675
|
probe !== null
|
|
2477
|
-
? h(
|
|
2676
|
+
? h(
|
|
2677
|
+
'span',
|
|
2678
|
+
{
|
|
2679
|
+
className: 'mvp_groupProbe' + (probe.failed === true ? ' mvp_groupProbe--warn' : ''),
|
|
2680
|
+
title: probe.title || undefined,
|
|
2681
|
+
},
|
|
2682
|
+
probe.label,
|
|
2683
|
+
)
|
|
2478
2684
|
: null,
|
|
2479
2685
|
),
|
|
2480
2686
|
h(
|
|
2481
2687
|
'button',
|
|
2482
2688
|
{
|
|
2483
2689
|
type: 'button',
|
|
2484
|
-
className: 'mvp_button',
|
|
2690
|
+
className: 'mvp_button mvp_button--quiet',
|
|
2485
2691
|
disabled: disabled,
|
|
2486
2692
|
onClick: () => setGroup(group, models, !allShown),
|
|
2487
2693
|
},
|
|
@@ -2513,15 +2719,20 @@ window.__ModuleLoader__.load({
|
|
|
2513
2719
|
className:
|
|
2514
2720
|
'mvp_row' +
|
|
2515
2721
|
(generation ? ' mvp_row--gen' : '') +
|
|
2722
|
+
(shownModel ? '' : ' mvp_row--off') +
|
|
2516
2723
|
kindTint,
|
|
2517
2724
|
},
|
|
2518
2725
|
h(
|
|
2519
2726
|
'div',
|
|
2520
2727
|
{ className: 'mvp_rowText' },
|
|
2521
|
-
h(
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2728
|
+
h(
|
|
2729
|
+
'div',
|
|
2730
|
+
{ className: 'mvp_rowHead' },
|
|
2731
|
+
h('span', { className: 'mvp_rowTitle' }, model.name),
|
|
2732
|
+
model.id === model.name
|
|
2733
|
+
? null
|
|
2734
|
+
: h('span', { className: 'mvp_rowMeta' }, model.id),
|
|
2735
|
+
),
|
|
2525
2736
|
h(
|
|
2526
2737
|
'div',
|
|
2527
2738
|
{ className: 'mvp_tags' },
|
|
@@ -2546,7 +2757,7 @@ window.__ModuleLoader__.load({
|
|
|
2546
2757
|
'button',
|
|
2547
2758
|
{
|
|
2548
2759
|
type: 'button',
|
|
2549
|
-
className: 'mvp_button mvp_editButton',
|
|
2760
|
+
className: 'mvp_button mvp_button--quiet mvp_editButton',
|
|
2550
2761
|
'aria-pressed': editing === rowKey,
|
|
2551
2762
|
'aria-label': t('row.edit') + ': ' + model.name,
|
|
2552
2763
|
title: t('row.editTitle'),
|
|
@@ -2594,9 +2805,13 @@ window.__ModuleLoader__.load({
|
|
|
2594
2805
|
)
|
|
2595
2806
|
}
|
|
2596
2807
|
|
|
2808
|
+
/* ------------------------------------------------------------- status */
|
|
2809
|
+
|
|
2597
2810
|
const loading = state.status === 'loading' || state.status === 'idle'
|
|
2598
2811
|
const providerList = Array.isArray(state.providers) ? state.providers : []
|
|
2599
|
-
const probeOk = providerList.filter(
|
|
2812
|
+
const probeOk = providerList.filter(
|
|
2813
|
+
(provider) => provider && (provider.status === 'ok' || provider.status === 'partial'),
|
|
2814
|
+
).length
|
|
2600
2815
|
const probeCaps = providerList.reduce(
|
|
2601
2816
|
(count, provider) => count + (Number(provider && provider.capabilities) || 0),
|
|
2602
2817
|
0,
|
|
@@ -2607,10 +2822,7 @@ window.__ModuleLoader__.load({
|
|
|
2607
2822
|
? t('page.probeSummary', { ok: probeOk, caps: probeCaps })
|
|
2608
2823
|
: t('page.probeSummaryNone')
|
|
2609
2824
|
: null
|
|
2610
|
-
// A page refresh can deliver this bundle while the Host half is still
|
|
2611
|
-
// the previous version; the switch would look live but do nothing.
|
|
2612
2825
|
const hostStale = state.source === 'host' && !hostFeatures.includes('applyInput')
|
|
2613
|
-
// A hard reload is only meaningful when the Host half understands it.
|
|
2614
2826
|
const canReload = state.source === 'host' && hostFeatures.includes('reload')
|
|
2615
2827
|
const anyCollapsed = groups.some((group) => collapsed.has(group.id))
|
|
2616
2828
|
const learnedDate =
|
|
@@ -2635,54 +2847,52 @@ window.__ModuleLoader__.load({
|
|
|
2635
2847
|
]
|
|
2636
2848
|
const hasActiveFilters =
|
|
2637
2849
|
filter !== 'all' || normalizedQuery !== '' || multimodalOnly || reasoningOnly || minContext > 0
|
|
2850
|
+
// A relay that answered with a diagnostic is worth surfacing next to the
|
|
2851
|
+
// group, but only failed routes belong in the page-level warning list.
|
|
2852
|
+
const failedRoutes = providerList.filter(
|
|
2853
|
+
(provider) => provider && (provider.status === 'failed' || provider.status === 'unauthorized'),
|
|
2854
|
+
)
|
|
2855
|
+
const summaryText =
|
|
2856
|
+
total === 0
|
|
2857
|
+
? loading
|
|
2858
|
+
? t('page.loading')
|
|
2859
|
+
: t('page.empty')
|
|
2860
|
+
: t('page.summary', { visible: total - hiddenCount, total })
|
|
2861
|
+
|
|
2862
|
+
/* ------------------------------------------------------------- render */
|
|
2638
2863
|
|
|
2639
2864
|
return h(
|
|
2640
2865
|
'div',
|
|
2641
2866
|
{ className: 'mvp_page' },
|
|
2642
|
-
|
|
2867
|
+
// Title + summary + the two actions that are worth a permanent slot.
|
|
2643
2868
|
h(
|
|
2644
2869
|
'div',
|
|
2645
2870
|
{ className: 'mvp_head' },
|
|
2646
|
-
h('h2', { className: 'mvp_title' }, t('page.title')),
|
|
2647
|
-
h('p', { className: 'mvp_desc' }, t('page.description')),
|
|
2648
|
-
),
|
|
2649
|
-
h(
|
|
2650
|
-
'div',
|
|
2651
|
-
{ className: 'mvp_toolbar' },
|
|
2652
2871
|
h(
|
|
2653
2872
|
'div',
|
|
2654
|
-
{ className: '
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
:
|
|
2660
|
-
|
|
2661
|
-
|
|
2873
|
+
{ className: 'mvp_headMain' },
|
|
2874
|
+
h('h2', { className: 'mvp_title' }, t('page.title')),
|
|
2875
|
+
h('span', { className: 'mvp_summary' }, summaryText),
|
|
2876
|
+
hiddenCount > 0
|
|
2877
|
+
? h('span', { className: 'mvp_summary mvp_summary--dim' }, t('page.hiddenSummary', { count: hiddenCount }))
|
|
2878
|
+
: null,
|
|
2879
|
+
generationCount > 0
|
|
2880
|
+
? h('span', { className: 'mvp_summary mvp_summary--dim' }, t('page.kindSummary', { count: generationCount }))
|
|
2881
|
+
: null,
|
|
2662
2882
|
),
|
|
2663
2883
|
h(
|
|
2664
2884
|
'div',
|
|
2665
|
-
{ className: '
|
|
2885
|
+
{ className: 'mvp_headActions' },
|
|
2666
2886
|
h(
|
|
2667
2887
|
'button',
|
|
2668
2888
|
{
|
|
2669
2889
|
type: 'button',
|
|
2670
|
-
className: 'mvp_button',
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
t('page.showAll'),
|
|
2675
|
-
),
|
|
2676
|
-
h(
|
|
2677
|
-
'button',
|
|
2678
|
-
{
|
|
2679
|
-
type: 'button',
|
|
2680
|
-
className: 'mvp_button',
|
|
2681
|
-
disabled: groups.length === 0,
|
|
2682
|
-
title: t(anyCollapsed ? 'page.expandGroupsTitle' : 'page.collapseGroupsTitle'),
|
|
2683
|
-
onClick: toggleAllCollapsed,
|
|
2890
|
+
className: 'mvp_button mvp_button--quiet',
|
|
2891
|
+
'aria-pressed': aboutOpen,
|
|
2892
|
+
title: t('page.aboutTitle'),
|
|
2893
|
+
onClick: toggleAbout,
|
|
2684
2894
|
},
|
|
2685
|
-
t(
|
|
2895
|
+
t('page.about') + (aboutOpen ? ' ▴' : ' ▾'),
|
|
2686
2896
|
),
|
|
2687
2897
|
h(
|
|
2688
2898
|
'button',
|
|
@@ -2690,6 +2900,7 @@ window.__ModuleLoader__.load({
|
|
|
2690
2900
|
type: 'button',
|
|
2691
2901
|
className: 'mvp_button',
|
|
2692
2902
|
disabled: busy || refreshing || reloading,
|
|
2903
|
+
title: t('page.refreshTitle'),
|
|
2693
2904
|
onClick: refreshCatalog,
|
|
2694
2905
|
},
|
|
2695
2906
|
t(refreshing ? 'page.refreshing' : 'page.refresh'),
|
|
@@ -2707,63 +2918,77 @@ window.__ModuleLoader__.load({
|
|
|
2707
2918
|
),
|
|
2708
2919
|
),
|
|
2709
2920
|
),
|
|
2921
|
+
aboutOpen
|
|
2922
|
+
? h(
|
|
2923
|
+
'div',
|
|
2924
|
+
{ className: 'mvp_about' },
|
|
2925
|
+
h('p', { className: 'mvp_aboutP' }, t('page.description')),
|
|
2926
|
+
h('p', { className: 'mvp_aboutP' }, t('page.legend')),
|
|
2927
|
+
h('p', { className: 'mvp_aboutP' }, t('page.kindLegend')),
|
|
2928
|
+
h('p', { className: 'mvp_aboutP' }, t('page.manualNote')),
|
|
2929
|
+
)
|
|
2930
|
+
: null,
|
|
2931
|
+
h(UsageToolbar, { usage, t, features: state.source === 'host' ? hostFeatures : [] }),
|
|
2710
2932
|
h(
|
|
2711
2933
|
'div',
|
|
2712
2934
|
{ className: 'mvp_filters' },
|
|
2713
|
-
h('
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2935
|
+
h('input', {
|
|
2936
|
+
className: 'mvp_searchInput',
|
|
2937
|
+
type: 'search',
|
|
2938
|
+
value: query,
|
|
2939
|
+
placeholder: t('page.searchPlaceholder'),
|
|
2940
|
+
'aria-label': t('page.searchPlaceholder'),
|
|
2941
|
+
onInput: (event) => setQuery(event.target.value),
|
|
2942
|
+
}),
|
|
2943
|
+
h(
|
|
2944
|
+
'div',
|
|
2945
|
+
{ className: 'mvp_filterGroup' },
|
|
2946
|
+
filterOptions.map(([value, label, count]) =>
|
|
2947
|
+
h(
|
|
2948
|
+
'button',
|
|
2949
|
+
{
|
|
2950
|
+
key: value,
|
|
2951
|
+
type: 'button',
|
|
2952
|
+
className: 'mvp_filter',
|
|
2953
|
+
'aria-pressed': filter === value,
|
|
2954
|
+
onClick: () => setFilter(value),
|
|
2955
|
+
},
|
|
2956
|
+
label + ' ' + count,
|
|
2957
|
+
),
|
|
2958
|
+
),
|
|
2723
2959
|
),
|
|
2724
|
-
h(
|
|
2725
|
-
|
|
2960
|
+
h(
|
|
2961
|
+
'div',
|
|
2962
|
+
{ className: 'mvp_filterGroup' },
|
|
2726
2963
|
h(
|
|
2727
2964
|
'button',
|
|
2728
2965
|
{
|
|
2729
|
-
key: value,
|
|
2730
2966
|
type: 'button',
|
|
2731
2967
|
className: 'mvp_filter',
|
|
2732
|
-
'aria-pressed':
|
|
2733
|
-
|
|
2968
|
+
'aria-pressed': multimodalOnly,
|
|
2969
|
+
title: t('page.capability.multimodalTitle'),
|
|
2970
|
+
onClick: () => setMultimodalOnly(!multimodalOnly),
|
|
2734
2971
|
},
|
|
2735
|
-
|
|
2972
|
+
t('page.capability.multimodal'),
|
|
2973
|
+
),
|
|
2974
|
+
h(
|
|
2975
|
+
'button',
|
|
2976
|
+
{
|
|
2977
|
+
type: 'button',
|
|
2978
|
+
className: 'mvp_filter',
|
|
2979
|
+
'aria-pressed': reasoningOnly,
|
|
2980
|
+
title: t('page.capability.reasoningTitle'),
|
|
2981
|
+
onClick: () => setReasoningOnly(!reasoningOnly),
|
|
2982
|
+
},
|
|
2983
|
+
t('page.capability.reasoning'),
|
|
2736
2984
|
),
|
|
2737
|
-
),
|
|
2738
|
-
h('span', { className: 'mvp_filterLabel' }, t('page.capabilityLabel')),
|
|
2739
|
-
h(
|
|
2740
|
-
'button',
|
|
2741
|
-
{
|
|
2742
|
-
type: 'button',
|
|
2743
|
-
className: 'mvp_filter',
|
|
2744
|
-
'aria-pressed': multimodalOnly,
|
|
2745
|
-
onClick: () => setMultimodalOnly(!multimodalOnly),
|
|
2746
|
-
},
|
|
2747
|
-
t('page.capability.multimodal'),
|
|
2748
|
-
),
|
|
2749
|
-
h(
|
|
2750
|
-
'button',
|
|
2751
|
-
{
|
|
2752
|
-
type: 'button',
|
|
2753
|
-
className: 'mvp_filter',
|
|
2754
|
-
'aria-pressed': reasoningOnly,
|
|
2755
|
-
onClick: () => setReasoningOnly(!reasoningOnly),
|
|
2756
|
-
},
|
|
2757
|
-
t('page.capability.reasoning'),
|
|
2758
|
-
),
|
|
2759
|
-
h('label', { className: 'mvp_context' },
|
|
2760
|
-
h('span', { className: 'mvp_filterLabel' }, t('page.contextLabel')),
|
|
2761
2985
|
h(
|
|
2762
2986
|
'select',
|
|
2763
2987
|
{
|
|
2764
2988
|
className: 'mvp_contextSelect',
|
|
2765
2989
|
value: String(minContext),
|
|
2766
2990
|
'aria-label': t('page.contextLabel'),
|
|
2991
|
+
title: t('page.contextLabel'),
|
|
2767
2992
|
onChange: (event) => setMinContext(Number(event.target.value)),
|
|
2768
2993
|
},
|
|
2769
2994
|
[
|
|
@@ -2775,6 +3000,31 @@ window.__ModuleLoader__.load({
|
|
|
2775
3000
|
].map(([value, label]) => h('option', { key: value, value: String(value) }, t(label))),
|
|
2776
3001
|
),
|
|
2777
3002
|
),
|
|
3003
|
+
h(
|
|
3004
|
+
'div',
|
|
3005
|
+
{ className: 'mvp_filterGroup mvp_filterGroup--push' },
|
|
3006
|
+
h(
|
|
3007
|
+
'button',
|
|
3008
|
+
{
|
|
3009
|
+
type: 'button',
|
|
3010
|
+
className: 'mvp_button mvp_button--quiet',
|
|
3011
|
+
disabled: disabled || hiddenCount === 0,
|
|
3012
|
+
onClick: () => commit(new Set()),
|
|
3013
|
+
},
|
|
3014
|
+
t('page.showAll'),
|
|
3015
|
+
),
|
|
3016
|
+
h(
|
|
3017
|
+
'button',
|
|
3018
|
+
{
|
|
3019
|
+
type: 'button',
|
|
3020
|
+
className: 'mvp_button mvp_button--quiet',
|
|
3021
|
+
disabled: groups.length === 0,
|
|
3022
|
+
title: t(anyCollapsed ? 'page.expandGroupsTitle' : 'page.collapseGroupsTitle'),
|
|
3023
|
+
onClick: toggleAllCollapsed,
|
|
3024
|
+
},
|
|
3025
|
+
t(anyCollapsed ? 'page.expandGroups' : 'page.collapseGroups'),
|
|
3026
|
+
),
|
|
3027
|
+
),
|
|
2778
3028
|
),
|
|
2779
3029
|
h(
|
|
2780
3030
|
'div',
|
|
@@ -2787,6 +3037,7 @@ window.__ModuleLoader__.load({
|
|
|
2787
3037
|
className: 'mvp_switch',
|
|
2788
3038
|
'aria-checked': applyInput,
|
|
2789
3039
|
'aria-label': t('page.applyInput'),
|
|
3040
|
+
'aria-describedby': 'mvp-apply-hint',
|
|
2790
3041
|
disabled: disabled,
|
|
2791
3042
|
onClick: () => commitApply(!applyInput),
|
|
2792
3043
|
},
|
|
@@ -2795,29 +3046,33 @@ window.__ModuleLoader__.load({
|
|
|
2795
3046
|
h(
|
|
2796
3047
|
'div',
|
|
2797
3048
|
{ className: 'mvp_applyText' },
|
|
2798
|
-
h('
|
|
2799
|
-
h('
|
|
3049
|
+
h('span', { className: 'mvp_applyTitle' }, t('page.applyInput')),
|
|
3050
|
+
h('span', { className: 'mvp_applyHint', id: 'mvp-apply-hint' }, t('page.applyInputShort')),
|
|
2800
3051
|
),
|
|
2801
3052
|
),
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
3053
|
+
failedRoutes.length > 0
|
|
3054
|
+
? h(
|
|
3055
|
+
'div',
|
|
3056
|
+
{
|
|
3057
|
+
className: 'mvp_banner mvp_banner--warn',
|
|
3058
|
+
title: failedRoutes
|
|
3059
|
+
.map((route) => (route.name || route.id) + ': ' + (route.message || route.status))
|
|
3060
|
+
.join('\n'),
|
|
3061
|
+
},
|
|
3062
|
+
t('page.probeFailures', {
|
|
3063
|
+
names: failedRoutes.map((route) => route.name || route.id).join(' · '),
|
|
3064
|
+
}),
|
|
3065
|
+
)
|
|
2811
3066
|
: null,
|
|
3067
|
+
hostStale ? h('div', { className: 'mvp_banner' }, t('page.hostStale')) : null,
|
|
2812
3068
|
state.detailsError !== null && state.source === 'catalog'
|
|
2813
|
-
? h('div', { className: '
|
|
3069
|
+
? h('div', { className: 'mvp_banner' }, t('page.metaFallback'))
|
|
2814
3070
|
: null,
|
|
2815
|
-
hostStale ? h('div', { className: 'mvp_fallback' }, t('page.hostStale')) : null,
|
|
2816
3071
|
state.failures.length > 0
|
|
2817
3072
|
? h(
|
|
2818
3073
|
'div',
|
|
2819
3074
|
{
|
|
2820
|
-
className: '
|
|
3075
|
+
className: 'mvp_banner mvp_banner--warn',
|
|
2821
3076
|
title: state.failures
|
|
2822
3077
|
.map((failure) => (failure.name || failure.id) + ': ' + failure.message)
|
|
2823
3078
|
.join('\n'),
|
|
@@ -2829,20 +3084,34 @@ window.__ModuleLoader__.load({
|
|
|
2829
3084
|
}),
|
|
2830
3085
|
)
|
|
2831
3086
|
: null,
|
|
2832
|
-
|
|
3087
|
+
overrideState.available === false
|
|
3088
|
+
? h('div', { className: 'mvp_banner' }, t('override.unavailable'))
|
|
3089
|
+
: null,
|
|
3090
|
+
error !== null ? h('div', { className: 'mvp_banner mvp_banner--error', role: 'alert' }, error) : null,
|
|
2833
3091
|
state.error !== null
|
|
2834
|
-
? h(
|
|
3092
|
+
? h(
|
|
3093
|
+
'div',
|
|
3094
|
+
{ className: 'mvp_banner mvp_banner--error', role: 'alert' },
|
|
3095
|
+
t('page.error', { message: state.error }),
|
|
3096
|
+
)
|
|
2835
3097
|
: null,
|
|
2836
|
-
|
|
3098
|
+
h('div', { className: 'mvp_groups' }, rows),
|
|
2837
3099
|
total > 0 && hasActiveFilters && rows.length === 0
|
|
2838
3100
|
? h('div', { className: 'mvp_note' }, t('page.filterEmpty'))
|
|
2839
3101
|
: null,
|
|
2840
3102
|
rows.length === 0 && !loading && state.error === null && total === 0
|
|
2841
|
-
? h('div', { className: '
|
|
3103
|
+
? h('div', { className: 'mvp_note' }, t('page.empty'))
|
|
2842
3104
|
: null,
|
|
2843
|
-
|
|
3105
|
+
h(
|
|
3106
|
+
'div',
|
|
3107
|
+
{ className: 'mvp_footer' },
|
|
3108
|
+
probeNote !== null
|
|
3109
|
+
? h('span', { className: 'mvp_footerItem', title: t('page.probeSummaryTitle') }, probeNote)
|
|
3110
|
+
: null,
|
|
3111
|
+
catalogNote !== null ? h('span', { className: 'mvp_footerItem' }, catalogNote) : null,
|
|
3112
|
+
),
|
|
2844
3113
|
providerNote !== null ? h('div', { className: 'mvp_note' }, providerNote) : null,
|
|
2845
|
-
|
|
3114
|
+
h(VerifyPanel, { verify, catalog: state, t, canUse: canVerify }),
|
|
2846
3115
|
)
|
|
2847
3116
|
}
|
|
2848
3117
|
}
|
|
@@ -3057,13 +3326,11 @@ window.__ModuleLoader__.load({
|
|
|
3057
3326
|
// -------------------------------------------------------- usage-view
|
|
3058
3327
|
define("usage-view", function (require, module, exports) {
|
|
3059
3328
|
/**
|
|
3060
|
-
* Token-usage strip with the
|
|
3329
|
+
* Token-usage strip with the two CSV exports.
|
|
3061
3330
|
*
|
|
3062
|
-
*
|
|
3063
|
-
*
|
|
3064
|
-
*
|
|
3065
|
-
*
|
|
3066
|
-
* @module client-src/usage-view
|
|
3331
|
+
* One line, four numbers, two icon-sized buttons: the strip is context, not
|
|
3332
|
+
* the point of the page, so it must not push the model list down. Exact
|
|
3333
|
+
* numbers live in the tooltip; the visible figure is the total.
|
|
3067
3334
|
*/
|
|
3068
3335
|
|
|
3069
3336
|
const React = require('react')
|
|
@@ -3073,15 +3340,22 @@ window.__ModuleLoader__.load({
|
|
|
3073
3340
|
const { formatTokens } = require("pure")
|
|
3074
3341
|
const { useSource } = require("hooks")
|
|
3075
3342
|
|
|
3076
|
-
/**
|
|
3077
|
-
function
|
|
3078
|
-
if (!Array.isArray(range)
|
|
3343
|
+
/** Total of one bucket tuple (input + output + cache read + cache write). */
|
|
3344
|
+
function totalOf(range) {
|
|
3345
|
+
if (!Array.isArray(range)) return 0
|
|
3346
|
+
return range.reduce((sum, value) => sum + (Number.isFinite(value) ? value : 0), 0)
|
|
3347
|
+
}
|
|
3348
|
+
|
|
3349
|
+
/** Detail line for one window: input / output / cache split. */
|
|
3350
|
+
function detailOf(range, t) {
|
|
3351
|
+
if (!Array.isArray(range) || totalOf(range) === 0) return t('usage.none')
|
|
3079
3352
|
const input = (range[0] ?? 0) + (range[2] ?? 0) + (range[3] ?? 0)
|
|
3080
3353
|
const output = range[1] ?? 0
|
|
3081
|
-
return t('usage.
|
|
3354
|
+
return t('usage.detail', {
|
|
3082
3355
|
input: formatTokens(input),
|
|
3083
3356
|
output: formatTokens(output),
|
|
3084
|
-
|
|
3357
|
+
cacheRead: formatTokens(range[2] ?? 0),
|
|
3358
|
+
cacheWrite: formatTokens(range[3] ?? 0),
|
|
3085
3359
|
})
|
|
3086
3360
|
}
|
|
3087
3361
|
|
|
@@ -3090,6 +3364,7 @@ window.__ModuleLoader__.load({
|
|
|
3090
3364
|
const anchor = document.createElement('a')
|
|
3091
3365
|
anchor.href = href
|
|
3092
3366
|
anchor.rel = 'noopener'
|
|
3367
|
+
anchor.download = ''
|
|
3093
3368
|
document.body.appendChild(anchor)
|
|
3094
3369
|
anchor.click()
|
|
3095
3370
|
anchor.remove()
|
|
@@ -3100,71 +3375,69 @@ window.__ModuleLoader__.load({
|
|
|
3100
3375
|
const supported = features.length === 0 || features.includes('usage')
|
|
3101
3376
|
const ranges = state.snapshot?.ranges
|
|
3102
3377
|
const computing = state.snapshot?.computing === true
|
|
3378
|
+
|
|
3379
|
+
if (!supported) {
|
|
3380
|
+
return h('div', { className: 'mvp_usage mvp_usage--off' }, t('usage.hostStale'))
|
|
3381
|
+
}
|
|
3382
|
+
|
|
3383
|
+
const windows = [
|
|
3384
|
+
['today', 'usage.today', 'usage.todayTitle'],
|
|
3385
|
+
['week', 'usage.week', 'usage.weekTitle'],
|
|
3386
|
+
['month', 'usage.month', 'usage.monthTitle'],
|
|
3387
|
+
['total', 'usage.total', 'usage.totalTitle'],
|
|
3388
|
+
]
|
|
3389
|
+
|
|
3103
3390
|
return h(
|
|
3104
3391
|
'div',
|
|
3105
|
-
{ className: 'mvp_usage'
|
|
3392
|
+
{ className: 'mvp_usage' },
|
|
3106
3393
|
h(
|
|
3107
|
-
'
|
|
3108
|
-
{ className: '
|
|
3109
|
-
|
|
3394
|
+
'span',
|
|
3395
|
+
{ className: 'mvp_usageLabel' },
|
|
3396
|
+
t('usage.title'),
|
|
3110
3397
|
computing ? h('span', { className: 'mvp_usageComputing' }, t('usage.computing')) : null,
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
{
|
|
3117
|
-
type: 'button',
|
|
3118
|
-
className: 'mvp_button',
|
|
3119
|
-
title: t('usage.exportUsageTitle'),
|
|
3120
|
-
onClick: () => download(`${API}/export?type=usage`),
|
|
3121
|
-
},
|
|
3122
|
-
t('usage.exportUsage'),
|
|
3123
|
-
),
|
|
3398
|
+
),
|
|
3399
|
+
h(
|
|
3400
|
+
'span',
|
|
3401
|
+
{ className: 'mvp_usageRanges' },
|
|
3402
|
+
windows.map(([key, labelKey, titleKey]) =>
|
|
3124
3403
|
h(
|
|
3125
|
-
'
|
|
3404
|
+
'span',
|
|
3126
3405
|
{
|
|
3127
|
-
|
|
3128
|
-
className: '
|
|
3129
|
-
title: t('
|
|
3130
|
-
onClick: () => download(`${API}/export?type=models`),
|
|
3406
|
+
key,
|
|
3407
|
+
className: 'mvp_stat' + (key === 'total' ? ' mvp_stat--total' : ''),
|
|
3408
|
+
title: t(titleKey) + '\n' + detailOf(ranges?.[key], t),
|
|
3131
3409
|
},
|
|
3132
|
-
|
|
3410
|
+
h('span', { className: 'mvp_statLabel' }, t(labelKey)),
|
|
3411
|
+
h('span', { className: 'mvp_statValue' }, formatTokens(totalOf(ranges?.[key]))),
|
|
3133
3412
|
),
|
|
3134
3413
|
),
|
|
3135
3414
|
),
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
),
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
{ className: 'mvp_stat mvp_stat--total', title: t('usage.totalTitle') },
|
|
3161
|
-
h('span', { className: 'mvp_statLabel' }, t('usage.total')),
|
|
3162
|
-
h('span', { className: 'mvp_statValue' }, rangeText(ranges?.total, t)),
|
|
3163
|
-
),
|
|
3164
|
-
)
|
|
3165
|
-
: h('div', { className: 'mvp_usageHint' }, t('usage.hostStale')),
|
|
3415
|
+
h(
|
|
3416
|
+
'span',
|
|
3417
|
+
{ className: 'mvp_usageActions' },
|
|
3418
|
+
h(
|
|
3419
|
+
'button',
|
|
3420
|
+
{
|
|
3421
|
+
type: 'button',
|
|
3422
|
+
className: 'mvp_button mvp_button--quiet',
|
|
3423
|
+
title: t('usage.exportUsageTitle'),
|
|
3424
|
+
onClick: () => download(`${API}/export?type=usage`),
|
|
3425
|
+
},
|
|
3426
|
+
t('usage.exportUsage'),
|
|
3427
|
+
),
|
|
3428
|
+
h(
|
|
3429
|
+
'button',
|
|
3430
|
+
{
|
|
3431
|
+
type: 'button',
|
|
3432
|
+
className: 'mvp_button mvp_button--quiet',
|
|
3433
|
+
title: t('usage.exportModelsTitle'),
|
|
3434
|
+
onClick: () => download(`${API}/export?type=models`),
|
|
3435
|
+
},
|
|
3436
|
+
t('usage.exportModels'),
|
|
3437
|
+
),
|
|
3438
|
+
),
|
|
3166
3439
|
state.error !== null
|
|
3167
|
-
? h('
|
|
3440
|
+
? h('span', { className: 'mvp_usageHint mvp_usageHint--warn' }, state.error)
|
|
3168
3441
|
: null,
|
|
3169
3442
|
)
|
|
3170
3443
|
}
|