@tzyc/dsh-model-visibility 1.10.3 → 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 +90 -16
- 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 +864 -594
- 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
|
@@ -312,6 +312,7 @@ window.__ModuleLoader__.load({
|
|
|
312
312
|
let state = { status: 'idle', snapshot: null, error: null }
|
|
313
313
|
let inflight = null
|
|
314
314
|
let timer = null
|
|
315
|
+
let timerMs = null
|
|
315
316
|
|
|
316
317
|
const publish = (next) => {
|
|
317
318
|
state = next
|
|
@@ -343,16 +344,33 @@ window.__ModuleLoader__.load({
|
|
|
343
344
|
return run
|
|
344
345
|
}
|
|
345
346
|
|
|
346
|
-
/**
|
|
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
|
|
347
353
|
const POLL_MS = 30_000
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
|
|
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()
|
|
351
368
|
}
|
|
352
369
|
const stopTimer = () => {
|
|
353
370
|
if (timer !== null) {
|
|
354
371
|
clearInterval(timer)
|
|
355
372
|
timer = null
|
|
373
|
+
timerMs = null
|
|
356
374
|
}
|
|
357
375
|
}
|
|
358
376
|
|
|
@@ -360,7 +378,11 @@ window.__ModuleLoader__.load({
|
|
|
360
378
|
subscribe(fn) {
|
|
361
379
|
const first = listeners.size === 0
|
|
362
380
|
listeners.add(fn)
|
|
363
|
-
|
|
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
|
+
}
|
|
364
386
|
return () => {
|
|
365
387
|
listeners.delete(fn)
|
|
366
388
|
if (listeners.size === 0) stopTimer()
|
|
@@ -370,12 +392,21 @@ window.__ModuleLoader__.load({
|
|
|
370
392
|
return state
|
|
371
393
|
},
|
|
372
394
|
load() {
|
|
373
|
-
return read()
|
|
395
|
+
return read().then(
|
|
396
|
+
(value) => {
|
|
397
|
+
rearm()
|
|
398
|
+
return value
|
|
399
|
+
},
|
|
400
|
+
(error) => {
|
|
401
|
+
rearm()
|
|
402
|
+
throw error
|
|
403
|
+
},
|
|
404
|
+
)
|
|
374
405
|
},
|
|
375
406
|
refresh() {
|
|
376
407
|
// A POST rescan is never needed: the Host rescan throttle refreshes on
|
|
377
408
|
// reads anyway. A plain read is enough here.
|
|
378
|
-
return
|
|
409
|
+
return this.load()
|
|
379
410
|
},
|
|
380
411
|
}
|
|
381
412
|
}
|
|
@@ -390,6 +421,8 @@ window.__ModuleLoader__.load({
|
|
|
390
421
|
let state = { status: 'idle', snapshot: null, error: null }
|
|
391
422
|
let inflight = null
|
|
392
423
|
let timer = null
|
|
424
|
+
/** Cadence the armed timer currently runs at, so rearm can compare. */
|
|
425
|
+
let timerMs = null
|
|
393
426
|
|
|
394
427
|
const publish = (next) => {
|
|
395
428
|
state = next
|
|
@@ -451,16 +484,30 @@ window.__ModuleLoader__.load({
|
|
|
451
484
|
|
|
452
485
|
const SWEEP_POLL_MS = 2_000
|
|
453
486
|
const IDLE_POLL_MS = 60_000
|
|
454
|
-
|
|
455
|
-
|
|
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
|
|
456
500
|
timer = setInterval(() => {
|
|
457
|
-
void read()
|
|
458
|
-
},
|
|
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()
|
|
459
505
|
}
|
|
460
506
|
const stopTimer = () => {
|
|
461
507
|
if (timer !== null) {
|
|
462
508
|
clearInterval(timer)
|
|
463
509
|
timer = null
|
|
510
|
+
timerMs = null
|
|
464
511
|
}
|
|
465
512
|
}
|
|
466
513
|
|
|
@@ -469,8 +516,8 @@ window.__ModuleLoader__.load({
|
|
|
469
516
|
const first = listeners.size === 0
|
|
470
517
|
listeners.add(fn)
|
|
471
518
|
if (first) {
|
|
472
|
-
void read()
|
|
473
|
-
|
|
519
|
+
void read().then(rearm, rearm)
|
|
520
|
+
rearm()
|
|
474
521
|
}
|
|
475
522
|
return () => {
|
|
476
523
|
listeners.delete(fn)
|
|
@@ -481,13 +528,40 @@ window.__ModuleLoader__.load({
|
|
|
481
528
|
return state
|
|
482
529
|
},
|
|
483
530
|
load() {
|
|
484
|
-
return read()
|
|
531
|
+
return read().then(
|
|
532
|
+
(value) => {
|
|
533
|
+
rearm()
|
|
534
|
+
return value
|
|
535
|
+
},
|
|
536
|
+
(error) => {
|
|
537
|
+
rearm()
|
|
538
|
+
throw error
|
|
539
|
+
},
|
|
540
|
+
)
|
|
485
541
|
},
|
|
486
542
|
start(payload) {
|
|
487
|
-
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
|
+
)
|
|
488
553
|
},
|
|
489
554
|
cancel() {
|
|
490
|
-
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
|
+
)
|
|
491
565
|
},
|
|
492
566
|
}
|
|
493
567
|
}
|
|
@@ -504,12 +578,13 @@ window.__ModuleLoader__.load({
|
|
|
504
578
|
/**
|
|
505
579
|
* The online capability verification panel.
|
|
506
580
|
*
|
|
507
|
-
*
|
|
508
|
-
*
|
|
509
|
-
*
|
|
510
|
-
*
|
|
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.
|
|
511
585
|
*
|
|
512
|
-
*
|
|
586
|
+
* The last route choice is kept in browser storage only: the Host persists run
|
|
587
|
+
* facts, not UI selections.
|
|
513
588
|
*/
|
|
514
589
|
|
|
515
590
|
const React = require('react')
|
|
@@ -519,6 +594,10 @@ window.__ModuleLoader__.load({
|
|
|
519
594
|
const { useSource } = require("hooks")
|
|
520
595
|
|
|
521
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]
|
|
522
601
|
|
|
523
602
|
function readPick() {
|
|
524
603
|
try {
|
|
@@ -528,10 +607,14 @@ window.__ModuleLoader__.load({
|
|
|
528
607
|
provider: typeof parsed.provider === 'string' ? parsed.provider : '',
|
|
529
608
|
model: typeof parsed.model === 'string' ? parsed.model : '',
|
|
530
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,
|
|
531
614
|
}
|
|
532
|
-
: { provider: '', model: '', writeBack: true }
|
|
615
|
+
: { provider: '', model: '', writeBack: true, concurrency: 2 }
|
|
533
616
|
} catch (_error) {
|
|
534
|
-
return { provider: '', model: '', writeBack: true }
|
|
617
|
+
return { provider: '', model: '', writeBack: true, concurrency: 2 }
|
|
535
618
|
}
|
|
536
619
|
}
|
|
537
620
|
|
|
@@ -562,10 +645,12 @@ window.__ModuleLoader__.load({
|
|
|
562
645
|
.find((group) => group.id === pick.provider)?.models ?? []
|
|
563
646
|
|
|
564
647
|
if (!canUse) {
|
|
565
|
-
return h('div', { className: '
|
|
648
|
+
return h('div', { className: 'mvp_banner' }, t('checker.hostStale'))
|
|
566
649
|
}
|
|
567
650
|
|
|
568
|
-
const tokensSpent = progress
|
|
651
|
+
const tokensSpent = progress
|
|
652
|
+
? formatTokens((progress.tokenUsage?.[0] ?? 0) + (progress.tokenUsage?.[1] ?? 0))
|
|
653
|
+
: null
|
|
569
654
|
|
|
570
655
|
const commit = (next) => {
|
|
571
656
|
setPick(next)
|
|
@@ -575,11 +660,11 @@ window.__ModuleLoader__.load({
|
|
|
575
660
|
const run = () => {
|
|
576
661
|
setMessage(null)
|
|
577
662
|
verify
|
|
578
|
-
.start({
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
663
|
+
.start({
|
|
664
|
+
provider: pick.provider,
|
|
665
|
+
model: pick.model,
|
|
666
|
+
writeBack: pick.writeBack,
|
|
667
|
+
concurrency: pick.concurrency,
|
|
583
668
|
})
|
|
584
669
|
.catch((error) => setMessage(String(error?.message ?? error)))
|
|
585
670
|
}
|
|
@@ -588,6 +673,24 @@ window.__ModuleLoader__.load({
|
|
|
588
673
|
verify.cancel().catch((error) => setMessage(String(error?.message ?? error)))
|
|
589
674
|
}
|
|
590
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
|
+
|
|
591
694
|
return h(
|
|
592
695
|
'div',
|
|
593
696
|
{ className: 'mvp_checker' + (open ? '' : ' mvp_checker--closed') },
|
|
@@ -599,26 +702,16 @@ window.__ModuleLoader__.load({
|
|
|
599
702
|
'aria-expanded': open,
|
|
600
703
|
onClick: () => setOpen(!open),
|
|
601
704
|
},
|
|
705
|
+
h('span', { className: 'mvp_chevron' }, open ? '▾' : '▸'),
|
|
602
706
|
h('span', { className: 'mvp_checkerTitle' }, t('checker.title')),
|
|
603
|
-
h(
|
|
604
|
-
|
|
605
|
-
{ className: 'mvp_checkerSummary' },
|
|
606
|
-
running
|
|
607
|
-
? t('checker.running', {
|
|
608
|
-
done: progress?.done ?? 0,
|
|
609
|
-
total: progress?.total ?? 0,
|
|
610
|
-
phase: t(`checker.phase.${progress?.phase ?? 'checker'}`),
|
|
611
|
-
})
|
|
612
|
-
: finished
|
|
613
|
-
? t('checker.finished', { done: progress?.done ?? 0, total: progress?.total ?? 0, tokens: tokensSpent })
|
|
614
|
-
: t('checker.idle'),
|
|
615
|
-
),
|
|
707
|
+
h('span', { className: 'mvp_checkerSummary' }, summary),
|
|
708
|
+
errors.length > 0 ? h('span', { className: 'mvp_badge' }, t('checker.errorBadge', { count: errors.length })) : null,
|
|
616
709
|
),
|
|
617
710
|
open
|
|
618
711
|
? h(
|
|
619
712
|
'div',
|
|
620
713
|
{ className: 'mvp_checkerBody' },
|
|
621
|
-
h('
|
|
714
|
+
h('p', { className: 'mvp_checkerHint' }, t('checker.description')),
|
|
622
715
|
h(
|
|
623
716
|
'div',
|
|
624
717
|
{ className: 'mvp_checkerForm' },
|
|
@@ -657,67 +750,141 @@ window.__ModuleLoader__.load({
|
|
|
657
750
|
),
|
|
658
751
|
),
|
|
659
752
|
),
|
|
660
|
-
),
|
|
661
|
-
h(
|
|
662
|
-
'div',
|
|
663
|
-
{ className: 'mvp_checkerActions' },
|
|
664
753
|
h(
|
|
665
|
-
'
|
|
666
|
-
{
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
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
|
+
),
|
|
676
771
|
),
|
|
677
772
|
h(
|
|
678
|
-
'
|
|
679
|
-
{
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
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')),
|
|
686
782
|
),
|
|
687
783
|
h(
|
|
688
784
|
'button',
|
|
689
785
|
{
|
|
690
786
|
type: 'button',
|
|
691
787
|
className: running ? 'mvp_button' : 'mvp_button mvp_button--accent',
|
|
692
|
-
disabled: running
|
|
788
|
+
disabled: !running && (pick.provider === '' || pick.model === ''),
|
|
693
789
|
title: running ? t('checker.cancelTitle') : t('checker.startTitle'),
|
|
694
790
|
onClick: running ? cancel : run,
|
|
695
791
|
},
|
|
696
792
|
running ? t('checker.cancel') : t('checker.start'),
|
|
697
793
|
),
|
|
698
794
|
),
|
|
699
|
-
|
|
795
|
+
running || finished
|
|
700
796
|
? h(
|
|
701
797
|
'div',
|
|
702
798
|
{ className: 'mvp_checkerProgress' },
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
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
|
+
),
|
|
708
828
|
)
|
|
709
829
|
: null,
|
|
710
|
-
|
|
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
|
|
711
834
|
? h(
|
|
712
835
|
'div',
|
|
713
|
-
{
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
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
|
+
),
|
|
718
856
|
)
|
|
719
857
|
: null,
|
|
720
|
-
|
|
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,
|
|
721
888
|
)
|
|
722
889
|
: null,
|
|
723
890
|
)
|
|
@@ -922,47 +1089,116 @@ window.__ModuleLoader__.load({
|
|
|
922
1089
|
// -------------------------------------------------------- css
|
|
923
1090
|
define("css", function (require, module, exports) {
|
|
924
1091
|
const CSS = `
|
|
1092
|
+
/* ---------------------------------------------------------------- shell */
|
|
925
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}
|
|
926
|
-
|
|
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}
|
|
927
1098
|
.mvp_title{color:var(--dsw-alias-label-primary);margin:0;font-size:16px;font-weight:500;line-height:24px}
|
|
928
|
-
.
|
|
929
|
-
.
|
|
930
|
-
.
|
|
931
|
-
.
|
|
932
|
-
.
|
|
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)}
|
|
933
1110
|
.mvp_button--accent{color:var(--dsw-alias-brand-primary);border-color:color-mix(in srgb, var(--dsw-alias-brand-primary) 55%, transparent)}
|
|
934
1111
|
.mvp_button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(127,127,127,.12))}
|
|
935
1112
|
.mvp_button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}
|
|
936
|
-
.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}
|
|
937
1155
|
.mvp_group{flex:none;flex-direction:column;display:flex}
|
|
938
|
-
.mvp_groupHead{justify-content:space-between;align-items:center;gap:12px;padding:
|
|
939
|
-
.mvp_groupTitle{color:var(--dsw-alias-label-primary);align-items:baseline;row-gap:2px;
|
|
940
|
-
.mvp_groupName{
|
|
941
|
-
.
|
|
942
|
-
.
|
|
943
|
-
.
|
|
944
|
-
.mvp_groupCount{color:var(--dsw-alias-label-tertiary);font-size:
|
|
945
|
-
.
|
|
946
|
-
.
|
|
947
|
-
.
|
|
948
|
-
.
|
|
949
|
-
.
|
|
950
|
-
.
|
|
951
|
-
.
|
|
952
|
-
.
|
|
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)}
|
|
953
1182
|
.mvp_row--kind-translation{box-shadow:inset 2px 0 0 var(--dsw-alias-border-l2)}
|
|
954
|
-
.mvp_rowText{flex-direction:column;gap:
|
|
955
|
-
.
|
|
956
|
-
.
|
|
957
|
-
.
|
|
958
|
-
.
|
|
959
|
-
.
|
|
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)}
|
|
960
1193
|
.mvp_switch:disabled{opacity:.45;cursor:default}
|
|
961
1194
|
.mvp_switch:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}
|
|
962
|
-
.mvp_thumb{position:absolute;top:3px;left:3px;width:
|
|
963
|
-
.mvp_switch[aria-checked="true"] .mvp_thumb{
|
|
964
|
-
.
|
|
965
|
-
|
|
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}
|
|
966
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)}
|
|
967
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)}
|
|
968
1204
|
.mvp_tag--kind{font-weight:500}
|
|
@@ -976,198 +1212,61 @@ window.__ModuleLoader__.load({
|
|
|
976
1212
|
.mvp_tag--kind-unknown{color:var(--dsw-alias-label-secondary);border-color:var(--dsw-alias-border-l2)}
|
|
977
1213
|
.mvp_tag--inferred{border-style:dashed}
|
|
978
1214
|
.mvp_tag--muted{color:var(--dsw-alias-label-tertiary);border-color:var(--dsw-alias-border-l1)}
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
.
|
|
982
|
-
.
|
|
983
|
-
.
|
|
984
|
-
.mvp_applyText{flex-direction:column;gap:2px;min-width:0;display:flex}
|
|
985
|
-
.mvp_applyTitle{color:var(--dsw-alias-label-primary);font-size:12px;font-weight:500;line-height:18px}
|
|
986
|
-
.mvp_applyHint{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}
|
|
987
|
-
.mvp_applyRow .mvp_switch{margin-top:1px}
|
|
988
|
-
.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}
|
|
989
|
-
.mvp_filters{align-items:center;gap:6px;padding:8px 0 2px;display:flex;flex-wrap:wrap}
|
|
990
|
-
.mvp_filterLabel{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}
|
|
991
|
-
.mvp_search{align-items:center;gap:6px;display:flex;min-width:220px;flex:1 1 220px}
|
|
992
|
-
.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}
|
|
993
|
-
.mvp_searchInput:focus-visible,.mvp_contextSelect:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}
|
|
994
|
-
.mvp_context{align-items:center;gap:6px;display:flex}
|
|
995
|
-
.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}
|
|
996
|
-
.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}
|
|
997
|
-
.mvp_filter:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(127,127,127,.12))}
|
|
998
|
-
.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)}
|
|
999
|
-
.mvp_filter:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}
|
|
1000
|
-
.mvp_note{color:var(--dsw-alias-label-tertiary);flex:none;padding:6px 0 0;font-size:10px;line-height:15px}
|
|
1001
|
-
.mvp_legend{color:var(--dsw-alias-label-tertiary);flex:none;padding:8px 0 2px;font-size:11px;line-height:16px}
|
|
1002
|
-
.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}
|
|
1003
|
-
.mvp_rowActions{flex:none;align-items:center;gap:8px;display:flex}
|
|
1004
|
-
.mvp_editButton{height:24px;padding:0 8px;font-size:11px;line-height:18px}
|
|
1005
|
-
.mvp_editButton[aria-pressed="true"]{color:var(--dsw-alias-brand-primary);border-color:color-mix(in srgb, var(--dsw-alias-brand-primary) 55%, transparent)}
|
|
1006
|
-
.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}
|
|
1007
|
-
.mvp_editorHead{flex-direction:column;gap:2px;display:flex}
|
|
1008
|
-
.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}
|
|
1009
1220
|
.mvp_editorSub{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}
|
|
1010
|
-
.mvp_editorGrid{flex-wrap:wrap;align-items:flex-
|
|
1011
|
-
.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}
|
|
1012
1223
|
.mvp_field--wide{flex:1 1 100%}
|
|
1013
|
-
.mvp_fieldLabel{color:var(--dsw-alias-label-
|
|
1014
|
-
.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
|
|
1015
|
-
.
|
|
1016
|
-
.
|
|
1017
|
-
.
|
|
1018
|
-
.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}
|
|
1019
|
-
.mvp_chip:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(127,127,127,.12))}
|
|
1020
|
-
.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)}
|
|
1021
1229
|
.mvp_chip:disabled{opacity:.45;cursor:default}
|
|
1022
|
-
.
|
|
1023
|
-
.mvp_editorNote{color:var(--dsw-alias-label-tertiary);font-size:
|
|
1024
|
-
.
|
|
1025
|
-
.
|
|
1026
|
-
.mvp_editorActions{
|
|
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
|
-
|
|
1062
|
-
}
|
|
1063
|
-
|
|
1064
|
-
.mvp_usageRanges {
|
|
1065
|
-
display: flex;
|
|
1066
|
-
gap: 18px;
|
|
1067
|
-
flex-wrap: wrap;
|
|
1068
|
-
margin-top: 8px;
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
.mvp_stat {
|
|
1072
|
-
display: flex;
|
|
1073
|
-
flex-direction: column;
|
|
1074
|
-
gap: 2px;
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
.mvp_statLabel {
|
|
1078
|
-
font-size: 11px;
|
|
1079
|
-
color: var(--dsw-alias-text-tertiary, currentColor);
|
|
1080
|
-
}
|
|
1081
|
-
|
|
1082
|
-
.mvp_statValue {
|
|
1083
|
-
font-size: 12px;
|
|
1084
|
-
font-variant-numeric: tabular-nums;
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
.mvp_stat--total .mvp_statValue {
|
|
1088
|
-
font-weight: 600;
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
|
-
.mvp_usageHint {
|
|
1092
|
-
margin-top: 8px;
|
|
1093
|
-
font-size: 11px;
|
|
1094
|
-
color: var(--dsw-alias-text-tertiary, currentColor);
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
.mvp_usageHint--warn {
|
|
1098
|
-
color: var(--dsw-alias-danger, #d0605e);
|
|
1099
|
-
}
|
|
1100
|
-
|
|
1101
|
-
.mvp_checker {
|
|
1102
|
-
margin: 0 0 12px;
|
|
1103
|
-
padding: 10px 12px;
|
|
1104
|
-
border: 1px solid var(--dsw-alias-border, transparent);
|
|
1105
|
-
border-radius: 10px;
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
.mvp_checkerHead {
|
|
1109
|
-
display: flex;
|
|
1110
|
-
width: 100%;
|
|
1111
|
-
align-items: center;
|
|
1112
|
-
gap: 10px;
|
|
1113
|
-
padding: 0;
|
|
1114
|
-
border: 0;
|
|
1115
|
-
background: transparent;
|
|
1116
|
-
color: inherit;
|
|
1117
|
-
text-align: left;
|
|
1118
|
-
cursor: pointer;
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
.mvp_checkerTitle {
|
|
1122
|
-
font-weight: 600;
|
|
1123
|
-
font-size: 12px;
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
|
-
.mvp_checkerSummary {
|
|
1127
|
-
margin-left: auto;
|
|
1128
|
-
font-size: 11px;
|
|
1129
|
-
color: var(--dsw-alias-text-tertiary, currentColor);
|
|
1130
|
-
}
|
|
1131
|
-
|
|
1132
|
-
.mvp_checker--closed.mvp_checker {
|
|
1133
|
-
padding: 8px 12px;
|
|
1134
|
-
}
|
|
1135
|
-
|
|
1136
|
-
.mvp_checkerBody {
|
|
1137
|
-
margin-top: 10px;
|
|
1138
|
-
display: flex;
|
|
1139
|
-
flex-direction: column;
|
|
1140
|
-
gap: 10px;
|
|
1141
|
-
}
|
|
1142
|
-
|
|
1143
|
-
.mvp_checkerHint {
|
|
1144
|
-
font-size: 11px;
|
|
1145
|
-
color: var(--dsw-alias-text-tertiary, currentColor);
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
|
-
.mvp_checkerForm {
|
|
1149
|
-
display: grid;
|
|
1150
|
-
grid-template-columns: 1fr 1fr;
|
|
1151
|
-
gap: 10px;
|
|
1152
|
-
max-width: 560px;
|
|
1153
|
-
}
|
|
1154
|
-
|
|
1155
|
-
.mvp_checkerActions {
|
|
1156
|
-
display: flex;
|
|
1157
|
-
align-items: center;
|
|
1158
|
-
gap: 10px;
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
|
-
.mvp_checkerSwitchText {
|
|
1162
|
-
font-size: 12px;
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
.mvp_checkerProgress {
|
|
1166
|
-
font-size: 11px;
|
|
1167
|
-
font-variant-numeric: tabular-nums;
|
|
1168
|
-
color: var(--dsw-alias-text-secondary, currentColor);
|
|
1169
|
-
}
|
|
1170
|
-
|
|
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}
|
|
1171
1270
|
`
|
|
1172
1271
|
|
|
1173
1272
|
|
|
@@ -1181,10 +1280,12 @@ window.__ModuleLoader__.load({
|
|
|
1181
1280
|
'nav': '模型显示',
|
|
1182
1281
|
'page.title': '模型显示',
|
|
1183
1282
|
'page.description':
|
|
1184
|
-
'
|
|
1185
|
-
'page.
|
|
1186
|
-
'page.
|
|
1187
|
-
'page.
|
|
1283
|
+
'关闭的模型不会出现在聊天输入框的模型下拉列表中;这里的改动立即生效,之后仍可随时重新打开。每个模型标注真实能力:适配器声明优先,其次读中转站模型列表,最后用校验结果或模型名补全。',
|
|
1284
|
+
'page.about': '说明',
|
|
1285
|
+
'page.aboutTitle': '展开标签含义、来源与颜色说明',
|
|
1286
|
+
'page.summary': '已显示 {visible} / {total}',
|
|
1287
|
+
'page.hiddenSummary': '已关闭 {count}',
|
|
1288
|
+
'page.kindSummary': '生图/生视频 {count}',
|
|
1188
1289
|
'page.loading': '正在加载模型列表…',
|
|
1189
1290
|
'page.empty': '暂无可用模型。',
|
|
1190
1291
|
'page.filterEmpty': '当前筛选下没有模型。',
|
|
@@ -1192,31 +1293,33 @@ window.__ModuleLoader__.load({
|
|
|
1192
1293
|
'page.saveFailed': '保存失败,请重试。',
|
|
1193
1294
|
'page.refresh': '刷新',
|
|
1194
1295
|
'page.refreshing': '刷新中…',
|
|
1296
|
+
'page.refreshTitle': '重新读取适配器与中转站的能力信息(沿用缓存,10 分钟内的探测不会重发请求)',
|
|
1195
1297
|
'page.reload': '重新获取能力',
|
|
1196
1298
|
'page.reloading': '正在获取…',
|
|
1197
1299
|
'page.reloadTitle':
|
|
1198
1300
|
'清空本插件的能力缓存和正在进行的读取,重新询问适配器与所有中转站;不会改动任何模型开关。适配器没有公布的思考强度仍不会凭空出现。',
|
|
1199
1301
|
'page.reloadUnavailable': 'Host 半不支持强制重新获取;重启 DSH 后可用。',
|
|
1200
|
-
'page.collapseGroups': '
|
|
1201
|
-
'page.expandGroups': '
|
|
1302
|
+
'page.collapseGroups': '折叠全部',
|
|
1303
|
+
'page.expandGroups': '展开全部',
|
|
1202
1304
|
'page.collapseGroupsTitle': '只保留分组标题,方便跳到其他分组',
|
|
1203
1305
|
'page.expandGroupsTitle': '展开所有被折叠的分组',
|
|
1204
1306
|
'page.showAll': '全部显示',
|
|
1205
1307
|
'page.legend':
|
|
1206
|
-
'
|
|
1308
|
+
'标签:推理 = 可调推理等级;上下文 = 输入窗口;多模态 = 可输入图像。实线来自适配器、中转站或厂商文档,虚线来自联网校验或模型名推断。生图 / 生视频 / 嵌入等只标类型,不显示思考强度与文本标签。',
|
|
1207
1309
|
'page.metaFallback':
|
|
1208
1310
|
'模型能力信息暂不可用:上下文与模态显示为未知,推理档位仍来自官方目录。',
|
|
1209
1311
|
'page.hostStale':
|
|
1210
1312
|
'Host 半仍是旧版本:颜色与开关已更新,但「放行图像」和官方校正要等 DSH 重启(或 profile 重组)后才会生效。',
|
|
1211
|
-
'page.providerFailures': '
|
|
1212
|
-
'page.
|
|
1213
|
-
'page.
|
|
1214
|
-
'page.
|
|
1215
|
-
'page.
|
|
1216
|
-
'
|
|
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',
|
|
1217
1320
|
'usage.computing': '统计中…',
|
|
1218
1321
|
'usage.none': '—',
|
|
1219
|
-
'usage.
|
|
1322
|
+
'usage.detail': '输入 {input} · 输出 {output} · 缓存读 {cacheRead} · 缓存写 {cacheWrite}',
|
|
1220
1323
|
'usage.today': '今天',
|
|
1221
1324
|
'usage.todayTitle': '本地自然日(今天 0 点起)',
|
|
1222
1325
|
'usage.week': '本周',
|
|
@@ -1225,47 +1328,62 @@ window.__ModuleLoader__.load({
|
|
|
1225
1328
|
'usage.monthTitle': '本月 1 日 0 点起',
|
|
1226
1329
|
'usage.total': '总计',
|
|
1227
1330
|
'usage.totalTitle': '全部历史(更早的按会话创建日粗算)',
|
|
1228
|
-
'usage.exportUsage': '
|
|
1331
|
+
'usage.exportUsage': '用量 CSV',
|
|
1229
1332
|
'usage.exportUsageTitle': '下载按天分桶、含每模型 token 分解的 CSV',
|
|
1230
|
-
'usage.exportModels': '
|
|
1333
|
+
'usage.exportModels': '模型 CSV',
|
|
1231
1334
|
'usage.exportModelsTitle': '下载全部提供商模型的可见性与能力表',
|
|
1232
1335
|
'usage.hostStale': '宿主半尚未提供用量统计,重启 DSH 即可启用。',
|
|
1233
|
-
'checker.title': '
|
|
1336
|
+
'checker.title': '联网校验',
|
|
1234
1337
|
'checker.description':
|
|
1235
|
-
'
|
|
1338
|
+
'选定一个校验员模型:先拉 models.dev / OpenRouter / Hugging Face 三个公开目录做基准,再把其余模型分批交给它核对。公开目录已经知道的能力会直接采用,校验员答不上时也不例外。校验请求会消耗所选模型的额度,其 token 计入上方统计。提示:思考型模型会先花掉输出预算,若批次普遍失败,换一个非思考模型或降低其思考档位。',
|
|
1236
1339
|
'checker.provider': '校验员提供商',
|
|
1237
1340
|
'checker.model': '校验员模型',
|
|
1238
1341
|
'checker.start': '开始校验',
|
|
1239
1342
|
'checker.startTitle': '在宿主侧后台跑一轮全模型校验',
|
|
1240
1343
|
'checker.cancel': '取消',
|
|
1241
1344
|
'checker.cancelTitle': '中止正在进行的校验',
|
|
1242
|
-
'checker.writeBack': '
|
|
1345
|
+
'checker.writeBack': '同时写入提供商配置',
|
|
1243
1346
|
'checker.writeBackTitle':
|
|
1244
|
-
'开启:判定到的 input / contextWindow / 推理档位直接写进各提供商该模型条目(learned.json
|
|
1347
|
+
'开启:判定到的 input / contextWindow / 推理档位直接写进各提供商该模型条目(learned.json 只留写不进去的字段);关闭:只更新本页与快照。',
|
|
1245
1348
|
'checker.idle': '未运行',
|
|
1246
|
-
'checker.running': '进行中
|
|
1247
|
-
'checker.finished': '已完成 {done}/{total} ·
|
|
1349
|
+
'checker.running': '进行中{phase} · {done}/{total} · 已耗 {tokens}',
|
|
1350
|
+
'checker.finished': '已完成 {done}/{total} · 耗 {tokens}',
|
|
1248
1351
|
'checker.progress': '{done}/{total} 已核对 · 校验本身消耗 {tokens} token',
|
|
1249
|
-
'checker.
|
|
1250
|
-
'checker.
|
|
1251
|
-
'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} 个模型的能力来自公开目录(校验员未作答)。',
|
|
1252
1369
|
'checker.hostStale': '宿主半不支持联网校验,请重启 DSH 后使用。',
|
|
1253
1370
|
'checker.phase.public': '(读取公开目录)',
|
|
1254
1371
|
'checker.phase.checker': '(读取公开目录)',
|
|
1255
1372
|
'checker.phase.verifying': '(分批校验)',
|
|
1256
1373
|
'source.snapshot': '已校验',
|
|
1257
1374
|
'page.applyInput': '让输入框按能力标注放行图像',
|
|
1375
|
+
'page.applyInputShort': '多模态中转站模型自动补 image;关闭则只按 Provider 的 input 声明',
|
|
1258
1376
|
'page.applyInputHint':
|
|
1259
1377
|
'开启后,被标注为「多模态」的中转站对话模型会在发送请求时自动补上图像输入能力;关闭则完全按 Provider 配置里的 input 声明。只影响中转站(pi-ai)路由,不改任何配置文件;若某个上游实际拒绝图像,关闭此开关即可。',
|
|
1260
1378
|
'page.kindLegend':
|
|
1261
|
-
'
|
|
1262
|
-
'page.
|
|
1263
|
-
'page.searchPlaceholder': '搜索模型名称或 ID',
|
|
1264
|
-
'page.capabilityLabel': '能力',
|
|
1379
|
+
'类型配色:生图 蓝 · 生视频 绿 · 嵌入 橙 · 音频 紫 · 重排 红 · 审核 玫红 · 翻译 灰。「官方校正」表示目录与厂商文档不一致时以文档为准。',
|
|
1380
|
+
'page.searchPlaceholder': '搜索名称或 ID',
|
|
1265
1381
|
'page.capability.multimodal': '多模态',
|
|
1382
|
+
'page.capability.multimodalTitle': '只看可输入图像的模型',
|
|
1266
1383
|
'page.capability.reasoning': '支持推理',
|
|
1384
|
+
'page.capability.reasoningTitle': '只看公布了推理能力的模型',
|
|
1267
1385
|
'page.contextLabel': '上下文至少',
|
|
1268
|
-
'page.context.any': '
|
|
1386
|
+
'page.context.any': '上下文不限',
|
|
1269
1387
|
'page.context.128k': '128K+',
|
|
1270
1388
|
'page.context.256k': '256K+',
|
|
1271
1389
|
'page.context.512k': '512K+',
|
|
@@ -1277,15 +1395,15 @@ window.__ModuleLoader__.load({
|
|
|
1277
1395
|
'page.filter.other': '其它',
|
|
1278
1396
|
'group.showAll': '全组显示',
|
|
1279
1397
|
'group.hideAll': '全组隐藏',
|
|
1280
|
-
'group.showFiltered': '
|
|
1281
|
-
'group.hideFiltered': '
|
|
1398
|
+
'group.showFiltered': '显示结果',
|
|
1399
|
+
'group.hideFiltered': '隐藏结果',
|
|
1282
1400
|
'group.collapseNamed': '折叠 {name} 分组',
|
|
1283
1401
|
'group.expandNamed': '展开 {name} 分组',
|
|
1284
|
-
'group.
|
|
1285
|
-
'group.
|
|
1286
|
-
'group.
|
|
1287
|
-
'group.probeFailed': '
|
|
1288
|
-
'group.probeSkipped': '
|
|
1402
|
+
'group.probeOk': '{models} 个模型 · {caps} 带能力',
|
|
1403
|
+
'group.probeNoCaps': '{models} 个模型 · 无能力字段',
|
|
1404
|
+
'group.probePartial': '价格接口 {models} 个',
|
|
1405
|
+
'group.probeFailed': '读取失败',
|
|
1406
|
+
'group.probeSkipped': '未探测',
|
|
1289
1407
|
'tag.reasoning': '支持推理',
|
|
1290
1408
|
'tag.noReasoning': '不支持推理',
|
|
1291
1409
|
'tag.reasoningUnknown': '推理未知',
|
|
@@ -1308,7 +1426,6 @@ window.__ModuleLoader__.load({
|
|
|
1308
1426
|
'tag.kindVideoTitle': '视频生成模型(输出视频);类型来源:{source}',
|
|
1309
1427
|
'tag.correction': '官方校正',
|
|
1310
1428
|
'tag.correctionTitle': '{note}\n来源:{source}',
|
|
1311
|
-
'tag.resolution': '分辨率',
|
|
1312
1429
|
'tag.resolutionTitle': '模型名标注了 {value} 输出档位',
|
|
1313
1430
|
'tag.imageInput': '可改图',
|
|
1314
1431
|
'tag.imageInputTitle': '支持图像输入(图生图 / 参考图)',
|
|
@@ -1391,10 +1508,12 @@ window.__ModuleLoader__.load({
|
|
|
1391
1508
|
'nav': 'Model visibility',
|
|
1392
1509
|
'page.title': 'Model visibility',
|
|
1393
1510
|
'page.description':
|
|
1394
|
-
'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
|
|
1395
|
-
'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}',
|
|
1396
1515
|
'page.hiddenSummary': '{count} hidden',
|
|
1397
|
-
'page.kindSummary': '{count} image/video
|
|
1516
|
+
'page.kindSummary': '{count} image/video',
|
|
1398
1517
|
'page.loading': 'Loading model list…',
|
|
1399
1518
|
'page.empty': 'No models available.',
|
|
1400
1519
|
'page.filterEmpty': 'No models match this filter.',
|
|
@@ -1402,80 +1521,99 @@ window.__ModuleLoader__.load({
|
|
|
1402
1521
|
'page.saveFailed': 'Could not save this change. Try again.',
|
|
1403
1522
|
'page.refresh': 'Refresh',
|
|
1404
1523
|
'page.refreshing': 'Refreshing…',
|
|
1524
|
+
'page.refreshTitle': 'Read adapter and relay capabilities again (honours caches; a probe younger than 10 minutes is not repeated)',
|
|
1405
1525
|
'page.reload': 'Re-fetch capabilities',
|
|
1406
1526
|
'page.reloading': 'Re-fetching…',
|
|
1407
1527
|
'page.reloadTitle':
|
|
1408
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.',
|
|
1409
1529
|
'page.reloadUnavailable': 'The Host half does not support a hard re-fetch; restart DSH to enable it.',
|
|
1410
|
-
'page.collapseGroups': 'Collapse all
|
|
1411
|
-
'page.expandGroups': 'Expand all
|
|
1530
|
+
'page.collapseGroups': 'Collapse all',
|
|
1531
|
+
'page.expandGroups': 'Expand all',
|
|
1412
1532
|
'page.collapseGroupsTitle': 'Keep only group headers so other groups are easy to reach',
|
|
1413
1533
|
'page.expandGroupsTitle': 'Expand every collapsed group',
|
|
1414
1534
|
'page.showAll': 'Show all',
|
|
1415
1535
|
'page.legend':
|
|
1416
|
-
'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.',
|
|
1417
1537
|
'page.metaFallback':
|
|
1418
1538
|
'Model capability details are unavailable right now: context and modalities show as unknown, while effort levels still come from the shipped catalog.',
|
|
1419
1539
|
'page.hostStale':
|
|
1420
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).',
|
|
1421
1541
|
'page.providerFailures': 'Some providers could not be read: {names}',
|
|
1422
|
-
'page.
|
|
1423
|
-
'page.
|
|
1424
|
-
'page.
|
|
1425
|
-
'page.
|
|
1426
|
-
'
|
|
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',
|
|
1427
1548
|
'usage.computing': 'scanning…',
|
|
1428
1549
|
'usage.none': '—',
|
|
1429
|
-
'usage.
|
|
1550
|
+
'usage.detail': 'input {input} · output {output} · cache read {cacheRead} · cache write {cacheWrite}',
|
|
1430
1551
|
'usage.today': 'Today',
|
|
1552
|
+
'usage.week': 'Week',
|
|
1553
|
+
'usage.month': 'Month',
|
|
1554
|
+
'usage.total': 'Total',
|
|
1431
1555
|
'usage.todayTitle': 'Local calendar day',
|
|
1432
|
-
'usage.week': 'This week',
|
|
1433
1556
|
'usage.weekTitle': 'Since Monday 00:00 local',
|
|
1434
|
-
'usage.month': 'This month',
|
|
1435
1557
|
'usage.monthTitle': 'Since the 1st local',
|
|
1436
|
-
'usage.total': 'Total',
|
|
1437
1558
|
'usage.totalTitle': 'All history (older months attributed by session start day)',
|
|
1438
|
-
'usage.exportUsage': '
|
|
1559
|
+
'usage.exportUsage': 'Usage CSV',
|
|
1439
1560
|
'usage.exportUsageTitle': 'Download a day-bucketed CSV with per-model breakdown',
|
|
1440
|
-
'usage.exportModels': '
|
|
1561
|
+
'usage.exportModels': 'Models CSV',
|
|
1441
1562
|
'usage.exportModelsTitle': 'Download visibility and capability table of every provider model',
|
|
1442
1563
|
'usage.hostStale': 'The Host half does not provide usage stats yet; restart DSH to enable it.',
|
|
1443
|
-
'checker.title': '
|
|
1564
|
+
'checker.title': 'Online verification',
|
|
1444
1565
|
'checker.description':
|
|
1445
|
-
'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.',
|
|
1446
1567
|
'checker.provider': 'Checker provider',
|
|
1447
1568
|
'checker.model': 'Checker model',
|
|
1448
|
-
'checker.start': '
|
|
1569
|
+
'checker.start': 'Start',
|
|
1449
1570
|
'checker.startTitle': 'Run a background verification sweep in the Host',
|
|
1450
1571
|
'checker.cancel': 'Cancel',
|
|
1451
1572
|
'checker.cancelTitle': 'Abort the running sweep',
|
|
1452
|
-
'checker.writeBack': '
|
|
1573
|
+
'checker.writeBack': 'Also write into provider settings',
|
|
1453
1574
|
'checker.writeBackTitle':
|
|
1454
|
-
'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.',
|
|
1455
1576
|
'checker.idle': 'not run yet',
|
|
1456
|
-
'checker.running': 'running
|
|
1457
|
-
'checker.finished': 'finished {done}/{total} ·
|
|
1577
|
+
'checker.running': 'running{phase} · {done}/{total} · {tokens} spent',
|
|
1578
|
+
'checker.finished': 'finished {done}/{total} · {tokens} spent',
|
|
1458
1579
|
'checker.progress': '{done}/{total} verified · checker spent {tokens} tokens',
|
|
1459
|
-
'checker.
|
|
1460
|
-
'checker.
|
|
1461
|
-
'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).',
|
|
1462
1598
|
'checker.hostStale': 'The Host half does not support online verification yet; restart DSH.',
|
|
1463
|
-
'checker.phase.public': '(fetching public catalogs)',
|
|
1464
|
-
'checker.phase.checker': '(fetching public catalogs)',
|
|
1465
|
-
'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)',
|
|
1466
1602
|
'source.snapshot': 'verified',
|
|
1467
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',
|
|
1468
1606
|
'page.applyInputHint':
|
|
1469
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.',
|
|
1470
1608
|
'page.kindLegend':
|
|
1471
|
-
'
|
|
1472
|
-
'page.
|
|
1473
|
-
'page.searchPlaceholder': 'Search model name or ID',
|
|
1474
|
-
'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',
|
|
1475
1611
|
'page.capability.multimodal': 'Multimodal',
|
|
1612
|
+
'page.capability.multimodalTitle': 'Only models that accept image input',
|
|
1476
1613
|
'page.capability.reasoning': 'Reasoning',
|
|
1614
|
+
'page.capability.reasoningTitle': 'Only models that publish reasoning support',
|
|
1477
1615
|
'page.contextLabel': 'Context at least',
|
|
1478
|
-
'page.context.any': 'Any',
|
|
1616
|
+
'page.context.any': 'Any context',
|
|
1479
1617
|
'page.context.128k': '128K+',
|
|
1480
1618
|
'page.context.256k': '256K+',
|
|
1481
1619
|
'page.context.512k': '512K+',
|
|
@@ -1487,15 +1625,15 @@ window.__ModuleLoader__.load({
|
|
|
1487
1625
|
'page.filter.other': 'Other',
|
|
1488
1626
|
'group.showAll': 'Show all',
|
|
1489
1627
|
'group.hideAll': 'Hide all',
|
|
1490
|
-
'group.showFiltered': 'Show
|
|
1491
|
-
'group.hideFiltered': 'Hide
|
|
1628
|
+
'group.showFiltered': 'Show results',
|
|
1629
|
+
'group.hideFiltered': 'Hide results',
|
|
1492
1630
|
'group.collapseNamed': 'Collapse the {name} group',
|
|
1493
1631
|
'group.expandNamed': 'Expand the {name} group',
|
|
1494
|
-
'group.
|
|
1495
|
-
'group.
|
|
1496
|
-
'group.
|
|
1497
|
-
'group.probeFailed': '
|
|
1498
|
-
'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',
|
|
1499
1637
|
'tag.reasoning': 'Reasoning',
|
|
1500
1638
|
'tag.noReasoning': 'No reasoning',
|
|
1501
1639
|
'tag.reasoningUnknown': 'Reasoning unknown',
|
|
@@ -1518,7 +1656,6 @@ window.__ModuleLoader__.load({
|
|
|
1518
1656
|
'tag.kindVideoTitle': 'Video generation model (outputs video); kind source: {source}',
|
|
1519
1657
|
'tag.correction': 'Official correction',
|
|
1520
1658
|
'tag.correctionTitle': '{note}\nSource: {source}',
|
|
1521
|
-
'tag.resolution': 'Resolution',
|
|
1522
1659
|
'tag.resolutionTitle': 'The model name advertises a {value} output tier',
|
|
1523
1660
|
'tag.imageInput': 'Image edit',
|
|
1524
1661
|
'tag.imageInputTitle': 'Accepts image input (image-to-image / reference images)',
|
|
@@ -1704,9 +1841,16 @@ window.__ModuleLoader__.load({
|
|
|
1704
1841
|
return sources && typeof sources[field] === 'string' ? sources[field] : 'unknown'
|
|
1705
1842
|
}
|
|
1706
1843
|
|
|
1707
|
-
/**
|
|
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
|
+
*/
|
|
1708
1852
|
function isInferredSource(source) {
|
|
1709
|
-
return source === '
|
|
1853
|
+
return source === 'snapshot' || source === 'inferred'
|
|
1710
1854
|
}
|
|
1711
1855
|
|
|
1712
1856
|
/** Kind with a safe fallback for records from an older Host. */
|
|
@@ -2125,6 +2269,8 @@ window.__ModuleLoader__.load({
|
|
|
2125
2269
|
* @module client-src/pure
|
|
2126
2270
|
*/
|
|
2127
2271
|
|
|
2272
|
+
const { bucketOf, kindOf } = require("format")
|
|
2273
|
+
|
|
2128
2274
|
/** Number of one bucket's token sum, guarding partial buckets. */
|
|
2129
2275
|
function bucketTotalOf(buckets) {
|
|
2130
2276
|
if (!Array.isArray(buckets)) return 0
|
|
@@ -2199,11 +2345,9 @@ window.__ModuleLoader__.load({
|
|
|
2199
2345
|
}
|
|
2200
2346
|
|
|
2201
2347
|
function bucket(model) {
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
if (kind === 'chat') return 'chat'
|
|
2206
|
-
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))
|
|
2207
2351
|
}
|
|
2208
2352
|
|
|
2209
2353
|
/** Counts per kind bucket for the whole list. */
|
|
@@ -2224,7 +2368,11 @@ window.__ModuleLoader__.load({
|
|
|
2224
2368
|
return routes
|
|
2225
2369
|
}
|
|
2226
2370
|
|
|
2227
|
-
/**
|
|
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
|
+
*/
|
|
2228
2376
|
function probeOf(state, group, t) {
|
|
2229
2377
|
const list = Array.isArray(state.providers) ? state.providers : []
|
|
2230
2378
|
const diagnostic = list.find((item) => item && item.id === group.id)
|
|
@@ -2232,6 +2380,8 @@ window.__ModuleLoader__.load({
|
|
|
2232
2380
|
const details = [
|
|
2233
2381
|
diagnostic.host ? 'host: ' + diagnostic.host : null,
|
|
2234
2382
|
diagnostic.status ? 'status: ' + diagnostic.status : null,
|
|
2383
|
+
diagnostic.models ? 'models: ' + diagnostic.models : null,
|
|
2384
|
+
diagnostic.capabilities ? 'capabilities: ' + diagnostic.capabilities : null,
|
|
2235
2385
|
diagnostic.message ? 'message: ' + diagnostic.message : null,
|
|
2236
2386
|
diagnostic.pricing && diagnostic.pricing !== 'skipped' ? 'pricing: ' + diagnostic.pricing : null,
|
|
2237
2387
|
]
|
|
@@ -2249,8 +2399,19 @@ window.__ModuleLoader__.load({
|
|
|
2249
2399
|
title: details,
|
|
2250
2400
|
}
|
|
2251
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
|
+
}
|
|
2252
2409
|
if (diagnostic.status === 'failed' || diagnostic.status === 'unauthorized') {
|
|
2253
|
-
return {
|
|
2410
|
+
return {
|
|
2411
|
+
label: t('group.probeFailed'),
|
|
2412
|
+
title: details,
|
|
2413
|
+
failed: true,
|
|
2414
|
+
}
|
|
2254
2415
|
}
|
|
2255
2416
|
return { label: t('group.probeSkipped'), title: details }
|
|
2256
2417
|
}
|
|
@@ -2272,6 +2433,7 @@ window.__ModuleLoader__.load({
|
|
|
2272
2433
|
const React = require('react')
|
|
2273
2434
|
const h = React.createElement
|
|
2274
2435
|
|
|
2436
|
+
const { FIELD } = require("constants")
|
|
2275
2437
|
const { keyOf, kindOf, isGenerationKind, providerLabel, countModels, readCollapsedGroups, persistCollapsedGroups, overrideKeyOf } = require("format")
|
|
2276
2438
|
const { computeFilteredGroups, countKinds, probeOf } = require("pure")
|
|
2277
2439
|
const { capabilityTags, tagClassOf } = require("tags")
|
|
@@ -2280,6 +2442,33 @@ window.__ModuleLoader__.load({
|
|
|
2280
2442
|
const { UsageToolbar } = require("usage-view")
|
|
2281
2443
|
const { VerifyPanel } = require("checker-view")
|
|
2282
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
|
+
*/
|
|
2283
2472
|
function makeSection(form, catalog, overrides, usage, verify) {
|
|
2284
2473
|
return function ModelVisibilitySection({ t }) {
|
|
2285
2474
|
const { hidden, snapshot } = useHidden(form)
|
|
@@ -2290,6 +2479,7 @@ window.__ModuleLoader__.load({
|
|
|
2290
2479
|
const [refreshing, setRefreshing] = React.useState(false)
|
|
2291
2480
|
const [reloading, setReloading] = React.useState(false)
|
|
2292
2481
|
const [collapsed, setCollapsed] = React.useState(readCollapsedGroups)
|
|
2482
|
+
const [aboutOpen, setAboutOpen] = React.useState(readAboutOpen)
|
|
2293
2483
|
const [editing, setEditing] = React.useState(null)
|
|
2294
2484
|
const [providerNote, setProviderNote] = React.useState(null)
|
|
2295
2485
|
|
|
@@ -2339,16 +2529,18 @@ window.__ModuleLoader__.load({
|
|
|
2339
2529
|
if (disabled) return
|
|
2340
2530
|
setBusy(true)
|
|
2341
2531
|
setError(null)
|
|
2342
|
-
Promise.resolve(form.set(field, value))
|
|
2343
|
-
(
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
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
|
+
})
|
|
2352
2544
|
}
|
|
2353
2545
|
|
|
2354
2546
|
const commit = (next) => writeField(FIELD, Array.from(next))
|
|
@@ -2389,7 +2581,7 @@ window.__ModuleLoader__.load({
|
|
|
2389
2581
|
// A hard reload may supersede a soft refresh that is still running;
|
|
2390
2582
|
// catalog.load() gives it a new run and discards the older result.
|
|
2391
2583
|
const reloadCatalog = () => {
|
|
2392
|
-
if (busy || reloading) return
|
|
2584
|
+
if (busy || reloading || refreshing) return
|
|
2393
2585
|
runCatalogAction(() => catalog.reload(), setReloading)
|
|
2394
2586
|
}
|
|
2395
2587
|
|
|
@@ -2412,24 +2604,31 @@ window.__ModuleLoader__.load({
|
|
|
2412
2604
|
})
|
|
2413
2605
|
}
|
|
2414
2606
|
|
|
2607
|
+
const toggleAbout = () => {
|
|
2608
|
+
setAboutOpen((previous) => {
|
|
2609
|
+
persistAboutOpen(!previous)
|
|
2610
|
+
return !previous
|
|
2611
|
+
})
|
|
2612
|
+
}
|
|
2613
|
+
|
|
2614
|
+
/* ------------------------------------------------------------- groups */
|
|
2615
|
+
|
|
2415
2616
|
const rows = []
|
|
2416
2617
|
for (const group of filteredGroups) {
|
|
2417
2618
|
const allGroupModels =
|
|
2418
2619
|
(groups.find((candidate) => candidate.id === group.id) || group).models || []
|
|
2419
2620
|
const models = group.models || []
|
|
2420
|
-
const shown = allGroupModels.filter(
|
|
2421
|
-
(model) => !hidden.has(keyOf(group.id, model.id)),
|
|
2422
|
-
).length
|
|
2621
|
+
const shown = allGroupModels.filter((model) => !hidden.has(keyOf(group.id, model.id))).length
|
|
2423
2622
|
const allShown = models.every((model) => !hidden.has(keyOf(group.id, model.id)))
|
|
2424
2623
|
const kinds = countKinds(allGroupModels)
|
|
2425
2624
|
const kindParts = []
|
|
2426
|
-
if (kinds.chat > 0) kindParts.push(
|
|
2427
|
-
if (kinds.image > 0) kindParts.push(
|
|
2428
|
-
if (kinds.video > 0) kindParts.push(
|
|
2429
|
-
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')
|
|
2430
2629
|
const kindNodes = []
|
|
2431
|
-
for (const [index,
|
|
2432
|
-
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' }, '·'))
|
|
2433
2632
|
kindNodes.push(
|
|
2434
2633
|
h(
|
|
2435
2634
|
'span',
|
|
@@ -2438,8 +2637,9 @@ window.__ModuleLoader__.load({
|
|
|
2438
2637
|
className:
|
|
2439
2638
|
'mvp_kindCount' +
|
|
2440
2639
|
(kindKey === 'chat' ? '' : ' mvp_kindCount--' + kindKey),
|
|
2640
|
+
title: t('page.filter.' + kindKey) + ' ' + kinds[kindKey],
|
|
2441
2641
|
},
|
|
2442
|
-
|
|
2642
|
+
kinds[kindKey],
|
|
2443
2643
|
),
|
|
2444
2644
|
)
|
|
2445
2645
|
}
|
|
@@ -2457,34 +2657,37 @@ window.__ModuleLoader__.load({
|
|
|
2457
2657
|
'div',
|
|
2458
2658
|
{ className: 'mvp_groupHead' },
|
|
2459
2659
|
h(
|
|
2460
|
-
'
|
|
2461
|
-
{
|
|
2462
|
-
|
|
2463
|
-
'
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
onClick: () => toggleGroupCollapsed(group.id),
|
|
2471
|
-
},
|
|
2472
|
-
collapsedNow ? '▸' : '▾',
|
|
2473
|
-
),
|
|
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 ? '▸' : '▾'),
|
|
2474
2670
|
h('span', { className: 'mvp_groupName' }, groupName),
|
|
2475
2671
|
h('span', { className: 'mvp_groupCount' }, shown + '/' + allGroupModels.length),
|
|
2476
2672
|
kindParts.length > 0
|
|
2477
2673
|
? h('span', { className: 'mvp_groupCap' }, kindNodes)
|
|
2478
2674
|
: null,
|
|
2479
2675
|
probe !== null
|
|
2480
|
-
? 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
|
+
)
|
|
2481
2684
|
: null,
|
|
2482
2685
|
),
|
|
2483
2686
|
h(
|
|
2484
2687
|
'button',
|
|
2485
2688
|
{
|
|
2486
2689
|
type: 'button',
|
|
2487
|
-
className: 'mvp_button',
|
|
2690
|
+
className: 'mvp_button mvp_button--quiet',
|
|
2488
2691
|
disabled: disabled,
|
|
2489
2692
|
onClick: () => setGroup(group, models, !allShown),
|
|
2490
2693
|
},
|
|
@@ -2516,15 +2719,20 @@ window.__ModuleLoader__.load({
|
|
|
2516
2719
|
className:
|
|
2517
2720
|
'mvp_row' +
|
|
2518
2721
|
(generation ? ' mvp_row--gen' : '') +
|
|
2722
|
+
(shownModel ? '' : ' mvp_row--off') +
|
|
2519
2723
|
kindTint,
|
|
2520
2724
|
},
|
|
2521
2725
|
h(
|
|
2522
2726
|
'div',
|
|
2523
2727
|
{ className: 'mvp_rowText' },
|
|
2524
|
-
h(
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
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
|
+
),
|
|
2528
2736
|
h(
|
|
2529
2737
|
'div',
|
|
2530
2738
|
{ className: 'mvp_tags' },
|
|
@@ -2549,7 +2757,7 @@ window.__ModuleLoader__.load({
|
|
|
2549
2757
|
'button',
|
|
2550
2758
|
{
|
|
2551
2759
|
type: 'button',
|
|
2552
|
-
className: 'mvp_button mvp_editButton',
|
|
2760
|
+
className: 'mvp_button mvp_button--quiet mvp_editButton',
|
|
2553
2761
|
'aria-pressed': editing === rowKey,
|
|
2554
2762
|
'aria-label': t('row.edit') + ': ' + model.name,
|
|
2555
2763
|
title: t('row.editTitle'),
|
|
@@ -2597,9 +2805,13 @@ window.__ModuleLoader__.load({
|
|
|
2597
2805
|
)
|
|
2598
2806
|
}
|
|
2599
2807
|
|
|
2808
|
+
/* ------------------------------------------------------------- status */
|
|
2809
|
+
|
|
2600
2810
|
const loading = state.status === 'loading' || state.status === 'idle'
|
|
2601
2811
|
const providerList = Array.isArray(state.providers) ? state.providers : []
|
|
2602
|
-
const probeOk = providerList.filter(
|
|
2812
|
+
const probeOk = providerList.filter(
|
|
2813
|
+
(provider) => provider && (provider.status === 'ok' || provider.status === 'partial'),
|
|
2814
|
+
).length
|
|
2603
2815
|
const probeCaps = providerList.reduce(
|
|
2604
2816
|
(count, provider) => count + (Number(provider && provider.capabilities) || 0),
|
|
2605
2817
|
0,
|
|
@@ -2610,10 +2822,7 @@ window.__ModuleLoader__.load({
|
|
|
2610
2822
|
? t('page.probeSummary', { ok: probeOk, caps: probeCaps })
|
|
2611
2823
|
: t('page.probeSummaryNone')
|
|
2612
2824
|
: null
|
|
2613
|
-
// A page refresh can deliver this bundle while the Host half is still
|
|
2614
|
-
// the previous version; the switch would look live but do nothing.
|
|
2615
2825
|
const hostStale = state.source === 'host' && !hostFeatures.includes('applyInput')
|
|
2616
|
-
// A hard reload is only meaningful when the Host half understands it.
|
|
2617
2826
|
const canReload = state.source === 'host' && hostFeatures.includes('reload')
|
|
2618
2827
|
const anyCollapsed = groups.some((group) => collapsed.has(group.id))
|
|
2619
2828
|
const learnedDate =
|
|
@@ -2638,54 +2847,52 @@ window.__ModuleLoader__.load({
|
|
|
2638
2847
|
]
|
|
2639
2848
|
const hasActiveFilters =
|
|
2640
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 */
|
|
2641
2863
|
|
|
2642
2864
|
return h(
|
|
2643
2865
|
'div',
|
|
2644
2866
|
{ className: 'mvp_page' },
|
|
2645
|
-
|
|
2867
|
+
// Title + summary + the two actions that are worth a permanent slot.
|
|
2646
2868
|
h(
|
|
2647
2869
|
'div',
|
|
2648
2870
|
{ className: 'mvp_head' },
|
|
2649
|
-
h('h2', { className: 'mvp_title' }, t('page.title')),
|
|
2650
|
-
h('p', { className: 'mvp_desc' }, t('page.description')),
|
|
2651
|
-
),
|
|
2652
|
-
h(
|
|
2653
|
-
'div',
|
|
2654
|
-
{ className: 'mvp_toolbar' },
|
|
2655
2871
|
h(
|
|
2656
2872
|
'div',
|
|
2657
|
-
{ className: '
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
:
|
|
2663
|
-
|
|
2664
|
-
|
|
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,
|
|
2665
2882
|
),
|
|
2666
2883
|
h(
|
|
2667
2884
|
'div',
|
|
2668
|
-
{ className: '
|
|
2885
|
+
{ className: 'mvp_headActions' },
|
|
2669
2886
|
h(
|
|
2670
2887
|
'button',
|
|
2671
2888
|
{
|
|
2672
2889
|
type: 'button',
|
|
2673
|
-
className: 'mvp_button',
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
t('page.showAll'),
|
|
2678
|
-
),
|
|
2679
|
-
h(
|
|
2680
|
-
'button',
|
|
2681
|
-
{
|
|
2682
|
-
type: 'button',
|
|
2683
|
-
className: 'mvp_button',
|
|
2684
|
-
disabled: groups.length === 0,
|
|
2685
|
-
title: t(anyCollapsed ? 'page.expandGroupsTitle' : 'page.collapseGroupsTitle'),
|
|
2686
|
-
onClick: toggleAllCollapsed,
|
|
2890
|
+
className: 'mvp_button mvp_button--quiet',
|
|
2891
|
+
'aria-pressed': aboutOpen,
|
|
2892
|
+
title: t('page.aboutTitle'),
|
|
2893
|
+
onClick: toggleAbout,
|
|
2687
2894
|
},
|
|
2688
|
-
t(
|
|
2895
|
+
t('page.about') + (aboutOpen ? ' ▴' : ' ▾'),
|
|
2689
2896
|
),
|
|
2690
2897
|
h(
|
|
2691
2898
|
'button',
|
|
@@ -2693,6 +2900,7 @@ window.__ModuleLoader__.load({
|
|
|
2693
2900
|
type: 'button',
|
|
2694
2901
|
className: 'mvp_button',
|
|
2695
2902
|
disabled: busy || refreshing || reloading,
|
|
2903
|
+
title: t('page.refreshTitle'),
|
|
2696
2904
|
onClick: refreshCatalog,
|
|
2697
2905
|
},
|
|
2698
2906
|
t(refreshing ? 'page.refreshing' : 'page.refresh'),
|
|
@@ -2710,63 +2918,77 @@ window.__ModuleLoader__.load({
|
|
|
2710
2918
|
),
|
|
2711
2919
|
),
|
|
2712
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 : [] }),
|
|
2713
2932
|
h(
|
|
2714
2933
|
'div',
|
|
2715
2934
|
{ className: 'mvp_filters' },
|
|
2716
|
-
h('
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
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
|
+
),
|
|
2726
2959
|
),
|
|
2727
|
-
h(
|
|
2728
|
-
|
|
2960
|
+
h(
|
|
2961
|
+
'div',
|
|
2962
|
+
{ className: 'mvp_filterGroup' },
|
|
2729
2963
|
h(
|
|
2730
2964
|
'button',
|
|
2731
2965
|
{
|
|
2732
|
-
key: value,
|
|
2733
2966
|
type: 'button',
|
|
2734
2967
|
className: 'mvp_filter',
|
|
2735
|
-
'aria-pressed':
|
|
2736
|
-
|
|
2968
|
+
'aria-pressed': multimodalOnly,
|
|
2969
|
+
title: t('page.capability.multimodalTitle'),
|
|
2970
|
+
onClick: () => setMultimodalOnly(!multimodalOnly),
|
|
2737
2971
|
},
|
|
2738
|
-
|
|
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'),
|
|
2739
2984
|
),
|
|
2740
|
-
),
|
|
2741
|
-
h('span', { className: 'mvp_filterLabel' }, t('page.capabilityLabel')),
|
|
2742
|
-
h(
|
|
2743
|
-
'button',
|
|
2744
|
-
{
|
|
2745
|
-
type: 'button',
|
|
2746
|
-
className: 'mvp_filter',
|
|
2747
|
-
'aria-pressed': multimodalOnly,
|
|
2748
|
-
onClick: () => setMultimodalOnly(!multimodalOnly),
|
|
2749
|
-
},
|
|
2750
|
-
t('page.capability.multimodal'),
|
|
2751
|
-
),
|
|
2752
|
-
h(
|
|
2753
|
-
'button',
|
|
2754
|
-
{
|
|
2755
|
-
type: 'button',
|
|
2756
|
-
className: 'mvp_filter',
|
|
2757
|
-
'aria-pressed': reasoningOnly,
|
|
2758
|
-
onClick: () => setReasoningOnly(!reasoningOnly),
|
|
2759
|
-
},
|
|
2760
|
-
t('page.capability.reasoning'),
|
|
2761
|
-
),
|
|
2762
|
-
h('label', { className: 'mvp_context' },
|
|
2763
|
-
h('span', { className: 'mvp_filterLabel' }, t('page.contextLabel')),
|
|
2764
2985
|
h(
|
|
2765
2986
|
'select',
|
|
2766
2987
|
{
|
|
2767
2988
|
className: 'mvp_contextSelect',
|
|
2768
2989
|
value: String(minContext),
|
|
2769
2990
|
'aria-label': t('page.contextLabel'),
|
|
2991
|
+
title: t('page.contextLabel'),
|
|
2770
2992
|
onChange: (event) => setMinContext(Number(event.target.value)),
|
|
2771
2993
|
},
|
|
2772
2994
|
[
|
|
@@ -2778,6 +3000,31 @@ window.__ModuleLoader__.load({
|
|
|
2778
3000
|
].map(([value, label]) => h('option', { key: value, value: String(value) }, t(label))),
|
|
2779
3001
|
),
|
|
2780
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
|
+
),
|
|
2781
3028
|
),
|
|
2782
3029
|
h(
|
|
2783
3030
|
'div',
|
|
@@ -2790,6 +3037,7 @@ window.__ModuleLoader__.load({
|
|
|
2790
3037
|
className: 'mvp_switch',
|
|
2791
3038
|
'aria-checked': applyInput,
|
|
2792
3039
|
'aria-label': t('page.applyInput'),
|
|
3040
|
+
'aria-describedby': 'mvp-apply-hint',
|
|
2793
3041
|
disabled: disabled,
|
|
2794
3042
|
onClick: () => commitApply(!applyInput),
|
|
2795
3043
|
},
|
|
@@ -2798,29 +3046,33 @@ window.__ModuleLoader__.load({
|
|
|
2798
3046
|
h(
|
|
2799
3047
|
'div',
|
|
2800
3048
|
{ className: 'mvp_applyText' },
|
|
2801
|
-
h('
|
|
2802
|
-
h('
|
|
3049
|
+
h('span', { className: 'mvp_applyTitle' }, t('page.applyInput')),
|
|
3050
|
+
h('span', { className: 'mvp_applyHint', id: 'mvp-apply-hint' }, t('page.applyInputShort')),
|
|
2803
3051
|
),
|
|
2804
3052
|
),
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
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
|
+
)
|
|
2814
3066
|
: null,
|
|
3067
|
+
hostStale ? h('div', { className: 'mvp_banner' }, t('page.hostStale')) : null,
|
|
2815
3068
|
state.detailsError !== null && state.source === 'catalog'
|
|
2816
|
-
? h('div', { className: '
|
|
3069
|
+
? h('div', { className: 'mvp_banner' }, t('page.metaFallback'))
|
|
2817
3070
|
: null,
|
|
2818
|
-
hostStale ? h('div', { className: 'mvp_fallback' }, t('page.hostStale')) : null,
|
|
2819
3071
|
state.failures.length > 0
|
|
2820
3072
|
? h(
|
|
2821
3073
|
'div',
|
|
2822
3074
|
{
|
|
2823
|
-
className: '
|
|
3075
|
+
className: 'mvp_banner mvp_banner--warn',
|
|
2824
3076
|
title: state.failures
|
|
2825
3077
|
.map((failure) => (failure.name || failure.id) + ': ' + failure.message)
|
|
2826
3078
|
.join('\n'),
|
|
@@ -2832,20 +3084,34 @@ window.__ModuleLoader__.load({
|
|
|
2832
3084
|
}),
|
|
2833
3085
|
)
|
|
2834
3086
|
: null,
|
|
2835
|
-
|
|
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,
|
|
2836
3091
|
state.error !== null
|
|
2837
|
-
? h(
|
|
3092
|
+
? h(
|
|
3093
|
+
'div',
|
|
3094
|
+
{ className: 'mvp_banner mvp_banner--error', role: 'alert' },
|
|
3095
|
+
t('page.error', { message: state.error }),
|
|
3096
|
+
)
|
|
2838
3097
|
: null,
|
|
2839
|
-
|
|
3098
|
+
h('div', { className: 'mvp_groups' }, rows),
|
|
2840
3099
|
total > 0 && hasActiveFilters && rows.length === 0
|
|
2841
3100
|
? h('div', { className: 'mvp_note' }, t('page.filterEmpty'))
|
|
2842
3101
|
: null,
|
|
2843
3102
|
rows.length === 0 && !loading && state.error === null && total === 0
|
|
2844
|
-
? h('div', { className: '
|
|
3103
|
+
? h('div', { className: 'mvp_note' }, t('page.empty'))
|
|
2845
3104
|
: null,
|
|
2846
|
-
|
|
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
|
+
),
|
|
2847
3113
|
providerNote !== null ? h('div', { className: 'mvp_note' }, providerNote) : null,
|
|
2848
|
-
|
|
3114
|
+
h(VerifyPanel, { verify, catalog: state, t, canUse: canVerify }),
|
|
2849
3115
|
)
|
|
2850
3116
|
}
|
|
2851
3117
|
}
|
|
@@ -3060,13 +3326,11 @@ window.__ModuleLoader__.load({
|
|
|
3060
3326
|
// -------------------------------------------------------- usage-view
|
|
3061
3327
|
define("usage-view", function (require, module, exports) {
|
|
3062
3328
|
/**
|
|
3063
|
-
* Token-usage strip with the
|
|
3329
|
+
* Token-usage strip with the two CSV exports.
|
|
3064
3330
|
*
|
|
3065
|
-
*
|
|
3066
|
-
*
|
|
3067
|
-
*
|
|
3068
|
-
*
|
|
3069
|
-
* @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.
|
|
3070
3334
|
*/
|
|
3071
3335
|
|
|
3072
3336
|
const React = require('react')
|
|
@@ -3076,15 +3340,22 @@ window.__ModuleLoader__.load({
|
|
|
3076
3340
|
const { formatTokens } = require("pure")
|
|
3077
3341
|
const { useSource } = require("hooks")
|
|
3078
3342
|
|
|
3079
|
-
/**
|
|
3080
|
-
function
|
|
3081
|
-
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')
|
|
3082
3352
|
const input = (range[0] ?? 0) + (range[2] ?? 0) + (range[3] ?? 0)
|
|
3083
3353
|
const output = range[1] ?? 0
|
|
3084
|
-
return t('usage.
|
|
3354
|
+
return t('usage.detail', {
|
|
3085
3355
|
input: formatTokens(input),
|
|
3086
3356
|
output: formatTokens(output),
|
|
3087
|
-
|
|
3357
|
+
cacheRead: formatTokens(range[2] ?? 0),
|
|
3358
|
+
cacheWrite: formatTokens(range[3] ?? 0),
|
|
3088
3359
|
})
|
|
3089
3360
|
}
|
|
3090
3361
|
|
|
@@ -3093,6 +3364,7 @@ window.__ModuleLoader__.load({
|
|
|
3093
3364
|
const anchor = document.createElement('a')
|
|
3094
3365
|
anchor.href = href
|
|
3095
3366
|
anchor.rel = 'noopener'
|
|
3367
|
+
anchor.download = ''
|
|
3096
3368
|
document.body.appendChild(anchor)
|
|
3097
3369
|
anchor.click()
|
|
3098
3370
|
anchor.remove()
|
|
@@ -3103,71 +3375,69 @@ window.__ModuleLoader__.load({
|
|
|
3103
3375
|
const supported = features.length === 0 || features.includes('usage')
|
|
3104
3376
|
const ranges = state.snapshot?.ranges
|
|
3105
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
|
+
|
|
3106
3390
|
return h(
|
|
3107
3391
|
'div',
|
|
3108
|
-
{ className: 'mvp_usage'
|
|
3392
|
+
{ className: 'mvp_usage' },
|
|
3109
3393
|
h(
|
|
3110
|
-
'
|
|
3111
|
-
{ className: '
|
|
3112
|
-
|
|
3394
|
+
'span',
|
|
3395
|
+
{ className: 'mvp_usageLabel' },
|
|
3396
|
+
t('usage.title'),
|
|
3113
3397
|
computing ? h('span', { className: 'mvp_usageComputing' }, t('usage.computing')) : null,
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
{
|
|
3120
|
-
type: 'button',
|
|
3121
|
-
className: 'mvp_button',
|
|
3122
|
-
title: t('usage.exportUsageTitle'),
|
|
3123
|
-
onClick: () => download(`${API}/export?type=usage`),
|
|
3124
|
-
},
|
|
3125
|
-
t('usage.exportUsage'),
|
|
3126
|
-
),
|
|
3398
|
+
),
|
|
3399
|
+
h(
|
|
3400
|
+
'span',
|
|
3401
|
+
{ className: 'mvp_usageRanges' },
|
|
3402
|
+
windows.map(([key, labelKey, titleKey]) =>
|
|
3127
3403
|
h(
|
|
3128
|
-
'
|
|
3404
|
+
'span',
|
|
3129
3405
|
{
|
|
3130
|
-
|
|
3131
|
-
className: '
|
|
3132
|
-
title: t('
|
|
3133
|
-
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),
|
|
3134
3409
|
},
|
|
3135
|
-
|
|
3410
|
+
h('span', { className: 'mvp_statLabel' }, t(labelKey)),
|
|
3411
|
+
h('span', { className: 'mvp_statValue' }, formatTokens(totalOf(ranges?.[key]))),
|
|
3136
3412
|
),
|
|
3137
3413
|
),
|
|
3138
3414
|
),
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
),
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
{ className: 'mvp_stat mvp_stat--total', title: t('usage.totalTitle') },
|
|
3164
|
-
h('span', { className: 'mvp_statLabel' }, t('usage.total')),
|
|
3165
|
-
h('span', { className: 'mvp_statValue' }, rangeText(ranges?.total, t)),
|
|
3166
|
-
),
|
|
3167
|
-
)
|
|
3168
|
-
: 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
|
+
),
|
|
3169
3439
|
state.error !== null
|
|
3170
|
-
? h('
|
|
3440
|
+
? h('span', { className: 'mvp_usageHint mvp_usageHint--warn' }, state.error)
|
|
3171
3441
|
: null,
|
|
3172
3442
|
)
|
|
3173
3443
|
}
|