privateboard 0.1.9 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +3732 -2160
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/public/agent-overlay.js +59 -36
- package/public/agent-profile.css +392 -100
- package/public/agent-profile.js +551 -59
- package/public/app.js +1341 -681
- package/public/i18n.js +1990 -0
- package/public/index.html +251 -94
- package/public/keys-store.js +63 -0
- package/public/new-agent.css +60 -0
- package/public/new-agent.js +121 -53
- package/public/room-settings.js +2 -1
- package/public/user-settings.css +68 -0
- package/public/user-settings.js +241 -87
- package/public/voice-replay.css +607 -0
- package/public/voice-replay.js +533 -0
package/public/agent-profile.css
CHANGED
|
@@ -762,8 +762,8 @@
|
|
|
762
762
|
GAME-CARD layout (Director console)
|
|
763
763
|
─────────────────────────────────────────────
|
|
764
764
|
New chrome inspired by an RPG character sheet:
|
|
765
|
-
header tabs · left portrait · right
|
|
766
|
-
(Badges / Intel / Skills
|
|
765
|
+
header tabs · left portrait · right column
|
|
766
|
+
(Badges / Intel / Skills) · roster row
|
|
767
767
|
· single bright lime CTA. Boardroom palette
|
|
768
768
|
(lime on charcoal), no shadows, hairline lines.
|
|
769
769
|
═════════════════════════════════════════════ */
|
|
@@ -1246,7 +1246,10 @@
|
|
|
1246
1246
|
flex-direction: column;
|
|
1247
1247
|
gap: 4px;
|
|
1248
1248
|
padding-top: 4px;
|
|
1249
|
-
border-top
|
|
1249
|
+
/* No border-top here · the explicit `.dream-divider` element
|
|
1250
|
+
placed BEFORE the caption already paints the dashed hairline.
|
|
1251
|
+
Keeping a border on both produced two stacked rules under the
|
|
1252
|
+
running-state lanes. */
|
|
1250
1253
|
min-height: 38px;
|
|
1251
1254
|
}
|
|
1252
1255
|
.dream-caption-kicker {
|
|
@@ -2215,103 +2218,6 @@
|
|
|
2215
2218
|
line-height: 1;
|
|
2216
2219
|
}
|
|
2217
2220
|
|
|
2218
|
-
/* Equipment · 6 inventory slots */
|
|
2219
|
-
.ap-equipment-grid {
|
|
2220
|
-
display: grid;
|
|
2221
|
-
grid-template-columns: repeat(6, 1fr);
|
|
2222
|
-
gap: 6px;
|
|
2223
|
-
}
|
|
2224
|
-
/* Compact variant for the right rail · 3 cols, smaller slots. */
|
|
2225
|
-
.ap-equipment-grid.compact {
|
|
2226
|
-
grid-template-columns: repeat(3, 1fr);
|
|
2227
|
-
gap: 8px;
|
|
2228
|
-
}
|
|
2229
|
-
.ap-slot {
|
|
2230
|
-
aspect-ratio: 1;
|
|
2231
|
-
border: 0.5px solid var(--line);
|
|
2232
|
-
background: transparent;
|
|
2233
|
-
display: flex;
|
|
2234
|
-
align-items: center;
|
|
2235
|
-
justify-content: center;
|
|
2236
|
-
font-family: var(--mono);
|
|
2237
|
-
color: var(--text-faint);
|
|
2238
|
-
font-size: 14px;
|
|
2239
|
-
cursor: default;
|
|
2240
|
-
position: relative;
|
|
2241
|
-
opacity: 0.7;
|
|
2242
|
-
transition: border-color 0.12s, background 0.12s, opacity 0.12s, color 0.12s;
|
|
2243
|
-
}
|
|
2244
|
-
.ap-slot:hover {
|
|
2245
|
-
border-color: var(--line-bright);
|
|
2246
|
-
background: var(--panel);
|
|
2247
|
-
opacity: 1;
|
|
2248
|
-
color: var(--text-soft);
|
|
2249
|
-
}
|
|
2250
|
-
.ap-slot.filled {
|
|
2251
|
-
border-color: var(--line-bright);
|
|
2252
|
-
color: var(--text);
|
|
2253
|
-
background: var(--panel-3);
|
|
2254
|
-
opacity: 1;
|
|
2255
|
-
}
|
|
2256
|
-
.ap-slot.filled:hover { border-color: var(--text-faint); }
|
|
2257
|
-
.ap-slot .ap-slot-ext {
|
|
2258
|
-
position: absolute;
|
|
2259
|
-
bottom: 2px;
|
|
2260
|
-
left: 50%;
|
|
2261
|
-
transform: translateX(-50%);
|
|
2262
|
-
font-size: 7.5px;
|
|
2263
|
-
letter-spacing: 0.1em;
|
|
2264
|
-
color: var(--text-faint);
|
|
2265
|
-
text-transform: uppercase;
|
|
2266
|
-
}
|
|
2267
|
-
|
|
2268
|
-
/* Coming-soon block · used by sections (e.g., Equipment) whose feature
|
|
2269
|
-
isn't shipped yet. Centered glyph + title + body copy + status tag,
|
|
2270
|
-
so the section reads as deliberately reserved space rather than an
|
|
2271
|
-
empty grid. */
|
|
2272
|
-
.ap-coming-soon {
|
|
2273
|
-
display: flex;
|
|
2274
|
-
flex-direction: column;
|
|
2275
|
-
align-items: center;
|
|
2276
|
-
text-align: center;
|
|
2277
|
-
gap: 8px;
|
|
2278
|
-
padding: 6px 4px 4px;
|
|
2279
|
-
}
|
|
2280
|
-
.ap-coming-soon-mark {
|
|
2281
|
-
font-family: var(--mono);
|
|
2282
|
-
font-size: 18px;
|
|
2283
|
-
line-height: 1;
|
|
2284
|
-
color: var(--lime-dim);
|
|
2285
|
-
letter-spacing: 0;
|
|
2286
|
-
}
|
|
2287
|
-
.ap-coming-soon-title {
|
|
2288
|
-
font-family: var(--font-human);
|
|
2289
|
-
font-size: 13.5px;
|
|
2290
|
-
font-weight: 700;
|
|
2291
|
-
letter-spacing: -0.005em;
|
|
2292
|
-
color: var(--text);
|
|
2293
|
-
line-height: 1.2;
|
|
2294
|
-
}
|
|
2295
|
-
.ap-coming-soon-body {
|
|
2296
|
-
font-family: var(--font-human);
|
|
2297
|
-
font-size: 12px;
|
|
2298
|
-
line-height: 1.55;
|
|
2299
|
-
color: var(--text-soft);
|
|
2300
|
-
letter-spacing: -0.003em;
|
|
2301
|
-
margin: 0;
|
|
2302
|
-
max-width: 280px;
|
|
2303
|
-
}
|
|
2304
|
-
.ap-coming-soon-tag {
|
|
2305
|
-
margin-top: 4px;
|
|
2306
|
-
font-family: var(--mono);
|
|
2307
|
-
font-size: 9px;
|
|
2308
|
-
font-weight: 700;
|
|
2309
|
-
letter-spacing: 0.16em;
|
|
2310
|
-
text-transform: uppercase;
|
|
2311
|
-
color: var(--lime);
|
|
2312
|
-
border: 0.5px solid var(--lime-dim);
|
|
2313
|
-
padding: 3px 9px;
|
|
2314
|
-
}
|
|
2315
2221
|
|
|
2316
2222
|
/* Roster · bottom-spanning row showing recent room avatars */
|
|
2317
2223
|
.ap-roster-portraits {
|
|
@@ -2517,6 +2423,40 @@
|
|
|
2517
2423
|
.ap-model-picker::-webkit-scrollbar { width: 6px; }
|
|
2518
2424
|
.ap-model-picker::-webkit-scrollbar-thumb { background: var(--line-strong); }
|
|
2519
2425
|
|
|
2426
|
+
/* Loading row · shown inside the voice picker while /api/voices
|
|
2427
|
+
round-trips. The animated dot trio matches the play button's
|
|
2428
|
+
loading register so both surfaces share the same vocabulary
|
|
2429
|
+
for "working on it." Without the animation, users read the
|
|
2430
|
+
static label as "frozen / no response." */
|
|
2431
|
+
.ap-model-picker-loading {
|
|
2432
|
+
display: flex;
|
|
2433
|
+
align-items: center;
|
|
2434
|
+
justify-content: center;
|
|
2435
|
+
gap: 10px;
|
|
2436
|
+
padding: 22px 12px;
|
|
2437
|
+
font-family: var(--mono);
|
|
2438
|
+
font-size: 10.5px;
|
|
2439
|
+
letter-spacing: 0.10em;
|
|
2440
|
+
text-transform: uppercase;
|
|
2441
|
+
color: var(--text-soft);
|
|
2442
|
+
}
|
|
2443
|
+
.ap-model-picker-loading .ap-loading-dots {
|
|
2444
|
+
display: inline-flex;
|
|
2445
|
+
align-items: center;
|
|
2446
|
+
gap: 3px;
|
|
2447
|
+
flex-shrink: 0;
|
|
2448
|
+
}
|
|
2449
|
+
.ap-model-picker-loading .ap-loading-dots i {
|
|
2450
|
+
width: 4px;
|
|
2451
|
+
height: 4px;
|
|
2452
|
+
border-radius: 50%;
|
|
2453
|
+
background: var(--lime);
|
|
2454
|
+
display: inline-block;
|
|
2455
|
+
animation: ap-voice-preview-dot 1s ease-in-out infinite;
|
|
2456
|
+
}
|
|
2457
|
+
.ap-model-picker-loading .ap-loading-dots i:nth-child(2) { animation-delay: 0.15s; }
|
|
2458
|
+
.ap-model-picker-loading .ap-loading-dots i:nth-child(3) { animation-delay: 0.30s; }
|
|
2459
|
+
|
|
2520
2460
|
/* Provider section header · matches .cmp-dd-group · mono / faint /
|
|
2521
2461
|
uppercase, hairline divider above (suppressed on the first one). */
|
|
2522
2462
|
.ap-model-group {
|
|
@@ -3754,3 +3694,355 @@
|
|
|
3754
3694
|
color: var(--bg, #0A0A0A);
|
|
3755
3695
|
}
|
|
3756
3696
|
.ap-skill-info-configure-mark { font-size: 12px; }
|
|
3697
|
+
|
|
3698
|
+
/* ── Voice configuration panel ───────────────────────────── */
|
|
3699
|
+
/* The wrapping `.ap-voice-config` no longer needs spacing of its
|
|
3700
|
+
own — the parent `.ap-block-body` already handles padding now
|
|
3701
|
+
that Voice Setup is its own section (was nested inside Track
|
|
3702
|
+
Record before, where the inner offset disambiguated it from
|
|
3703
|
+
the model row above). */
|
|
3704
|
+
.ap-voice-picker-row {
|
|
3705
|
+
display: flex;
|
|
3706
|
+
align-items: center;
|
|
3707
|
+
gap: 8px;
|
|
3708
|
+
}
|
|
3709
|
+
.ap-voice-picker-row .ap-model-trigger { flex: 1; }
|
|
3710
|
+
/* Preview button · sits next to the voice picker trigger.
|
|
3711
|
+
Matches the global hairline register (`.ap-model-trigger` /
|
|
3712
|
+
`.ap-block-h-action`) — same vertical metrics so it baseline-
|
|
3713
|
+
aligns with the dropdown next to it, 0.5px border, transparent
|
|
3714
|
+
background with panel-on-hover. Lime accent on the glyph
|
|
3715
|
+
signals "audio action" without making the button shout. Loading
|
|
3716
|
+
overlays three pulsing dots ON TOP of the glyph (same grid cell
|
|
3717
|
+
via absolute positioning) so the button width is invariant
|
|
3718
|
+
across states — no horizontal jump on click. */
|
|
3719
|
+
.ap-voice-preview-btn {
|
|
3720
|
+
flex-shrink: 0;
|
|
3721
|
+
position: relative;
|
|
3722
|
+
padding: 6px 12px;
|
|
3723
|
+
display: inline-flex;
|
|
3724
|
+
align-items: center;
|
|
3725
|
+
justify-content: center;
|
|
3726
|
+
background: var(--bg);
|
|
3727
|
+
border: 0.5px solid var(--line-bright);
|
|
3728
|
+
color: var(--lime);
|
|
3729
|
+
font-family: var(--mono);
|
|
3730
|
+
font-size: 12px;
|
|
3731
|
+
line-height: 1.3;
|
|
3732
|
+
cursor: pointer;
|
|
3733
|
+
transition: border-color 0.1s, background 0.1s, color 0.1s;
|
|
3734
|
+
}
|
|
3735
|
+
.ap-voice-preview-btn:hover {
|
|
3736
|
+
background: var(--panel);
|
|
3737
|
+
border-color: var(--text-faint);
|
|
3738
|
+
}
|
|
3739
|
+
.ap-voice-preview-btn:disabled {
|
|
3740
|
+
cursor: wait;
|
|
3741
|
+
color: var(--text-faint);
|
|
3742
|
+
}
|
|
3743
|
+
.ap-voice-preview-btn .ap-voice-preview-glyph {
|
|
3744
|
+
display: inline-flex;
|
|
3745
|
+
align-items: center;
|
|
3746
|
+
justify-content: center;
|
|
3747
|
+
min-width: 18px;
|
|
3748
|
+
visibility: visible;
|
|
3749
|
+
}
|
|
3750
|
+
.ap-voice-preview-btn .ap-voice-preview-dots {
|
|
3751
|
+
position: absolute;
|
|
3752
|
+
inset: 0;
|
|
3753
|
+
display: inline-flex;
|
|
3754
|
+
align-items: center;
|
|
3755
|
+
justify-content: center;
|
|
3756
|
+
gap: 3px;
|
|
3757
|
+
visibility: hidden;
|
|
3758
|
+
pointer-events: none;
|
|
3759
|
+
}
|
|
3760
|
+
.ap-voice-preview-btn.is-loading .ap-voice-preview-glyph { visibility: hidden; }
|
|
3761
|
+
.ap-voice-preview-btn.is-loading .ap-voice-preview-dots { visibility: visible; }
|
|
3762
|
+
.ap-voice-preview-btn .ap-voice-preview-dots i {
|
|
3763
|
+
width: 4px;
|
|
3764
|
+
height: 4px;
|
|
3765
|
+
border-radius: 50%;
|
|
3766
|
+
background: var(--lime);
|
|
3767
|
+
display: inline-block;
|
|
3768
|
+
animation: ap-voice-preview-dot 1s ease-in-out infinite;
|
|
3769
|
+
}
|
|
3770
|
+
.ap-voice-preview-btn .ap-voice-preview-dots i:nth-child(2) { animation-delay: 0.15s; }
|
|
3771
|
+
.ap-voice-preview-btn .ap-voice-preview-dots i:nth-child(3) { animation-delay: 0.30s; }
|
|
3772
|
+
@keyframes ap-voice-preview-dot {
|
|
3773
|
+
0%, 80%, 100% { opacity: 0.25; transform: scale(0.8); }
|
|
3774
|
+
40% { opacity: 1; transform: scale(1); }
|
|
3775
|
+
}
|
|
3776
|
+
|
|
3777
|
+
/* Legacy `.ap-voice-sliders` / `.ap-voice-slider` were a left-label
|
|
3778
|
+
+ flex-row layout that read cramped at the agent profile's column
|
|
3779
|
+
width. Replaced by `.ap-voice-tune*` below. */
|
|
3780
|
+
/* Emotion row · sits directly under the voice picker outside the
|
|
3781
|
+
advanced section. The trigger fills the row's width and the
|
|
3782
|
+
hint caption sits below in the small mono-italic register
|
|
3783
|
+
used for inline help across the profile (no left-side label —
|
|
3784
|
+
the picker's selected option already names the choice). */
|
|
3785
|
+
.ap-voice-emotion-row {
|
|
3786
|
+
display: flex;
|
|
3787
|
+
flex-direction: column;
|
|
3788
|
+
gap: 6px;
|
|
3789
|
+
margin-top: 8px;
|
|
3790
|
+
}
|
|
3791
|
+
.ap-voice-emotion-row .ap-voice-emotion-trigger {
|
|
3792
|
+
width: 100%;
|
|
3793
|
+
}
|
|
3794
|
+
.ap-voice-emotion-hint {
|
|
3795
|
+
font-family: var(--font-human);
|
|
3796
|
+
font-size: 11px;
|
|
3797
|
+
font-style: italic;
|
|
3798
|
+
line-height: 1.45;
|
|
3799
|
+
color: var(--text-faint);
|
|
3800
|
+
letter-spacing: -0.003em;
|
|
3801
|
+
padding: 0 2px;
|
|
3802
|
+
}
|
|
3803
|
+
/* Locked-state card · shown inside Voice Setup when neither
|
|
3804
|
+
MiniMax nor ElevenLabs is configured. The sound-wave glyph
|
|
3805
|
+
carries the visual interest by itself · no gradient backdrop
|
|
3806
|
+
or framed container — the parent `.ap-block-body` already
|
|
3807
|
+
gives it scope, and the `.ap-voice-locked` element just
|
|
3808
|
+
centers its contents and gives them breathing room. */
|
|
3809
|
+
.ap-voice-locked {
|
|
3810
|
+
display: flex;
|
|
3811
|
+
flex-direction: column;
|
|
3812
|
+
align-items: center;
|
|
3813
|
+
text-align: center;
|
|
3814
|
+
gap: 14px;
|
|
3815
|
+
padding: 16px 12px 8px;
|
|
3816
|
+
}
|
|
3817
|
+
.ap-voice-locked-glyph {
|
|
3818
|
+
display: inline-flex;
|
|
3819
|
+
align-items: flex-end;
|
|
3820
|
+
justify-content: center;
|
|
3821
|
+
gap: 4px;
|
|
3822
|
+
height: 36px;
|
|
3823
|
+
padding-bottom: 2px;
|
|
3824
|
+
}
|
|
3825
|
+
.ap-voice-locked-glyph i {
|
|
3826
|
+
width: 4px;
|
|
3827
|
+
background: var(--lime);
|
|
3828
|
+
display: inline-block;
|
|
3829
|
+
animation: ap-voice-locked-bar 1.4s ease-in-out infinite;
|
|
3830
|
+
opacity: 0.55;
|
|
3831
|
+
}
|
|
3832
|
+
.ap-voice-locked-glyph i:nth-child(1) { height: 10px; animation-delay: 0s; }
|
|
3833
|
+
.ap-voice-locked-glyph i:nth-child(2) { height: 18px; animation-delay: 0.12s; }
|
|
3834
|
+
.ap-voice-locked-glyph i:nth-child(3) { height: 26px; animation-delay: 0.24s; }
|
|
3835
|
+
.ap-voice-locked-glyph i:nth-child(4) { height: 18px; animation-delay: 0.36s; }
|
|
3836
|
+
.ap-voice-locked-glyph i:nth-child(5) { height: 10px; animation-delay: 0.48s; }
|
|
3837
|
+
@keyframes ap-voice-locked-bar {
|
|
3838
|
+
0%, 100% { transform: scaleY(0.45); opacity: 0.35; }
|
|
3839
|
+
50% { transform: scaleY(1); opacity: 1; }
|
|
3840
|
+
}
|
|
3841
|
+
/* Small inline caption · was a bold display title; now reads as a
|
|
3842
|
+
secondary explainer matching the rest of the profile's faint
|
|
3843
|
+
helper-text register (.ap-voice-emotion-hint and similar). */
|
|
3844
|
+
.ap-voice-locked-title {
|
|
3845
|
+
font-family: var(--font-human);
|
|
3846
|
+
font-size: 12px;
|
|
3847
|
+
line-height: 1.5;
|
|
3848
|
+
letter-spacing: -0.003em;
|
|
3849
|
+
color: var(--text-soft);
|
|
3850
|
+
max-width: 320px;
|
|
3851
|
+
margin: 0;
|
|
3852
|
+
}
|
|
3853
|
+
.ap-voice-locked-cta {
|
|
3854
|
+
font-family: var(--mono);
|
|
3855
|
+
font-size: 10px;
|
|
3856
|
+
font-weight: 700;
|
|
3857
|
+
letter-spacing: 0.16em;
|
|
3858
|
+
text-transform: uppercase;
|
|
3859
|
+
color: var(--lime);
|
|
3860
|
+
background: transparent;
|
|
3861
|
+
border: 0.5px solid var(--lime);
|
|
3862
|
+
padding: 8px 16px;
|
|
3863
|
+
cursor: pointer;
|
|
3864
|
+
transition: background 0.12s, color 0.12s;
|
|
3865
|
+
display: inline-flex;
|
|
3866
|
+
align-items: center;
|
|
3867
|
+
gap: 8px;
|
|
3868
|
+
margin-top: 4px;
|
|
3869
|
+
}
|
|
3870
|
+
.ap-voice-locked-cta:hover {
|
|
3871
|
+
background: var(--lime);
|
|
3872
|
+
color: var(--bg);
|
|
3873
|
+
}
|
|
3874
|
+
.ap-voice-locked-cta-arrow {
|
|
3875
|
+
display: inline-block;
|
|
3876
|
+
transition: transform 0.12s;
|
|
3877
|
+
}
|
|
3878
|
+
.ap-voice-locked-cta:hover .ap-voice-locked-cta-arrow { transform: translateX(2px); }
|
|
3879
|
+
|
|
3880
|
+
/* Voice tune row · header (label left, value right) on top + a
|
|
3881
|
+
custom-styled range slider beneath. Each slider's track shows a
|
|
3882
|
+
lime "fill" segment from origin to thumb (or from neutral to
|
|
3883
|
+
thumb for centered ranges) so the row reads as "lit up" instead
|
|
3884
|
+
of just a thumb floating on a hairline. Fill positions are
|
|
3885
|
+
driven by `--fill-lo` / `--fill-hi` CSS vars, set inline by the
|
|
3886
|
+
render code and updated by the input handler. */
|
|
3887
|
+
.ap-voice-tune-grid {
|
|
3888
|
+
display: flex;
|
|
3889
|
+
flex-direction: column;
|
|
3890
|
+
gap: 10px;
|
|
3891
|
+
margin-top: 10px;
|
|
3892
|
+
}
|
|
3893
|
+
.ap-voice-tune {
|
|
3894
|
+
display: flex;
|
|
3895
|
+
flex-direction: column;
|
|
3896
|
+
gap: 2px;
|
|
3897
|
+
position: relative;
|
|
3898
|
+
}
|
|
3899
|
+
.ap-voice-tune-head {
|
|
3900
|
+
display: flex;
|
|
3901
|
+
align-items: baseline;
|
|
3902
|
+
justify-content: space-between;
|
|
3903
|
+
gap: 10px;
|
|
3904
|
+
min-height: 14px;
|
|
3905
|
+
}
|
|
3906
|
+
.ap-voice-tune-label {
|
|
3907
|
+
font-family: var(--mono);
|
|
3908
|
+
font-size: 9px;
|
|
3909
|
+
font-weight: 700;
|
|
3910
|
+
letter-spacing: 0.18em;
|
|
3911
|
+
text-transform: uppercase;
|
|
3912
|
+
color: var(--text-faint);
|
|
3913
|
+
}
|
|
3914
|
+
.ap-voice-tune-value {
|
|
3915
|
+
font-family: var(--mono);
|
|
3916
|
+
font-size: 11px;
|
|
3917
|
+
font-weight: 700;
|
|
3918
|
+
letter-spacing: 0.02em;
|
|
3919
|
+
color: var(--lime);
|
|
3920
|
+
font-variant-numeric: tabular-nums;
|
|
3921
|
+
line-height: 1;
|
|
3922
|
+
}
|
|
3923
|
+
|
|
3924
|
+
/* Custom slider chrome · 2px hairline track with a lime fill, 9px
|
|
3925
|
+
square thumb. The fill gradient uses `--fill-lo` / `--fill-hi`
|
|
3926
|
+
so the same rule covers both non-centered (speed: lo=0, hi=value)
|
|
3927
|
+
and centered (pitch / modify-*: lo/hi flank zero) sliders.
|
|
3928
|
+
Vendor prefixes carry the same shape so Chrome and Firefox
|
|
3929
|
+
render identically. */
|
|
3930
|
+
.ap-voice-tune-range {
|
|
3931
|
+
appearance: none;
|
|
3932
|
+
-webkit-appearance: none;
|
|
3933
|
+
width: 100%;
|
|
3934
|
+
height: 14px;
|
|
3935
|
+
background: transparent;
|
|
3936
|
+
cursor: pointer;
|
|
3937
|
+
margin: 0;
|
|
3938
|
+
padding: 0;
|
|
3939
|
+
--fill-lo: 0%;
|
|
3940
|
+
--fill-hi: 0%;
|
|
3941
|
+
}
|
|
3942
|
+
.ap-voice-tune-range:focus { outline: none; }
|
|
3943
|
+
.ap-voice-tune-range::-webkit-slider-runnable-track {
|
|
3944
|
+
height: 2px;
|
|
3945
|
+
border: none;
|
|
3946
|
+
background:
|
|
3947
|
+
linear-gradient(
|
|
3948
|
+
to right,
|
|
3949
|
+
var(--line-bright) 0%,
|
|
3950
|
+
var(--line-bright) var(--fill-lo),
|
|
3951
|
+
var(--lime) var(--fill-lo),
|
|
3952
|
+
var(--lime) var(--fill-hi),
|
|
3953
|
+
var(--line-bright) var(--fill-hi),
|
|
3954
|
+
var(--line-bright) 100%
|
|
3955
|
+
);
|
|
3956
|
+
}
|
|
3957
|
+
.ap-voice-tune-range::-moz-range-track {
|
|
3958
|
+
height: 2px;
|
|
3959
|
+
border: none;
|
|
3960
|
+
background:
|
|
3961
|
+
linear-gradient(
|
|
3962
|
+
to right,
|
|
3963
|
+
var(--line-bright) 0%,
|
|
3964
|
+
var(--line-bright) var(--fill-lo),
|
|
3965
|
+
var(--lime) var(--fill-lo),
|
|
3966
|
+
var(--lime) var(--fill-hi),
|
|
3967
|
+
var(--line-bright) var(--fill-hi),
|
|
3968
|
+
var(--line-bright) 100%
|
|
3969
|
+
);
|
|
3970
|
+
}
|
|
3971
|
+
.ap-voice-tune-range::-webkit-slider-thumb {
|
|
3972
|
+
appearance: none;
|
|
3973
|
+
-webkit-appearance: none;
|
|
3974
|
+
width: 9px;
|
|
3975
|
+
height: 9px;
|
|
3976
|
+
background: var(--lime);
|
|
3977
|
+
border: 0;
|
|
3978
|
+
border-radius: 0;
|
|
3979
|
+
margin-top: -3.5px;
|
|
3980
|
+
cursor: grab;
|
|
3981
|
+
transition: transform 0.1s, background 0.12s;
|
|
3982
|
+
}
|
|
3983
|
+
.ap-voice-tune-range::-moz-range-thumb {
|
|
3984
|
+
width: 9px;
|
|
3985
|
+
height: 9px;
|
|
3986
|
+
background: var(--lime);
|
|
3987
|
+
border: 0;
|
|
3988
|
+
border-radius: 0;
|
|
3989
|
+
cursor: grab;
|
|
3990
|
+
transition: transform 0.1s, background 0.12s;
|
|
3991
|
+
}
|
|
3992
|
+
.ap-voice-tune-range:active::-webkit-slider-thumb { cursor: grabbing; transform: scale(1.25); }
|
|
3993
|
+
.ap-voice-tune-range:active::-moz-range-thumb { cursor: grabbing; transform: scale(1.25); }
|
|
3994
|
+
|
|
3995
|
+
/* Centered ranges (pitch / modify-*) get a faint vertical tick at
|
|
3996
|
+
the 50% mark to anchor the neutral / zero position. Sits behind
|
|
3997
|
+
the lime thumb (z-index 0) so the thumb stays foreground when
|
|
3998
|
+
the value happens to land on zero. */
|
|
3999
|
+
.ap-voice-tune-centered::before {
|
|
4000
|
+
content: "";
|
|
4001
|
+
position: absolute;
|
|
4002
|
+
bottom: 6px;
|
|
4003
|
+
left: 50%;
|
|
4004
|
+
width: 1px;
|
|
4005
|
+
height: 5px;
|
|
4006
|
+
background: var(--line-strong);
|
|
4007
|
+
pointer-events: none;
|
|
4008
|
+
z-index: 0;
|
|
4009
|
+
}
|
|
4010
|
+
.ap-voice-advanced {
|
|
4011
|
+
margin-top: 10px;
|
|
4012
|
+
border-top: 1px solid var(--line, #222);
|
|
4013
|
+
padding-top: 8px;
|
|
4014
|
+
}
|
|
4015
|
+
/* Header row · label on the left, custom expand/collapse indicator
|
|
4016
|
+
on the right. Removes the browser's default ▶ disclosure triangle
|
|
4017
|
+
on the left so the row reads as a clean section header rather
|
|
4018
|
+
than a list item. The right-edge `+ / −` glyph swaps when
|
|
4019
|
+
[open] is set — same hairline mono register the rest of the
|
|
4020
|
+
profile uses for header actions. */
|
|
4021
|
+
.ap-voice-advanced summary {
|
|
4022
|
+
display: flex;
|
|
4023
|
+
align-items: center;
|
|
4024
|
+
justify-content: space-between;
|
|
4025
|
+
gap: 12px;
|
|
4026
|
+
font-family: var(--mono, monospace);
|
|
4027
|
+
font-size: 10px;
|
|
4028
|
+
letter-spacing: 0.04em;
|
|
4029
|
+
text-transform: uppercase;
|
|
4030
|
+
color: var(--text-soft, #999);
|
|
4031
|
+
cursor: pointer;
|
|
4032
|
+
user-select: none;
|
|
4033
|
+
list-style: none;
|
|
4034
|
+
}
|
|
4035
|
+
.ap-voice-advanced summary::-webkit-details-marker { display: none; }
|
|
4036
|
+
.ap-voice-advanced summary::marker { display: none; content: ""; }
|
|
4037
|
+
.ap-voice-advanced summary::after {
|
|
4038
|
+
content: "+";
|
|
4039
|
+
font-family: var(--mono, monospace);
|
|
4040
|
+
font-size: 14px;
|
|
4041
|
+
font-weight: 400;
|
|
4042
|
+
line-height: 1;
|
|
4043
|
+
color: var(--text-faint);
|
|
4044
|
+
transition: color 0.12s;
|
|
4045
|
+
}
|
|
4046
|
+
.ap-voice-advanced[open] summary::after { content: "−"; }
|
|
4047
|
+
.ap-voice-advanced summary:hover { color: var(--text, #eee); }
|
|
4048
|
+
.ap-voice-advanced summary:hover::after { color: var(--text); }
|