diff --git a/src/tray_app/flyout_palettes.h b/src/tray_app/flyout_palettes.h index d99f838..8c7f84a 100644 --- a/src/tray_app/flyout_palettes.h +++ b/src/tray_app/flyout_palettes.h @@ -27,16 +27,16 @@ struct FlyoutPalette { inline constexpr int kPaletteCount = 4; inline const FlyoutPalette kPalettes[kPaletteCount] = { { "grey", 10, 8, 6, - { 0x000000, 0x121212, 0x191919, 0xf2f2f2, 0xd0d0d0, 0xb4b6ba, 0x18191a, 0x2d2d2d, 0xb0b0b0, 0x2fbfa5, 0x2fbfa5, 0x000000, 0x0b0b0b, 0x000000, 0x0a0a0a, 0x3d3d3d, 0x0e0e0e, 0x1c1c1c, 0xc8cad0, 0xa9ece0, 0x1f5650, 0x266560, 0x2fbfa5, 0x222222, 0xf2f2f2, 0x000000, 0x222222, + { 0x000000, 0x121212, 0x191919, 0xf2f2f2, 0xd0d0d0, 0xb4b6ba, 0x18191a, 0x2d2d2d, 0xb0b0b0, 0x2fbfa5, 0x2fbfa5, 0x000000, 0x0b0b0b, 0x000000, 0x0a0a0a, 0x3d3d3d, 0x0e0e0e, 0x1c1c1c, 0xc8cad0, 0x04201b, 0x2fbfa5, 0x45cbb3, 0x2fbfa5, 0x222222, 0xf2f2f2, 0x000000, 0x222222, 0.f, 0.55f, 0.4f, 0.62f, 0.f } }, { "ember", 10, 8, 6, - { 0x0c0908, 0x130e0b, 0x261d17, 0xf5eee7, 0xd8cbbf, 0xa39382, 0x1f1611, 0x1d1612, 0xb9a898, 0xe48a45, 0xe48a45, 0x000000, 0x130e0b, 0x0c0908, 0x130e0b, 0x3a2e25, 0x18120e, 0x251f1b, 0xb9a898, 0xe9a876, 0x704c31, 0x7e5433, 0xe48a45, 0x3a2c23, 0xe48a45, 0xff8a3d, 0x3a2c23, + { 0x0c0908, 0x130e0b, 0x261d17, 0xf5eee7, 0xd8cbbf, 0xa39382, 0x1f1611, 0x1d1612, 0xb9a898, 0xe48a45, 0xe48a45, 0x000000, 0x130e0b, 0x0c0908, 0x130e0b, 0x3a2e25, 0x18120e, 0x251f1b, 0xb9a898, 0x1c0f06, 0xe48a45, 0xe69658, 0xe48a45, 0x3a2c23, 0xe48a45, 0xff8a3d, 0x3a2c23, 0.f, 0.62f, 0.4f, 0.68f, 0.8f } }, { "ocean", 10, 8, 6, - { 0x0c1117, 0x121821, 0x1f2938, 0xf1f2f4, 0xc6cbd2, 0x9aa2ac, 0x19212e, 0x19222e, 0xb0b6bf, 0x5aaaf5, 0x5aaaf5, 0x000000, 0x121821, 0x0c1117, 0x121821, 0x2d3a4e, 0x161e29, 0x232b35, 0xb0b6bf, 0x87c0f5, 0x3d5d7d, 0x41678c, 0x5aaaf5, 0x263345, 0x5aaaf5, 0x5aaaf5, 0x263345, + { 0x0c1117, 0x121821, 0x1f2938, 0xf1f2f4, 0xc6cbd2, 0x9aa2ac, 0x19212e, 0x19222e, 0xb0b6bf, 0x5aaaf5, 0x5aaaf5, 0x000000, 0x121821, 0x0c1117, 0x121821, 0x2d3a4e, 0x161e29, 0x232b35, 0xb0b6bf, 0x061018, 0x5aaaf5, 0x6cb3f5, 0x5aaaf5, 0x263345, 0x5aaaf5, 0x5aaaf5, 0x263345, 0.f, 0.62f, 0.4f, 0.68f, 0.7f } }, { "hicon", 4, 3, 2, - { 0x000000, 0x000000, 0xbdbdbd, 0xffffff, 0xffffff, 0xe0e0e0, 0x9a9a9a, 0x262626, 0xffffff, 0xffffff, 0xffffff, 0x000000, 0x000000, 0x000000, 0x000000, 0x4a4a4a, 0x000000, 0x0f0f0f, 0xffffff, 0xffffff, 0x707070, 0x828282, 0xffffff, 0xbdbdbd, 0xffffff, 0x000000, 0xbdbdbd, + { 0x000000, 0x000000, 0xbdbdbd, 0xffffff, 0xffffff, 0xe0e0e0, 0x9a9a9a, 0x262626, 0xffffff, 0xffffff, 0xffffff, 0x000000, 0x000000, 0x000000, 0x000000, 0x4a4a4a, 0x000000, 0x0f0f0f, 0xffffff, 0x000000, 0xffffff, 0xffffff, 0xffffff, 0xbdbdbd, 0xffffff, 0x000000, 0xbdbdbd, 0.f, 0.62f, 0.4f, 0.408f, 0.f } }, }; diff --git a/src/tray_items.cpp b/src/tray_items.cpp index e812c0d..ca591b4 100644 --- a/src/tray_items.cpp +++ b/src/tray_items.cpp @@ -73,10 +73,11 @@ static std::string Join(const std::vector& items, bool onlyEnabled) { TrayLayout ParseTrayLayout(const IniValues& v) { TrayLayout l; const std::string* p = Find(v, "trayPerf"); - l.perf = p && Trim(*p) == "1"; + l.perf = !p || Trim(*p) == "1"; // shown by default (#329); an explicit 0 hides it l.sliders = ParseList(v, "traySliders", "traySliderOrder", EligibleSliders(), {"colorWarmPct", "colorDimPct"}, kMaxTraySliders); - l.toggles = ParseList(v, "trayToggles", "trayToggleOrder", EligibleToggles(), {}, 0); + // Every toggle item is on by default (#329): follow caret, follow focus, keep centred, engine. + l.toggles = ParseList(v, "trayToggles", "trayToggleOrder", EligibleToggles(), EligibleToggles(), 0); return l; } void WriteTrayLayout(const TrayLayout& l, IniValues& v) { diff --git a/src/version.h b/src/version.h index 2b9524c..7b876e2 100644 --- a/src/version.h +++ b/src/version.h @@ -4,7 +4,7 @@ #define WIND_VER_MAJOR 0 #define WIND_VER_MINOR 20 -#define WIND_VER_PATCH 1 +#define WIND_VER_PATCH 2 // String form for logs/snapshot/UI. Keep in sync with the numeric parts above. -#define WIND_VERSION_STR "0.20.1" +#define WIND_VERSION_STR "0.20.2" diff --git a/tests/test_flyout.cpp b/tests/test_flyout.cpp index 7aa9862..4b4c335 100644 --- a/tests/test_flyout.cpp +++ b/tests/test_flyout.cpp @@ -272,7 +272,7 @@ TEST_CASE("view: only enabled, known items appear, in layout order") { CHECK(v.toggles[1].key == "keepEdges"); CHECK(v.toggles[1].on); // no align keys = centred = Keep cursor centred ON CHECK(v.profile == L"Work"); - CHECK_FALSE(v.perf); + CHECK(v.perf); // no trayPerf key: shown by default (#329) } TEST_CASE("view: a legacy uiTheme=light ini changes nothing, the flyout is always dark (#324)") { diff --git a/tests/test_flyout_palettes.cpp b/tests/test_flyout_palettes.cpp index 736e45f..9ecbfe5 100644 --- a/tests/test_flyout_palettes.cpp +++ b/tests/test_flyout_palettes.cpp @@ -41,7 +41,8 @@ TEST_CASE("flyout palettes: Wind grey keeps the look the flyout already had") { const FlyoutPalette& g = kPalettes[0]; CHECK(g.dark.menu == 0x000000); CHECK(g.dark.fill == 0x2fbfa5); - CHECK(g.dark.on == 0x1f5650); + CHECK(g.dark.on == 0x2fbfa5); // solid teal ON segments with a near-black icon (#329) + CHECK(g.dark.onic == 0x04201b); CHECK(g.dark.tintA == 0.f); } @@ -62,12 +63,12 @@ TEST_CASE("flyout palettes: radii are sharp for hicon, soft elsewhere") { } } -TEST_CASE("flyout palettes: High contrast dark is white on black with grey on-segments") { +TEST_CASE("flyout palettes: High contrast dark is white on black, ON segments solid white with a black icon (#329)") { const PaletteMode& d = FindPalette("hicon").dark; CHECK(d.menu == 0x000000); CHECK(d.fg == 0xffffff); - CHECK(d.on == 0x707070); - CHECK(d.onic == 0xffffff); + CHECK(d.on == 0xffffff); + CHECK(d.onic == 0x000000); } TEST_CASE("flyout palettes: every palette is fully specified, dark, and on-state differs from off") { diff --git a/tests/test_tray_items.cpp b/tests/test_tray_items.cpp index 3ee932e..b67f319 100644 --- a/tests/test_tray_items.cpp +++ b/tests/test_tray_items.cpp @@ -11,15 +11,21 @@ static std::vector Keys(const std::vector& v, bool onlyOn } using V = std::vector; -TEST_CASE("tray layout defaults: perf off, warmth and brightness on") { +TEST_CASE("tray layout defaults: perf on, warmth and brightness, every toggle on (#329)") { TrayLayout l = ParseTrayLayout({}); - CHECK_FALSE(l.perf); + CHECK(l.perf); CHECK(Keys(l.sliders, true) == V{"colorWarmPct", "colorDimPct"}); CHECK(Keys(l.sliders, false) == EligibleSliders()); - CHECK(Keys(l.toggles, true).empty()); + CHECK(Keys(l.toggles, true) == EligibleToggles()); CHECK(Keys(l.toggles, false) == EligibleToggles()); } +TEST_CASE("tray layout: an explicit trayPerf=0 and an empty toggle list still hide them (#329)") { + TrayLayout l = ParseTrayLayout({ { "trayPerf", "0" }, { "trayToggles", "" } }); + CHECK_FALSE(l.perf); + CHECK(Keys(l.toggles, true).empty()); +} + TEST_CASE("eligible lists match the owner's choice") { CHECK(EligibleSliders() == V{"colorWarmPct", "colorDimPct", "maxLevel", "zoomInSpeed", "zoomOutSpeed", "panSpeed", "cursorSmoothing", "zoomEaseOutMs"}); @@ -108,10 +114,10 @@ TEST_CASE("toggles are uncapped") { CHECK(Keys(ParseTrayLayout(v).toggles, true).size() == 4); } -TEST_CASE("the engine item exists, defaults off, and round trips") { +TEST_CASE("the engine item exists, defaults on (#329), and round trips") { TrayLayout d = ParseTrayLayout({}); bool found = false; - for (const auto& i : d.toggles) if (i.key == "engine") { found = true; CHECK_FALSE(i.on); } + for (const auto& i : d.toggles) if (i.key == "engine") { found = true; CHECK(i.on); } CHECK(found); IniValues v; v["trayToggles"] = "engine,trackCaret"; diff --git a/ui/src/Settings.svelte b/ui/src/Settings.svelte index 3443703..bcd6e16 100644 --- a/ui/src/Settings.svelte +++ b/ui/src/Settings.svelte @@ -17,6 +17,7 @@ import { droppedBinds } from './lib/keybindRules.js'; import TitleBar from './shell/TitleBar.svelte'; import Sidebar from './shell/Sidebar.svelte'; + import ScrollChip from './shell/ScrollChip.svelte'; import Banner from './shell/Banner.svelte'; import Card from './shell/Card.svelte'; import SaveCapsule from './shell/SaveCapsule.svelte'; @@ -317,6 +318,7 @@ {/key}
+
{announcement}
@@ -404,7 +406,9 @@ .app.pending { visibility: hidden; } .app { width: 100vw; height: 100vh; display: grid; grid-template-rows: 38px 1fr; position: relative; overflow: hidden; } .body { display: grid; grid-template-columns: 240px 1fr; min-height: 0; } - .main { position: relative; min-height: 0; overflow-y: auto; padding: 0 40px; outline: none; } + .body { position: relative; } /* the scroll chip sits on the right edge of the page column */ + /* No native scrollbar on the page: ScrollChip shows the position instead (#329). */ + .main { position: relative; min-height: 0; overflow-y: auto; padding: 0 40px; outline: none; scrollbar-width: none; } .main[data-page="tray"] :global(.banner + .card) { margin-top: 20px; } /* k01: a caption-less first card sits 20px under the band */ .page.fade { animation: pagein var(--dur-fast) var(--ease); } @keyframes pagein { from { opacity: 0; } } diff --git a/ui/src/controls/Preview.svelte b/ui/src/controls/Preview.svelte index 9968560..aafb21d 100644 --- a/ui/src/controls/Preview.svelte +++ b/ui/src/controls/Preview.svelte @@ -23,7 +23,7 @@ const visible = (r) => !r.showIf || String(values[r.showIf.key]) === String(r.showIf.eq); const set = (key, val) => { values[key] = val; calls.push([key, val]); }; const extra = { - runningModel: 'hybrid', mpoNeedsRestart: false, version: '0.20.1', + runningModel: 'hybrid', mpoNeedsRestart: false, version: '0.20.2', onRestart: () => calls.push('restart'), onAction: (a) => calls.push(['action', a]), onRepo: () => calls.push('repo'), pick: async () => 'RDR2.exe', diff --git a/ui/src/settings-schema.js b/ui/src/settings-schema.js index 4138d3d..c629af4 100644 --- a/ui/src/settings-schema.js +++ b/ui/src/settings-schema.js @@ -155,7 +155,7 @@ export const groups = [ desc: 'Choose what the tray menu shows, and in what order.', cards: [ { caption: '', rows: [ - { key: 'trayPerf', type: 'toggle', label: 'Performance in the tray', desc: 'Zoom, fps and a frame-time graph.', def: 0 }, + { key: 'trayPerf', type: 'toggle', label: 'Performance in the tray', desc: 'Zoom, fps and a frame-time graph.', def: 1 }, ] }, ] }, diff --git a/ui/src/shell/ScrollChip.svelte b/ui/src/shell/ScrollChip.svelte new file mode 100644 index 0000000..ffe72ff --- /dev/null +++ b/ui/src/shell/ScrollChip.svelte @@ -0,0 +1,61 @@ + + +{#if show} + +{/if} + + diff --git a/ui/src/tray/trayModel.js b/ui/src/tray/trayModel.js index 299c8d8..4529499 100644 --- a/ui/src/tray/trayModel.js +++ b/ui/src/tray/trayModel.js @@ -26,7 +26,7 @@ export const TOGGLES = [ ]; export const KINDS = { sliders: { items: SLIDERS, enabledKey: 'traySliders', orderKey: 'traySliderOrder', defaultOn: ['colorWarmPct', 'colorDimPct'], cap: MAX_SLIDERS }, - toggles: { items: TOGGLES, enabledKey: 'trayToggles', orderKey: 'trayToggleOrder', defaultOn: [], cap: 0 }, + toggles: { items: TOGGLES, enabledKey: 'trayToggles', orderKey: 'trayToggleOrder', defaultOn: ['trackCaret', 'trackFocus', 'keepEdges', 'engine'], cap: 0 }, // all on by default (#329) }; export const TRAY_KEYS = ['trayPerf', 'traySliders', 'traySliderOrder', 'trayToggles', 'trayToggleOrder']; @@ -56,7 +56,7 @@ export function parseList(values, kind) { export function parseTray(values) { return { - perf: String(values.trayPerf ?? '').trim() === '1', + perf: values.trayPerf === undefined || values.trayPerf === null || String(values.trayPerf).trim() === '1', // on by default (#329) sliders: parseList(values, 'sliders'), toggles: parseList(values, 'toggles'), }; diff --git a/ui/tests/tray.spec.js b/ui/tests/tray.spec.js index dcf9432..30f3b19 100644 --- a/ui/tests/tray.spec.js +++ b/ui/tests/tray.spec.js @@ -36,13 +36,16 @@ const chk = (page, list, name) => rowsOf(page, list).filter({ hasText: name }).l // ---- the pure model ---------------------------------------------------------------------------- -test('model: defaults are Warmth and Brightness on, everything else off, performance off', () => { +test('model: defaults are performance on, Warmth and Brightness, and every toggle on (#329)', () => { const t = parseTray({}); - expect(t.perf).toBe(false); + expect(t.perf).toBe(true); expect(t.sliders.map((i) => i.key)).toEqual(SLIDERS.map((i) => i.key)); expect(t.sliders.filter((i) => i.on).map((i) => i.key)).toEqual(['colorWarmPct', 'colorDimPct']); expect(t.toggles.map((i) => i.key)).toEqual(['trackCaret', 'trackFocus', 'keepEdges', 'engine']); - expect(t.toggles.some((i) => i.on)).toBe(false); + expect(t.toggles.every((i) => i.on)).toBe(true); + const off = parseTray({ trayPerf: '0', trayToggles: '' }); // explicit choices still win + expect(off.perf).toBe(false); + expect(off.toggles.some((i) => i.on)).toBe(false); }); test('model: round trip keeps order and enabled, drops unknown keys, appends missing items off', () => { @@ -80,13 +83,13 @@ test('tab: below the sidebar divider, banner, performance card and both lists', await page.locator('.side .it[data-g="tray"]').click(); await expect(page.locator('h1')).toHaveText('Tray menu'); await expect(page.locator('.bdesc')).toHaveText('Choose what the tray menu shows, and in what order.'); - await expect(page.getByRole('switch', { name: 'Performance in the tray' })).not.toBeChecked(); + await expect(page.getByRole('switch', { name: 'Performance in the tray' })).toBeChecked(); // on by default (#329) await expect(page.locator('#cap-sliders')).toHaveText('Sliders'); await expect(page.locator('#cap-toggles')).toHaveText('Toggles'); expect(await names(page, 'sliders')).toEqual(['Warmth', 'Brightness', 'Max zoom', 'Zoom-in speed', 'Zoom-out speed', 'Arrow key speed', 'Pan smoothing', 'Release glide']); expect(await names(page, 'toggles')).toEqual(['Follow the text cursor', 'Follow keyboard focus', 'Keep cursor centred', 'Engine']); await expect(page.locator('[data-cnt="sliders"]')).toHaveText('2 of 4'); - await expect(page.locator('[data-cnt="toggles"]')).toHaveText('0 on'); + await expect(page.locator('[data-cnt="toggles"]')).toHaveText('4 on'); // every toggle on by default (#329) await expect(chk(page, 'sliders', 'Warmth')).toHaveAttribute('aria-checked', 'true'); await expect(chk(page, 'sliders', 'Max zoom')).toHaveAttribute('aria-checked', 'false'); }); @@ -102,10 +105,11 @@ test('tab: item icons are bare line icons without a background box', async ({ pa test('performance switch writes trayPerf', async ({ page }) => { await open(page); - await page.getByRole('switch', { name: 'Performance in the tray' }).check({ force: true }); - expect((await live(page)).trayPerf).toBe('1'); + // On by default (#329): turning it off writes 0, back on writes 1. await page.getByRole('switch', { name: 'Performance in the tray' }).uncheck({ force: true }); expect((await live(page)).trayPerf).toBe('0'); + await page.getByRole('switch', { name: 'Performance in the tray' }).check({ force: true }); + expect((await live(page)).trayPerf).toBe('1'); }); test('check and uncheck write the enabled list and the full order', async ({ page }) => { @@ -119,11 +123,11 @@ test('check and uncheck write the enabled list and the full order', async ({ pag v = await live(page); expect(v.traySliders).toBe('colorDimPct,panSpeed'); // a click anywhere on the row toggles it too - await rowsOf(page, 'toggles').filter({ hasText: 'Follow keyboard focus' }).locator('.k').click(); + await rowsOf(page, 'toggles').filter({ hasText: 'Follow keyboard focus' }).locator('.k').click(); // on by default: turns it off v = await live(page); - expect(v.trayToggles).toBe('trackFocus'); + expect(v.trayToggles).toBe('trackCaret,keepEdges,engine'); expect(v.trayToggleOrder).toBe('trackCaret,trackFocus,keepEdges,engine'); - await expect(page.locator('[data-cnt="toggles"]')).toHaveText('1 on'); + await expect(page.locator('[data-cnt="toggles"]')).toHaveText('3 on'); }); test('slider cap: at four the unchecked boxes are disabled with a note, unchecking frees them', async ({ page }) => { @@ -138,8 +142,10 @@ test('slider cap: at four the unchecked boxes are disabled with a note, unchecki await rowsOf(page, 'sliders').filter({ hasText: 'Zoom-in speed' }).locator('.k').click(); // row click is refused too expect((await live(page)).traySliders).toBe(before); // toggles are not capped - for (const n of ['Follow the text cursor', 'Follow keyboard focus', 'Keep cursor centred']) await chk(page, 'toggles', n).click(); - await expect(page.locator('[data-cnt="toggles"]')).toHaveText('3 on'); + for (const n of ['Follow the text cursor', 'Follow keyboard focus', 'Keep cursor centred']) await chk(page, 'toggles', n).click(); // all on by default: three off + await expect(page.locator('[data-cnt="toggles"]')).toHaveText('1 on'); + for (const n of ['Follow the text cursor', 'Follow keyboard focus', 'Keep cursor centred']) await chk(page, 'toggles', n).click(); // and back on: no cap + await expect(page.locator('[data-cnt="toggles"]')).toHaveText('4 on'); await chk(page, 'sliders', 'Warmth').click(); await expect(page.locator('[data-cnt="sliders"]')).toHaveText('3 of 4'); await expect(chk(page, 'sliders', 'Zoom-in speed')).toBeEnabled(); @@ -237,17 +243,17 @@ test('search finds the Performance row and jumps to the tray tab', async ({ page await expect(page.locator('h1')).toHaveText('Tray menu'); }); -test('engine: the Engine item is off by default, has an icon and a description, and writes trayToggles', async ({ page }) => { +test('engine: the Engine item is on by default (#329), has an icon and a description, and writes trayToggles', async ({ page }) => { await open(page); const row = rowsOf(page, 'toggles').filter({ hasText: 'Engine' }); await expect(row.locator('.tic svg')).toHaveCount(1); await expect(row.locator('.d')).not.toBeEmpty(); - await expect(chk(page, 'toggles', 'Engine')).toHaveAttribute('aria-checked', 'false'); + await expect(chk(page, 'toggles', 'Engine')).toHaveAttribute('aria-checked', 'true'); await chk(page, 'toggles', 'Engine').click(); const v = await live(page); - expect(v.trayToggles).toBe('engine'); + expect(v.trayToggles).toBe('trackCaret,trackFocus,keepEdges'); expect(v.trayToggleOrder).toBe('trackCaret,trackFocus,keepEdges,engine'); - await expect(page.locator('[data-cnt="toggles"]')).toHaveText('1 on'); + await expect(page.locator('[data-cnt="toggles"]')).toHaveText('3 on'); }); test('removed tools: Mouse lock, Pass keys and Pause Wind are gone, and old ini keys for them are dropped', async ({ page }) => { diff --git a/ui/tools/gen-flyout-palettes.cjs b/ui/tools/gen-flyout-palettes.cjs index d24fc2a..c9af0a4 100644 --- a/ui/tools/gen-flyout-palettes.cjs +++ b/ui/tools/gen-flyout-palettes.cjs @@ -1,7 +1,7 @@ // Generates src/tray_app/flyout_palettes.h (the tray flyout's 4 themes, dark only since 0.20.0 / #324) from the same mockup palettes // as ui/tools/gen-themes.cjs, evaluating the tokens tray08.css derives (all color-mix(in srgb) done here): -// toff = fg 12% over card, toffh = fg 18% over card, ton = segon || fill 36% over toff, tonh = segon || fill 44%, -// tonic = segonfg || accent 70% over fg, scrim = bg at 62%, segline = card (the 1px gap shows the group's card). +// off = chip, offh = fg 6% over chip, ton = segon || swon (solid), tonh = segon || fg 12% over swon, +// tonic = segonfg || accenton, scrim = bg at 62%, segline = card (the 1px gap shows the group's card). // // node tools/gen-flyout-palettes.cjs [mockup-ia-dir] // @@ -24,10 +24,12 @@ const H = (n) => '0x' + n.toString(16).padStart(6, '0'); const f = (v) => { const s = String(+(+v).toFixed(3)); return (s.includes('.') ? s : s + '.') + 'f'; }; // today's flyout look (MakeTheme in flyout_draw.cpp before #318), the Wind grey rows +// Wind grey dark ON segments: solid teal with a near-black icon, the black-and-teal contrast of the Settings +// switches (Max 2026-10-02, #329); the old dim teal tint did not fit. const GREY = { dark: { menu: 0x000000, card: 0x121212, menub: 0x333333, fg: 0xf2f2f2, fg2: 0xd0d0d0, fg3: 0xb4b6ba, rule: 0x303236, hl: 0x2d2d2d, glyph: 0xb0b0b0, spark: 0x2fbfa5, fill: 0x2fbfa5, fillline: 0, filllineA: 0, lift: 0x0b0b0b, scrim: 0x000000, scrimA: .55, - band: 0x0a0a0a, track: 0x3d3d3d, off: 0x303033, offh: 0x3b3b3f, offic: 0xc8cad0, onic: 0xa9ece0, on: 0x1f5650, onh: 0x266560, + band: 0x0a0a0a, track: 0x3d3d3d, off: 0x303033, offh: 0x3b3b3f, offic: 0xc8cad0, onic: 0x04201b, on: 0x2fbfa5, onh: 0x45cbb3, onb: 0x2fbfa5, onbA: .4, segline: 0x000000, focus: 0xf2f2f2, aurora: .62, tint: 0, tintA: 0 }, }; // Settings control greys (ui/src/design/themes.css, Wind grey): the toggle bar and dropdown match them. @@ -40,10 +42,11 @@ function mixN(a, b, t) { // a over b by t, 0xRRGGBB numbers } function derive(k) { const card = hex(k.card), fg = hex(k.fg), fill = hex(k.fill), accent = hex(k.pbg); - const toff = mix(fg, card, .12); // base for the ON tint only (unchanged look) const chip = hex(k.chip), chipb = hex(k.chipb), chiph = mix(fg, chip, .06); - const ton = k.segon ? hex(k.segon) : mix(fill, toff, .36), tonh = k.segon ? hex(k.segon) : mix(fill, toff, .44); - const tonic = k.segonfg ? hex(k.segonfg) : mix(accent, fg, .7); + // ON segments: a SOLID fill in the colour of the Settings switches (swon) with the on-accent icon colour, + // so they pop like Wind grey does (Max 2026-10-02, #329); the old tint over the off fill was too faint. + const ton = k.segon ? hex(k.segon) : hex(k.swon), tonh = k.segon ? hex(k.segon) : mix(fg, hex(k.swon), .12); + const tonic = k.segonfg ? hex(k.segonfg) : hex(k.accenton); const fl = k.fillline && k.fillline !== 'transparent'; return { menu: hex(k.bg), card, menub: hex(k.chipb), fg, fg2: hex(k.fg2), fg3: hex(k.fg3), rule: hex(k.line2), hl: hex(k.hover), glyph: hex(k.glyph), spark: accent, fill, fillline: fl ? hex(k.fillline) : 0, filllineA: fl ? 1 : 0, lift: card,