@lolannn/dsh-effort-slider 0.2.6 → 0.2.7
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 +8 -8
- package/client.js +367 -141
- package/package.json +60 -60
package/README.md
CHANGED
|
@@ -46,6 +46,14 @@ actually using.
|
|
|
46
46
|
four-point stars, a breathing star core, and a three-armed galaxy that
|
|
47
47
|
collapses out of the rail at the top level. Reaching the top fires a shockwave
|
|
48
48
|
burst, a star-warp starfield and a light-blade flash.
|
|
49
|
+
- The composer itself carries the light: a halo wraps the whole input card, with
|
|
50
|
+
the same hue as the rail. Brightness, ring alpha and glow radius all follow the
|
|
51
|
+
level, so `off` is a faint rim and the top level breathes; opening the panel
|
|
52
|
+
lifts it. The pill only keeps an outline while the halo is on.
|
|
53
|
+
- The halo is a setting, not a hard rule. A `外圈光环` switch sits under the rail;
|
|
54
|
+
turn it off and the glow returns to the pill alone, the way it was before the
|
|
55
|
+
halo existed. The choice is stored in `localStorage` under
|
|
56
|
+
`effort-slider:halo`, so it survives a reload.
|
|
49
57
|
- Models without reasoning efforts are tagged `无档位` in the model list and
|
|
50
58
|
degrade gracefully once selected: the rail is dimmed and the status line says so
|
|
51
59
|
instead of pretending to switch.
|
|
@@ -65,11 +73,6 @@ would clip a `position: fixed` child.
|
|
|
65
73
|
|
|
66
74
|
- Click the trigger, then drag the rail or press `←` `→` `↑` `↓` `Home` `End`.
|
|
67
75
|
- The rail snaps to the nearest level on release (elastic overshoot).
|
|
68
|
-
- Reaching **high** turns the starfield into warp: the stars stream, the rail
|
|
69
|
-
becomes a slow galaxy swirl, and it stays lit for as long as the dial is at
|
|
70
|
-
high or above — so `xhigh` and `max` keep the look instead of only the top.
|
|
71
|
-
Dropping back below high switches it off. The top level still adds the full
|
|
72
|
-
celebration (flash, quake, caption).
|
|
73
76
|
- The level names under the rail are buttons too — same edge math as the thumb,
|
|
74
77
|
so each label sits exactly under its stop.
|
|
75
78
|
- Click the 模型 row to expand the model list; picking a model carries the current
|
|
@@ -92,9 +95,6 @@ would clip a `position: fixed` child.
|
|
|
92
95
|
- Only transforms, `opacity` and canvas are animated; no layout thrash.
|
|
93
96
|
- The canvas is sized from the card (`getBoundingClientRect` + `ResizeObserver`),
|
|
94
97
|
not from the viewport.
|
|
95
|
-
- The warp gate is the level named `high`; a model that declares no `high`
|
|
96
|
-
warps from the last level at or below it on the shared ladder, and a model
|
|
97
|
-
with one level never warps.
|
|
98
98
|
- Opening the popup while already on the top level arms the burst guard instead of
|
|
99
99
|
firing it, so the celebration only plays when you actually move into the top
|
|
100
100
|
level.
|
package/client.js
CHANGED
|
@@ -50,6 +50,8 @@ window.__ModuleLoader__.load({
|
|
|
50
50
|
clamped: "该模型没有「{from}」档,已自动切到最接近的「{to}」",
|
|
51
51
|
noEffortModel: "「{model}」没有思考强度档位,已按模型默认运行",
|
|
52
52
|
noLevelsTag: "无档位",
|
|
53
|
+
haloLabel: "外圈光环",
|
|
54
|
+
haloTitle: "在输入框外面加一圈随档位变化的光环;关闭后只在档位按钮周围发光",
|
|
53
55
|
boom: "宇宙毁灭",
|
|
54
56
|
afterBoom: "星际探索"
|
|
55
57
|
};
|
|
@@ -72,6 +74,8 @@ window.__ModuleLoader__.load({
|
|
|
72
74
|
clamped: "This model has no \"{from}\" level — switched to the nearest one, \"{to}\"",
|
|
73
75
|
noEffortModel: "\"{model}\" exposes no effort levels — running at its default",
|
|
74
76
|
noLevelsTag: "no levels",
|
|
77
|
+
haloLabel: "Composer halo",
|
|
78
|
+
haloTitle: "Ring the input box with a glow that follows the effort level; off keeps the glow on the pill only",
|
|
75
79
|
boom: "SINGULARITY",
|
|
76
80
|
afterBoom: "DEEP SPACE"
|
|
77
81
|
};
|
|
@@ -84,8 +88,12 @@ window.__ModuleLoader__.load({
|
|
|
84
88
|
".es-anchor{display:inline-flex;align-items:center;min-width:0;max-width:100%}",
|
|
85
89
|
".es-trigger{--es-hue:214;--es-e:0;position:relative;display:inline-flex;align-items:center;gap:6px;height:28px;max-width:min(340px,44vw);padding:0 9px;margin:0;border-radius:999px;border:1px solid transparent;background:transparent;color:#c3cbdb;font:inherit;font-size:12.5px;font-weight:500;line-height:1;cursor:pointer;white-space:nowrap;transition:background .2s ease,border-color .25s ease,color .2s ease,box-shadow .35s ease}",
|
|
86
90
|
".es-trigger:hover{background:rgba(255,255,255,.055);border-color:rgba(255,255,255,.1);color:#eef2fb}",
|
|
87
|
-
'.es-trigger[data-open="1"]{background:rgba(255,255,255,.
|
|
91
|
+
'.es-trigger[data-open="1"]{background:rgba(255,255,255,.09);border-color:hsl(var(--es-hue) 100% 76% / .34);color:#fff}',
|
|
88
92
|
'.es-trigger[data-locked="1"]{opacity:.5;cursor:default}',
|
|
93
|
+
".es-halo{position:fixed;z-index:2147482600;pointer-events:none;opacity:0;border-radius:20px;border:1px solid hsl(var(--es-hue) 100% 74% / var(--es-a0,.12));box-shadow:0 0 0 1px hsl(var(--es-hue) 100% 74% / var(--es-a1,.05)),0 0 var(--es-b1,10px) hsl(var(--es-hue) 100% 64% / var(--es-a2,.1)),0 0 var(--es-b2,34px) hsl(var(--es-hue) 100% 58% / var(--es-a3,.05));transition:opacity .5s ease,box-shadow .45s ease,border-color .45s ease;will-change:opacity,box-shadow}",
|
|
94
|
+
'.es-halo[data-on="1"]{opacity:1}',
|
|
95
|
+
'.es-halo[data-hot="1"]{animation:es-halo-breathe 3.6s ease-in-out infinite}',
|
|
96
|
+
"@keyframes es-halo-breathe{0%,100%{opacity:.62}50%{opacity:1}}",
|
|
89
97
|
".es-trigger-bolt{display:var(--dsh-composer-model-icon-display,none);width:14px;height:14px;flex:none;align-items:center;justify-content:center;color:hsl(var(--es-hue) 100% 78%)}",
|
|
90
98
|
".es-trigger-text{display:var(--dsh-composer-model-text-display,inline);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:180px}",
|
|
91
99
|
".es-trigger-effort{display:var(--dsh-composer-model-text-display,inline);font-weight:600;color:hsl(var(--es-hue) 100% 84%);text-shadow:0 0 10px hsl(var(--es-hue) 100% 70% / .45)}",
|
|
@@ -133,6 +141,12 @@ window.__ModuleLoader__.load({
|
|
|
133
141
|
".es-foot{display:flex;align-items:center;gap:8px;padding:10px 2px 0;font-size:11.5px;color:#7f8798;min-height:18px;line-height:1.4}",
|
|
134
142
|
'.es-foot[data-err="1"]{color:#ff9a9a}',
|
|
135
143
|
'.es-foot[data-note="1"]{color:hsl(var(--es-hue) 100% 84%)}',
|
|
144
|
+
".es-opt{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:10px 2px 0;font-size:11.5px;color:#8b93a6}",
|
|
145
|
+
".es-switch{flex:none;width:34px;height:19px;padding:0;border-radius:999px;border:1px solid rgba(255,255,255,.14);background:rgba(255,255,255,.06);position:relative;cursor:pointer;transition:background .2s ease,border-color .2s ease}",
|
|
146
|
+
'.es-switch[data-on="1"]{background:hsl(var(--es-hue) 100% 62% / .38);border-color:hsl(var(--es-hue) 100% 70% / .55)}',
|
|
147
|
+
".es-switch-knob{position:absolute;top:1.5px;left:1.5px;width:14px;height:14px;border-radius:50%;background:#cfd6e6;transition:transform .18s ease,background .18s ease}",
|
|
148
|
+
'.es-switch[data-on="1"] .es-switch-knob{transform:translateX(15px);background:#fff}',
|
|
149
|
+
'.es-trigger[data-halo="0"][data-open="1"]{box-shadow:0 0 14px hsl(var(--es-hue) 100% 66% / .28)}',
|
|
136
150
|
".es-fx{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;mix-blend-mode:screen;display:block}",
|
|
137
151
|
".es-flash{position:absolute;inset:0;pointer-events:none;opacity:0;background:radial-gradient(60% 60% at 50% 50%,#fff,rgba(190,215,255,.6) 45%,transparent 72%)}",
|
|
138
152
|
".es-flash.go{animation:es-flash 1.1s ease-out both}",
|
|
@@ -427,9 +441,6 @@ window.__ModuleLoader__.load({
|
|
|
427
441
|
anim: null,
|
|
428
442
|
dragging: false,
|
|
429
443
|
warp: 0,
|
|
430
|
-
heat: 0,
|
|
431
|
-
warpFrom: -1,
|
|
432
|
-
warpFired: false,
|
|
433
444
|
topFired: false,
|
|
434
445
|
reduce: false,
|
|
435
446
|
els: null,
|
|
@@ -514,7 +525,7 @@ window.__ModuleLoader__.load({
|
|
|
514
525
|
s.y = Math.random() * 2 - 1;
|
|
515
526
|
s.z = 0.98;
|
|
516
527
|
s.born = S.time;
|
|
517
|
-
s.life = S.
|
|
528
|
+
s.life = S.idx === topIndex() ? 6 + Math.random() * 5.5 : 4.2 + Math.random() * 4.2;
|
|
518
529
|
}
|
|
519
530
|
|
|
520
531
|
function initStars() {
|
|
@@ -558,23 +569,8 @@ window.__ModuleLoader__.load({
|
|
|
558
569
|
}
|
|
559
570
|
}
|
|
560
571
|
|
|
561
|
-
/**
|
|
562
|
-
* Visual spec for the rail. At or above the warp gate the rail renders
|
|
563
|
-
* the galaxy tier — the same swirl the top level shows — and keeps it for
|
|
564
|
-
* as long as the dial stays there.
|
|
565
|
-
*/
|
|
566
|
-
function railSpec() {
|
|
567
|
-
const spec = specAt(S.t);
|
|
568
|
-
if (S.heat > 0.01 && spec.kind !== "none") {
|
|
569
|
-
spec.kind = "galaxy";
|
|
570
|
-
spec.n = Math.max(spec.n, 38);
|
|
571
|
-
spec.ribbon = false;
|
|
572
|
-
}
|
|
573
|
-
return spec;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
572
|
function ensureParticles() {
|
|
577
|
-
const spec =
|
|
573
|
+
const spec = specAt(S.t);
|
|
578
574
|
buildParticles(spec.n, spec.kind);
|
|
579
575
|
}
|
|
580
576
|
|
|
@@ -763,7 +759,7 @@ window.__ModuleLoader__.load({
|
|
|
763
759
|
const hh = S.rh;
|
|
764
760
|
c.setTransform(S.rdpr, 0, 0, S.rdpr, 0, 0);
|
|
765
761
|
c.clearRect(0, 0, w, hh);
|
|
766
|
-
const spec =
|
|
762
|
+
const spec = specAt(S.t);
|
|
767
763
|
const e = S.e;
|
|
768
764
|
const hue = hueAtT(S.t);
|
|
769
765
|
const pad = 11;
|
|
@@ -839,26 +835,22 @@ window.__ModuleLoader__.load({
|
|
|
839
835
|
}
|
|
840
836
|
|
|
841
837
|
if (spec.kind === "galaxy") {
|
|
842
|
-
const ga = clamp01(
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
const gw = Math.max(24, reach);
|
|
846
|
-
const cxx = pad + gw * 0.5;
|
|
847
|
-
const gr = gw * 0.24;
|
|
848
|
-
const core = c.createRadialGradient(cxx, cyy, 0, cxx, cyy, gr);
|
|
838
|
+
const ga = clamp01((e - 0.9) / 0.1);
|
|
839
|
+
const cxx = pad + iw * 0.5;
|
|
840
|
+
const core = c.createRadialGradient(cxx, cyy, 0, cxx, cyy, iw * 0.24);
|
|
849
841
|
core.addColorStop(0, "rgba(255,255,255,.95)");
|
|
850
842
|
core.addColorStop(0.35, "hsla(196,100%,82%,.75)");
|
|
851
843
|
core.addColorStop(0.72, "hsla(258,100%,62%,.34)");
|
|
852
844
|
core.addColorStop(1, "hsla(258,100%,60%,0)");
|
|
853
845
|
c.fillStyle = core;
|
|
854
846
|
c.beginPath();
|
|
855
|
-
c.arc(cxx, cyy,
|
|
847
|
+
c.arc(cxx, cyy, iw * 0.24, 0, Math.PI * 2);
|
|
856
848
|
c.fill();
|
|
857
849
|
for (let a = 0; a < 3; a++) {
|
|
858
850
|
for (let i = 0; i < 130; i++) {
|
|
859
851
|
const f = i / 130;
|
|
860
852
|
const ang = a * ((Math.PI * 2) / 3) + f * 5.4 + S.time * 0.55;
|
|
861
|
-
const rad = f *
|
|
853
|
+
const rad = f * iw * 0.46;
|
|
862
854
|
const x = cxx + Math.cos(ang) * rad;
|
|
863
855
|
const y = cyy + Math.sin(ang) * rad * 0.42;
|
|
864
856
|
const alpha = (1 - f) * 0.5 * (0.4 + ga * 0.6);
|
|
@@ -959,33 +951,6 @@ window.__ModuleLoader__.load({
|
|
|
959
951
|
}
|
|
960
952
|
}
|
|
961
953
|
|
|
962
|
-
/**
|
|
963
|
-
* Light celebration for crossing the warp gate at a level below the top.
|
|
964
|
-
*
|
|
965
|
-
* The full boom (quake, white flash, the big caption) stays reserved for
|
|
966
|
-
* the top level; this one only throws streaks, embers and two thin rings,
|
|
967
|
-
* so the rail lights up the moment the dial reaches high territory.
|
|
968
|
-
*/
|
|
969
|
-
function surge() {
|
|
970
|
-
if (!S.els || S.reduce) return;
|
|
971
|
-
spawnStreaks();
|
|
972
|
-
S.waves.push({ r: 0, a: 0.72, w: 7, hue: 190 });
|
|
973
|
-
S.waves.push({ r: 0, a: 0.4, w: 18, hue: 300 });
|
|
974
|
-
for (let i = 0; i < 90; i++) {
|
|
975
|
-
const ang = Math.random() * Math.PI * 2;
|
|
976
|
-
const sp = 40 + Math.random() * 320;
|
|
977
|
-
S.debris.push({
|
|
978
|
-
x: S.W * 0.5,
|
|
979
|
-
y: S.H * 0.5,
|
|
980
|
-
vx: Math.cos(ang) * sp,
|
|
981
|
-
vy: Math.sin(ang) * sp,
|
|
982
|
-
life: 1,
|
|
983
|
-
hue: [190, 300, 45][i % 3],
|
|
984
|
-
size: 0.5 + Math.random() * 1.6
|
|
985
|
-
});
|
|
986
|
-
}
|
|
987
|
-
}
|
|
988
|
-
|
|
989
954
|
function boom() {
|
|
990
955
|
const els = S.els;
|
|
991
956
|
if (!els) return;
|
|
@@ -1049,27 +1014,13 @@ window.__ModuleLoader__.load({
|
|
|
1049
1014
|
}
|
|
1050
1015
|
}
|
|
1051
1016
|
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
* the very top: the gate is the index of the level named "high" (see
|
|
1055
|
-
* warpIndexOf), so moving into high — or anything above it — lights the
|
|
1056
|
-
* rail and keeps it lit until the slider drops back below.
|
|
1057
|
-
*/
|
|
1058
|
-
const gate = S.warpFrom >= 0 ? Math.min(S.warpFrom, top) : top;
|
|
1059
|
-
const inside = top > 0 && i >= gate;
|
|
1060
|
-
if (inside && !S.warpFired) {
|
|
1061
|
-
S.warpFired = true;
|
|
1062
|
-
S.warp = 1;
|
|
1063
|
-
if (i < top) surge();
|
|
1064
|
-
} else if (!inside) {
|
|
1065
|
-
S.warpFired = false;
|
|
1066
|
-
if (S.warp > 0.01) S.warp = 0;
|
|
1067
|
-
}
|
|
1068
|
-
if (i < top) {
|
|
1069
|
-
S.topFired = false;
|
|
1070
|
-
} else if (i === top && !S.topFired) {
|
|
1017
|
+
const isTop = top > 0 && i === top;
|
|
1018
|
+
if (isTop && !S.topFired) {
|
|
1071
1019
|
S.topFired = true;
|
|
1072
1020
|
boom();
|
|
1021
|
+
} else if (!isTop) {
|
|
1022
|
+
S.topFired = false;
|
|
1023
|
+
if (S.warp > 0.01) S.warp = 0;
|
|
1073
1024
|
}
|
|
1074
1025
|
}
|
|
1075
1026
|
|
|
@@ -1103,18 +1054,10 @@ window.__ModuleLoader__.load({
|
|
|
1103
1054
|
if (S.prevFade > 0) S.prevFade = Math.max(0, S.prevFade - dt / 0.24);
|
|
1104
1055
|
|
|
1105
1056
|
syncLevel();
|
|
1106
|
-
|
|
1107
|
-
/* 0 outside the warp gate, 1 inside it, eased so the galaxy on the rail
|
|
1108
|
-
* fades in instead of popping when the dial crosses high. */
|
|
1109
|
-
const heatTarget = S.warpFired ? 1 : 0;
|
|
1110
|
-
S.heat += (heatTarget - S.heat) * Math.min(1, dt * 7);
|
|
1111
|
-
if (Math.abs(heatTarget - S.heat) < 0.002) S.heat = heatTarget;
|
|
1112
|
-
|
|
1113
1057
|
ensureParticles();
|
|
1114
1058
|
|
|
1115
1059
|
const top = topIndex();
|
|
1116
|
-
|
|
1117
|
-
if (!S.reduce && top > 0 && S.idx >= gate && S.frameNo % 10 === 0) spawnStreaks();
|
|
1060
|
+
if (!S.reduce && top > 0 && S.idx === top && S.frameNo % 10 === 0) spawnStreaks();
|
|
1118
1061
|
|
|
1119
1062
|
drawBG(dt);
|
|
1120
1063
|
drawRail();
|
|
@@ -1206,14 +1149,12 @@ window.__ModuleLoader__.load({
|
|
|
1206
1149
|
S.texts = opts.texts || S.texts;
|
|
1207
1150
|
S.reduce = !!opts.reduce;
|
|
1208
1151
|
S.count = Math.max(1, (opts.count || 1) | 0);
|
|
1209
|
-
S.warpFrom = typeof opts.warpFrom === "number" ? opts.warpFrom | 0 : -1;
|
|
1210
1152
|
S.mounted = true;
|
|
1211
1153
|
S.time = 0;
|
|
1212
1154
|
S.frameNo = 0;
|
|
1213
1155
|
S.last = 0;
|
|
1214
1156
|
S.e = S.t;
|
|
1215
1157
|
S.topFired = false;
|
|
1216
|
-
S.warpFired = false;
|
|
1217
1158
|
S.warp = 0;
|
|
1218
1159
|
S.waves.length = 0;
|
|
1219
1160
|
S.debris.length = 0;
|
|
@@ -1282,20 +1223,6 @@ window.__ModuleLoader__.load({
|
|
|
1282
1223
|
S.idx = Math.min(S.idx, topIndexOf(S.count));
|
|
1283
1224
|
}
|
|
1284
1225
|
|
|
1285
|
-
/** Move the warp gate when the model (and so its level list) changes. */
|
|
1286
|
-
function setWarpFrom(index) {
|
|
1287
|
-
const next = typeof index === "number" && index >= 0 ? index | 0 : -1;
|
|
1288
|
-
if (next === S.warpFrom) return;
|
|
1289
|
-
S.warpFrom = next;
|
|
1290
|
-
if (!S.mounted) return;
|
|
1291
|
-
const top = topIndex();
|
|
1292
|
-
const gate = S.warpFrom >= 0 ? Math.min(S.warpFrom, top) : top;
|
|
1293
|
-
if (S.idx < gate) {
|
|
1294
|
-
S.warpFired = false;
|
|
1295
|
-
if (S.warp > 0.01) S.warp = 0;
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
1226
|
function setIndex(index, immediate) {
|
|
1300
1227
|
const top = topIndex();
|
|
1301
1228
|
const to = levelT(clamp(index, 0, top), S.count);
|
|
@@ -1305,17 +1232,11 @@ window.__ModuleLoader__.load({
|
|
|
1305
1232
|
S.e = to;
|
|
1306
1233
|
S.idx = Math.round(to * top);
|
|
1307
1234
|
/*
|
|
1308
|
-
* Opening the panel must not replay the celebration. Only a
|
|
1309
|
-
* into the
|
|
1310
|
-
*
|
|
1311
|
-
* warp glow itself stays on either way: the state, not the burst, is
|
|
1312
|
-
* what marks "this model is running hot".
|
|
1235
|
+
* Opening the panel must not replay the top-level celebration. Only a
|
|
1236
|
+
* real move into the top level (drag, click, keyboard) fires boom();
|
|
1237
|
+
* mounting while already at the top just arms the guard.
|
|
1313
1238
|
*/
|
|
1314
|
-
const gate = S.warpFrom >= 0 ? Math.min(S.warpFrom, top) : top;
|
|
1315
|
-
const inside = S.count > 1 && S.idx >= gate;
|
|
1316
1239
|
S.topFired = S.count > 1 && S.idx === top;
|
|
1317
|
-
S.warpFired = inside;
|
|
1318
|
-
S.warp = inside ? 1 : 0;
|
|
1319
1240
|
} else {
|
|
1320
1241
|
snapTo(to, 320);
|
|
1321
1242
|
}
|
|
@@ -1325,7 +1246,6 @@ window.__ModuleLoader__.load({
|
|
|
1325
1246
|
mount,
|
|
1326
1247
|
unmount,
|
|
1327
1248
|
setCount,
|
|
1328
|
-
setWarpFrom,
|
|
1329
1249
|
setIndex,
|
|
1330
1250
|
beginDrag,
|
|
1331
1251
|
isDragging() {
|
|
@@ -1483,25 +1403,231 @@ window.__ModuleLoader__.load({
|
|
|
1483
1403
|
}
|
|
1484
1404
|
|
|
1485
1405
|
/**
|
|
1486
|
-
*
|
|
1487
|
-
*
|
|
1488
|
-
*
|
|
1489
|
-
* model that declares no "high" gets the last level at or below high on the
|
|
1490
|
-
* shared effort ladder (a three-step off/low/max model starts warping at
|
|
1491
|
-
* low), and a model with a single level never warps at all.
|
|
1406
|
+
* The composer card that owns the trigger. The shipped DOM uses hashed
|
|
1407
|
+
* CSS-module names (`<hash>_card`), so match that suffix first and fall
|
|
1408
|
+
* back to "the nearest rounded box that owns a text field".
|
|
1492
1409
|
*/
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1410
|
+
/* A preference, not state: the halo can be switched off, and the choice
|
|
1411
|
+
outlives the page. localStorage is guarded because a host context may
|
|
1412
|
+
refuse it. */
|
|
1413
|
+
const HALO_KEY = "effort-slider:halo";
|
|
1414
|
+
function readHaloPref() {
|
|
1415
|
+
try {
|
|
1416
|
+
if (typeof window === "undefined" || !window.localStorage) return true;
|
|
1417
|
+
const raw = window.localStorage.getItem(HALO_KEY);
|
|
1418
|
+
return raw === null ? true : raw !== "0";
|
|
1419
|
+
} catch (err) {
|
|
1420
|
+
return true;
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
function writeHaloPref(on) {
|
|
1424
|
+
try {
|
|
1425
|
+
if (typeof window === "undefined" || !window.localStorage) return;
|
|
1426
|
+
window.localStorage.setItem(HALO_KEY, on ? "1" : "0");
|
|
1427
|
+
} catch (err) {
|
|
1428
|
+
/* a preference that cannot persist still applies to this page */
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
function composerBox(el) {
|
|
1433
|
+
if (!el || typeof el.closest !== "function") return null;
|
|
1434
|
+
const card = el.closest('[class*="_card"]');
|
|
1435
|
+
if (card && card.querySelector && card.querySelector('textarea,[contenteditable="true"],[role="textbox"]')) return card;
|
|
1436
|
+
let node = el.parentElement;
|
|
1437
|
+
while (node && node !== document.body) {
|
|
1438
|
+
if (node.querySelector && node.querySelector('textarea,[contenteditable="true"],[role="textbox"]')) {
|
|
1439
|
+
const cs = window.getComputedStyle(node);
|
|
1440
|
+
if ((parseFloat(cs.borderTopLeftRadius) || 0) >= 10) return node;
|
|
1441
|
+
}
|
|
1442
|
+
node = node.parentElement;
|
|
1503
1443
|
}
|
|
1504
|
-
return
|
|
1444
|
+
return null;
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
/** The page shares one ring, so a second mount cannot leave a ghost. */
|
|
1448
|
+
let HALO = null;
|
|
1449
|
+
function haloLayer() {
|
|
1450
|
+
if (HALO === null) HALO = createHalo();
|
|
1451
|
+
return HALO;
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
/**
|
|
1455
|
+
* One fixed ring that hugs the composer. It is created once and reused, so
|
|
1456
|
+
* re-renders never stack copies; the geometry follows the card through a
|
|
1457
|
+
* ResizeObserver plus the window events that can move it.
|
|
1458
|
+
*/
|
|
1459
|
+
function createHalo() {
|
|
1460
|
+
let node = null;
|
|
1461
|
+
let target = null;
|
|
1462
|
+
let observer = null;
|
|
1463
|
+
let frame = 0;
|
|
1464
|
+
let mounts = 0;
|
|
1465
|
+
let anchor = null;
|
|
1466
|
+
let pollTimer = 0;
|
|
1467
|
+
let until = 0;
|
|
1468
|
+
let lastGeom = "";
|
|
1469
|
+
|
|
1470
|
+
function paint() {
|
|
1471
|
+
if (!node) return;
|
|
1472
|
+
/*
|
|
1473
|
+
* The host may rebuild the composer node (a session switch, a layout
|
|
1474
|
+
* change) without telling us; look the card up again from the trigger
|
|
1475
|
+
* rather than keeping a ring pinned to a detached node.
|
|
1476
|
+
*/
|
|
1477
|
+
if ((!target || !target.isConnected) && anchor && anchor.isConnected) {
|
|
1478
|
+
const again = composerBox(anchor);
|
|
1479
|
+
if (again !== target) {
|
|
1480
|
+
target = again;
|
|
1481
|
+
if (again && typeof window.ResizeObserver === "function") {
|
|
1482
|
+
if (observer && typeof observer.disconnect === "function") observer.disconnect();
|
|
1483
|
+
observer = new window.ResizeObserver(schedule);
|
|
1484
|
+
observer.observe(again);
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
if (!target || !target.isConnected) {
|
|
1489
|
+
hide();
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
1492
|
+
const rect = target.getBoundingClientRect();
|
|
1493
|
+
if (!rect.width || !rect.height || rect.bottom < 0 || rect.top > window.innerHeight) {
|
|
1494
|
+
hide();
|
|
1495
|
+
return;
|
|
1496
|
+
}
|
|
1497
|
+
/*
|
|
1498
|
+
* The composer moves without resizing — a Session switch turns the
|
|
1499
|
+
* centred empty state into the docked conversation view, and a pure
|
|
1500
|
+
* ResizeObserver never fires for that. Re-read the geometry instead of
|
|
1501
|
+
* trusting the last placed rect, but only touch the DOM when it moved.
|
|
1502
|
+
*/
|
|
1503
|
+
const key = rect.left + "," + rect.top + "," + rect.width + "," + rect.height;
|
|
1504
|
+
if (key === lastGeom && node.dataset.on === "1") return;
|
|
1505
|
+
lastGeom = key;
|
|
1506
|
+
const radius = parseFloat(window.getComputedStyle(target).borderTopLeftRadius) || 16;
|
|
1507
|
+
node.style.left = rect.left - 2 + "px";
|
|
1508
|
+
node.style.top = rect.top - 2 + "px";
|
|
1509
|
+
node.style.width = rect.width + 4 + "px";
|
|
1510
|
+
node.style.height = rect.height + 4 + "px";
|
|
1511
|
+
node.style.borderRadius = radius + 2 + "px";
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
/** Frames follow the card closely for a moment after anything may have moved it. */
|
|
1515
|
+
function watchFor(ms) {
|
|
1516
|
+
until = Math.max(until, Date.now() + ms);
|
|
1517
|
+
if (frame) return;
|
|
1518
|
+
frame = window.requestAnimationFrame(track);
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
function track() {
|
|
1522
|
+
frame = 0;
|
|
1523
|
+
if (!node) return;
|
|
1524
|
+
paint();
|
|
1525
|
+
if (!target || Date.now() >= until) return;
|
|
1526
|
+
frame = window.requestAnimationFrame(track);
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
function schedule() {
|
|
1530
|
+
watchFor(1200);
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
/* A layout change can also land long after the switch, so a slow poll
|
|
1534
|
+
keeps watching for as long as the ring exists. */
|
|
1535
|
+
function poll() {
|
|
1536
|
+
pollTimer = setTimeout(() => {
|
|
1537
|
+
pollTimer = 0;
|
|
1538
|
+
if (!node) return;
|
|
1539
|
+
paint();
|
|
1540
|
+
poll();
|
|
1541
|
+
}, 250);
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
function startPoll() {
|
|
1545
|
+
if (pollTimer || typeof setTimeout !== "function") return;
|
|
1546
|
+
poll();
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
function stopPoll() {
|
|
1550
|
+
if (!pollTimer) return;
|
|
1551
|
+
clearTimeout(pollTimer);
|
|
1552
|
+
pollTimer = 0;
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
/** Leaving no paint behind matters more than leaving no state behind. */
|
|
1556
|
+
function hide() {
|
|
1557
|
+
if (!node) return;
|
|
1558
|
+
node.dataset.on = "0";
|
|
1559
|
+
node.dataset.hot = "0";
|
|
1560
|
+
node.style.opacity = "0.000";
|
|
1561
|
+
node.style.visibility = "hidden";
|
|
1562
|
+
lastGeom = "";
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
function ensure() {
|
|
1566
|
+
if (node) return node;
|
|
1567
|
+
/* An earlier copy of this module may have left its ring in the body. */
|
|
1568
|
+
if (typeof document.querySelectorAll === "function") {
|
|
1569
|
+
const stale = document.querySelectorAll(".es-halo");
|
|
1570
|
+
for (let i = 0; i < stale.length; i += 1) {
|
|
1571
|
+
if (stale[i].parentNode) stale[i].parentNode.removeChild(stale[i]);
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
node = document.createElement("div");
|
|
1575
|
+
node.className = "es-halo";
|
|
1576
|
+
node.setAttribute("aria-hidden", "true");
|
|
1577
|
+
document.body.appendChild(node);
|
|
1578
|
+
window.addEventListener("resize", schedule, { passive: true });
|
|
1579
|
+
document.addEventListener("scroll", schedule, { capture: true, passive: true });
|
|
1580
|
+
return node;
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
function place(box, opts, from) {
|
|
1584
|
+
const el = ensure();
|
|
1585
|
+
if (from) anchor = from;
|
|
1586
|
+
if (box !== target) {
|
|
1587
|
+
target = box;
|
|
1588
|
+
if (observer && typeof observer.disconnect === "function") observer.disconnect();
|
|
1589
|
+
observer = null;
|
|
1590
|
+
if (target && typeof window.ResizeObserver === "function") {
|
|
1591
|
+
observer = new window.ResizeObserver(schedule);
|
|
1592
|
+
observer.observe(target);
|
|
1593
|
+
}
|
|
1594
|
+
lastGeom = "";
|
|
1595
|
+
if (target) startPoll();
|
|
1596
|
+
watchFor(3000);
|
|
1597
|
+
}
|
|
1598
|
+
const heat = Math.max(0, Math.min(1, Number(opts.heat) || 0));
|
|
1599
|
+
el.style.setProperty("--es-hue", String(opts.hue | 0));
|
|
1600
|
+
el.style.setProperty("--es-a0", (0.1 + heat * 0.34).toFixed(3));
|
|
1601
|
+
el.style.setProperty("--es-a1", (0.04 + heat * 0.14).toFixed(3));
|
|
1602
|
+
el.style.setProperty("--es-a2", (0.1 + heat * 0.44).toFixed(3));
|
|
1603
|
+
el.style.setProperty("--es-a3", (0.05 + heat * 0.3).toFixed(3));
|
|
1604
|
+
el.style.setProperty("--es-b1", (10 + heat * 26).toFixed(1) + "px");
|
|
1605
|
+
el.style.setProperty("--es-b2", (34 + heat * 78).toFixed(1) + "px");
|
|
1606
|
+
el.style.opacity = String((!target ? 0 : opts.open ? 0.62 + heat * 0.38 : 0.2 + heat * 0.8).toFixed(3));
|
|
1607
|
+
el.style.visibility = target ? "visible" : "hidden";
|
|
1608
|
+
el.dataset.on = target ? "1" : "0";
|
|
1609
|
+
el.dataset.hot = target && opts.hot ? "1" : "0";
|
|
1610
|
+
if (!target) hide();
|
|
1611
|
+
else schedule();
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
function retain() {
|
|
1615
|
+
mounts += 1;
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
function release() {
|
|
1619
|
+
mounts = Math.max(0, mounts - 1);
|
|
1620
|
+
if (mounts > 0) return;
|
|
1621
|
+
stopPoll();
|
|
1622
|
+
lastGeom = "";
|
|
1623
|
+
if (observer && typeof observer.disconnect === "function") observer.disconnect();
|
|
1624
|
+
observer = null;
|
|
1625
|
+
target = null;
|
|
1626
|
+
anchor = null;
|
|
1627
|
+
hide();
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
return { place, retain, release };
|
|
1505
1631
|
}
|
|
1506
1632
|
|
|
1507
1633
|
class EffortController {
|
|
@@ -1513,6 +1639,13 @@ window.__ModuleLoader__.load({
|
|
|
1513
1639
|
this.defaultIndex = 0;
|
|
1514
1640
|
this.sessionId = void 0;
|
|
1515
1641
|
this.loading = false;
|
|
1642
|
+
/* A read requested while another is in flight is queued, not dropped:
|
|
1643
|
+
the correction that follows a Session switch almost always arrives
|
|
1644
|
+
during the first catalogue read, and dropping it left the pill on
|
|
1645
|
+
the deployment default until something else happened to refresh. */
|
|
1646
|
+
this.refreshQueued = false;
|
|
1647
|
+
this.bindTimer = 0;
|
|
1648
|
+
this.bindTries = 0;
|
|
1516
1649
|
this.commitTimer = 0;
|
|
1517
1650
|
this.pendingIndex = -1;
|
|
1518
1651
|
this.noticeTimer = 0;
|
|
@@ -1537,7 +1670,8 @@ window.__ModuleLoader__.load({
|
|
|
1537
1670
|
ready: false,
|
|
1538
1671
|
modelName: "",
|
|
1539
1672
|
modelId: "",
|
|
1540
|
-
provider: ""
|
|
1673
|
+
provider: "",
|
|
1674
|
+
halo: readHaloPref()
|
|
1541
1675
|
});
|
|
1542
1676
|
}
|
|
1543
1677
|
|
|
@@ -1575,6 +1709,7 @@ window.__ModuleLoader__.load({
|
|
|
1575
1709
|
close: () => this.close(),
|
|
1576
1710
|
toggle: () => this.toggle(),
|
|
1577
1711
|
toggleModels: () => this.toggleModels(),
|
|
1712
|
+
toggleHalo: () => this.toggleHalo(),
|
|
1578
1713
|
chooseModel: (provider, modelId) => this.chooseModel(provider, modelId),
|
|
1579
1714
|
reset: () => this.reset(),
|
|
1580
1715
|
commit: (i) => this.commit(i),
|
|
@@ -1583,8 +1718,18 @@ window.__ModuleLoader__.load({
|
|
|
1583
1718
|
};
|
|
1584
1719
|
}
|
|
1585
1720
|
|
|
1721
|
+
toggleHalo() {
|
|
1722
|
+
const on = !this.store.getSnapshot().halo;
|
|
1723
|
+
writeHaloPref(on);
|
|
1724
|
+
this.store.set({ halo: on });
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1586
1727
|
dispose() {
|
|
1587
1728
|
clearTimeout(this.commitTimer);
|
|
1729
|
+
if (this.bindTimer) {
|
|
1730
|
+
clearTimeout(this.bindTimer);
|
|
1731
|
+
this.bindTimer = 0;
|
|
1732
|
+
}
|
|
1588
1733
|
this.dropSelectionSubscription();
|
|
1589
1734
|
}
|
|
1590
1735
|
|
|
@@ -1670,9 +1815,25 @@ window.__ModuleLoader__.load({
|
|
|
1670
1815
|
*/
|
|
1671
1816
|
bindSelection() {
|
|
1672
1817
|
const face = this.selectionFace();
|
|
1673
|
-
|
|
1818
|
+
/* Both being undefined is not "already bound": a Session that was just
|
|
1819
|
+
created has no binding yet, and comparing undefined to undefined
|
|
1820
|
+
used to skip the retry that fixes it. */
|
|
1821
|
+
if (face) {
|
|
1822
|
+
if (face === this.selFace) return;
|
|
1823
|
+
} else {
|
|
1824
|
+
this.scheduleBind();
|
|
1825
|
+
return;
|
|
1826
|
+
}
|
|
1674
1827
|
this.dropSelectionSubscription();
|
|
1675
|
-
if (
|
|
1828
|
+
if (typeof face.subscribe !== "function") {
|
|
1829
|
+
this.scheduleBind();
|
|
1830
|
+
return;
|
|
1831
|
+
}
|
|
1832
|
+
this.bindTries = 0;
|
|
1833
|
+
if (this.bindTimer) {
|
|
1834
|
+
clearTimeout(this.bindTimer);
|
|
1835
|
+
this.bindTimer = 0;
|
|
1836
|
+
}
|
|
1676
1837
|
this.selFace = face;
|
|
1677
1838
|
this.selUnsub = face.subscribe(() => {
|
|
1678
1839
|
/* Opening the face and later commits both notify; defer so the
|
|
@@ -1683,6 +1844,17 @@ window.__ModuleLoader__.load({
|
|
|
1683
1844
|
});
|
|
1684
1845
|
}
|
|
1685
1846
|
|
|
1847
|
+
scheduleBind() {
|
|
1848
|
+
if (this.bindTimer || this.bindTries >= 12) return;
|
|
1849
|
+
const delay = this.bindTries < 6 ? 200 : 1000;
|
|
1850
|
+
this.bindTries += 1;
|
|
1851
|
+
this.bindTimer = setTimeout(() => {
|
|
1852
|
+
this.bindTimer = 0;
|
|
1853
|
+
this.bindSelection();
|
|
1854
|
+
this.refresh();
|
|
1855
|
+
}, delay);
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1686
1858
|
resolveSessionId() {
|
|
1687
1859
|
if (this.sessionId) return this.sessionId;
|
|
1688
1860
|
const sessions = readService(() => this.ctx.sessions);
|
|
@@ -1804,8 +1976,22 @@ window.__ModuleLoader__.load({
|
|
|
1804
1976
|
/* ---------------- catalogue ---------------- */
|
|
1805
1977
|
|
|
1806
1978
|
async refresh() {
|
|
1807
|
-
if (this.loading)
|
|
1979
|
+
if (this.loading) {
|
|
1980
|
+
this.refreshQueued = true;
|
|
1981
|
+
return;
|
|
1982
|
+
}
|
|
1808
1983
|
this.loading = true;
|
|
1984
|
+
try {
|
|
1985
|
+
do {
|
|
1986
|
+
this.refreshQueued = false;
|
|
1987
|
+
await this.refreshOnce();
|
|
1988
|
+
} while (this.refreshQueued);
|
|
1989
|
+
} finally {
|
|
1990
|
+
this.loading = false;
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1994
|
+
async refreshOnce() {
|
|
1809
1995
|
/* Reading a projection before subscribing to it can return no value at
|
|
1810
1996
|
all, so the follow-up starts the subscription first. */
|
|
1811
1997
|
this.bindSelection();
|
|
@@ -1813,12 +1999,10 @@ window.__ModuleLoader__.load({
|
|
|
1813
1999
|
try {
|
|
1814
2000
|
remote = this.ctx.remote.session;
|
|
1815
2001
|
} catch (err) {
|
|
1816
|
-
this.loading = false;
|
|
1817
2002
|
this.store.set({ supported: false, ready: true, levels: [], modelName: "", error: null, debug: "inject:" + messageOf(err) });
|
|
1818
2003
|
return;
|
|
1819
2004
|
}
|
|
1820
2005
|
if (!remote || typeof remote.modelCatalog !== "function") {
|
|
1821
|
-
this.loading = false;
|
|
1822
2006
|
this.store.set({
|
|
1823
2007
|
supported: false,
|
|
1824
2008
|
ready: true,
|
|
@@ -1888,8 +2072,6 @@ window.__ModuleLoader__.load({
|
|
|
1888
2072
|
});
|
|
1889
2073
|
} catch (err) {
|
|
1890
2074
|
this.store.set({ ready: true, error: messageOf(err), debug: "throw:" + messageOf(err) });
|
|
1891
|
-
} finally {
|
|
1892
|
-
this.loading = false;
|
|
1893
2075
|
}
|
|
1894
2076
|
}
|
|
1895
2077
|
|
|
@@ -2014,6 +2196,33 @@ window.__ModuleLoader__.load({
|
|
|
2014
2196
|
|
|
2015
2197
|
if (engineRef.current === null) engineRef.current = createEngine();
|
|
2016
2198
|
|
|
2199
|
+
/* The glow that used to sit on the pill now wraps the composer card. */
|
|
2200
|
+
react.useEffect(() => {
|
|
2201
|
+
const halo = haloLayer();
|
|
2202
|
+
halo.retain();
|
|
2203
|
+
return () => halo.release();
|
|
2204
|
+
}, []);
|
|
2205
|
+
react.useEffect(() => {
|
|
2206
|
+
const halo = haloLayer();
|
|
2207
|
+
const count = snap.levels.length;
|
|
2208
|
+
if (!snap.halo || !snap.ready || !count) {
|
|
2209
|
+
/* Hide, but keep the mount counted: release() belongs to unmount. */
|
|
2210
|
+
halo.place(null, { hue: 0, heat: 0, open: false, hot: false });
|
|
2211
|
+
return;
|
|
2212
|
+
}
|
|
2213
|
+
const heat = count > 1 ? snap.index / (count - 1) : 0;
|
|
2214
|
+
halo.place(
|
|
2215
|
+
composerBox(triggerRef.current),
|
|
2216
|
+
{
|
|
2217
|
+
hue: Math.round(hueAtT(snap.t)),
|
|
2218
|
+
heat: heat,
|
|
2219
|
+
open: snap.open,
|
|
2220
|
+
hot: heat >= 0.85
|
|
2221
|
+
},
|
|
2222
|
+
triggerRef.current
|
|
2223
|
+
);
|
|
2224
|
+
}, [snap.halo, snap.ready, snap.index, snap.levels.length, snap.open, snap.t]);
|
|
2225
|
+
|
|
2017
2226
|
/* Anchor the popup above the trigger, clamped into the viewport. */
|
|
2018
2227
|
react.useEffect(() => {
|
|
2019
2228
|
if (!snap.open) {
|
|
@@ -2059,7 +2268,6 @@ window.__ModuleLoader__.load({
|
|
|
2059
2268
|
}, [snap.open]);
|
|
2060
2269
|
|
|
2061
2270
|
const ready = snap.open && pos !== null;
|
|
2062
|
-
const warpFrom = warpIndexOf(snap.levels);
|
|
2063
2271
|
|
|
2064
2272
|
react.useEffect(() => {
|
|
2065
2273
|
if (!ready) return;
|
|
@@ -2078,7 +2286,6 @@ window.__ModuleLoader__.load({
|
|
|
2078
2286
|
big: bigRef.current
|
|
2079
2287
|
},
|
|
2080
2288
|
count: levelsRef.current.length,
|
|
2081
|
-
warpFrom: warpFrom,
|
|
2082
2289
|
texts: { boom: props.t("boom"), after: props.t("afterBoom") },
|
|
2083
2290
|
reduce,
|
|
2084
2291
|
onLevel: (i) => props.setIndex(i),
|
|
@@ -2091,10 +2298,9 @@ window.__ModuleLoader__.load({
|
|
|
2091
2298
|
react.useEffect(() => {
|
|
2092
2299
|
const engine = engineRef.current;
|
|
2093
2300
|
if (!engine || !ready || !engine.isMounted()) return;
|
|
2094
|
-
engine.setWarpFrom(warpFrom);
|
|
2095
2301
|
engine.setCount(snap.levels.length);
|
|
2096
2302
|
if (!engine.isDragging()) engine.setIndex(snap.index, true);
|
|
2097
|
-
}, [snap.levels.length
|
|
2303
|
+
}, [snap.levels.length]);
|
|
2098
2304
|
|
|
2099
2305
|
/* Dismiss on an outside press or Escape. */
|
|
2100
2306
|
react.useEffect(() => {
|
|
@@ -2139,6 +2345,7 @@ window.__ModuleLoader__.load({
|
|
|
2139
2345
|
"aria-expanded": snap.open ? "true" : "false",
|
|
2140
2346
|
"data-open": snap.open ? "1" : "0",
|
|
2141
2347
|
"data-locked": locked ? "1" : "0",
|
|
2348
|
+
"data-halo": snap.halo ? "1" : "0",
|
|
2142
2349
|
disabled: locked,
|
|
2143
2350
|
style: { "--es-hue": String(hue), "--es-e": snap.t.toFixed(3) },
|
|
2144
2351
|
onClick: () => props.toggle()
|
|
@@ -2280,6 +2487,25 @@ window.__ModuleLoader__.load({
|
|
|
2280
2487
|
)
|
|
2281
2488
|
)
|
|
2282
2489
|
),
|
|
2490
|
+
h(
|
|
2491
|
+
"div",
|
|
2492
|
+
{ className: "es-opt", key: "opt" },
|
|
2493
|
+
h("span", { key: "l" }, props.t("haloLabel")),
|
|
2494
|
+
h(
|
|
2495
|
+
"button",
|
|
2496
|
+
{
|
|
2497
|
+
type: "button",
|
|
2498
|
+
role: "switch",
|
|
2499
|
+
className: "es-switch",
|
|
2500
|
+
title: props.t("haloTitle"),
|
|
2501
|
+
"aria-label": props.t("haloLabel"),
|
|
2502
|
+
"aria-checked": snap.halo ? "true" : "false",
|
|
2503
|
+
"data-on": snap.halo ? "1" : "0",
|
|
2504
|
+
onClick: () => props.toggleHalo()
|
|
2505
|
+
},
|
|
2506
|
+
h("span", { className: "es-switch-knob", key: "k" })
|
|
2507
|
+
)
|
|
2508
|
+
),
|
|
2283
2509
|
h("div", { className: "es-foot", key: "foot", "data-err": snap.error ? "1" : "0", "data-note": footNote ? "1" : "0" }, footText)
|
|
2284
2510
|
),
|
|
2285
2511
|
h("canvas", { className: "es-fx", key: "fx", ref: fxRef }),
|
package/package.json
CHANGED
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@lolannn/dsh-effort-slider",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Replaces the dsh web GUI composer model selector: one control for the model list and its reasoning effort, drawn as a starfield popup with a glowing particle rail, iridescent four-point stars, ribbon waves and a galaxy collapse at the top level.",
|
|
5
|
-
"icon": "icon.svg",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"main": "index.js",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": "./index.js",
|
|
10
|
-
"./client": "./client.js",
|
|
11
|
-
"./package.json": "./package.json"
|
|
12
|
-
},
|
|
13
|
-
"scripts": {
|
|
14
|
-
"check": "node --check client.js && node --check index.js",
|
|
15
|
-
"test": "node test/run.cjs",
|
|
16
|
-
"prepublishOnly": "npm test"
|
|
17
|
-
},
|
|
18
|
-
"repository": {
|
|
19
|
-
"type": "git",
|
|
20
|
-
"url": "git+https://github.com/suyunxing/dsh-effort-slider.git"
|
|
21
|
-
},
|
|
22
|
-
"homepage": "https://github.com/suyunxing/dsh-effort-slider#readme",
|
|
23
|
-
"bugs": {
|
|
24
|
-
"url": "https://github.com/suyunxing/dsh-effort-slider/issues"
|
|
25
|
-
},
|
|
26
|
-
"author": "suyunxing",
|
|
27
|
-
"dsh": {
|
|
28
|
-
"bundle": {
|
|
29
|
-
"patch": "./cordis.patch.yml"
|
|
30
|
-
},
|
|
31
|
-
"client": {
|
|
32
|
-
"inject": [
|
|
33
|
-
"@deepseek-ai/dsh-client-connection",
|
|
34
|
-
"@deepseek-ai/dsh-client-ui-renderer",
|
|
35
|
-
"@deepseek-ai/dsh-api-remotes",
|
|
36
|
-
"@deepseek-ai/dsh-api-session-controller"
|
|
37
|
-
],
|
|
38
|
-
"platform": "web",
|
|
39
|
-
"immediately": true
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
|
-
"files": [
|
|
43
|
-
"index.js",
|
|
44
|
-
"client.js",
|
|
45
|
-
"icon.svg",
|
|
46
|
-
"cordis.patch.yml",
|
|
47
|
-
"README.md"
|
|
48
|
-
],
|
|
49
|
-
"license": "MIT",
|
|
50
|
-
"keywords": [
|
|
51
|
-
"dsh",
|
|
52
|
-
"dsh-plugin",
|
|
53
|
-
"reasoning-effort",
|
|
54
|
-
"model-selector",
|
|
55
|
-
"composer",
|
|
56
|
-
"slider",
|
|
57
|
-
"canvas",
|
|
58
|
-
"starfield"
|
|
59
|
-
]
|
|
60
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@lolannn/dsh-effort-slider",
|
|
3
|
+
"version": "0.2.7",
|
|
4
|
+
"description": "Replaces the dsh web GUI composer model selector: one control for the model list and its reasoning effort, drawn as a starfield popup with a glowing particle rail, iridescent four-point stars, ribbon waves and a galaxy collapse at the top level.",
|
|
5
|
+
"icon": "icon.svg",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "index.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./index.js",
|
|
10
|
+
"./client": "./client.js",
|
|
11
|
+
"./package.json": "./package.json"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"check": "node --check client.js && node --check index.js",
|
|
15
|
+
"test": "node test/run.cjs",
|
|
16
|
+
"prepublishOnly": "npm test"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/suyunxing/dsh-effort-slider.git"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://github.com/suyunxing/dsh-effort-slider#readme",
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/suyunxing/dsh-effort-slider/issues"
|
|
25
|
+
},
|
|
26
|
+
"author": "suyunxing",
|
|
27
|
+
"dsh": {
|
|
28
|
+
"bundle": {
|
|
29
|
+
"patch": "./cordis.patch.yml"
|
|
30
|
+
},
|
|
31
|
+
"client": {
|
|
32
|
+
"inject": [
|
|
33
|
+
"@deepseek-ai/dsh-client-connection",
|
|
34
|
+
"@deepseek-ai/dsh-client-ui-renderer",
|
|
35
|
+
"@deepseek-ai/dsh-api-remotes",
|
|
36
|
+
"@deepseek-ai/dsh-api-session-controller"
|
|
37
|
+
],
|
|
38
|
+
"platform": "web",
|
|
39
|
+
"immediately": true
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"index.js",
|
|
44
|
+
"client.js",
|
|
45
|
+
"icon.svg",
|
|
46
|
+
"cordis.patch.yml",
|
|
47
|
+
"README.md"
|
|
48
|
+
],
|
|
49
|
+
"license": "MIT",
|
|
50
|
+
"keywords": [
|
|
51
|
+
"dsh",
|
|
52
|
+
"dsh-plugin",
|
|
53
|
+
"reasoning-effort",
|
|
54
|
+
"model-selector",
|
|
55
|
+
"composer",
|
|
56
|
+
"slider",
|
|
57
|
+
"canvas",
|
|
58
|
+
"starfield"
|
|
59
|
+
]
|
|
60
|
+
}
|