From 35d8d579849c02ed09d17ca83b6db15b717a8eef Mon Sep 17 00:00:00 2001 From: 3dtours Date: Wed, 30 Sep 2026 07:34:07 +0700 Subject: [PATCH] HSL: click a colour chip to open its panel, the way PICK does MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The colour band chips only aimed the mixer before; now they open the same panel the eyedropper opens, seeded with the colour the chip is named after (hexToRgb turns that readout back into the RGB bytes the picker stores). A pick already on the photo keeps its spot — only a mixer with nowhere to hang takes the middle of the frame. The HSL column itself is untouched: 8 chips, the rule, IMAGE, HUE/SAT/LUM, the readout and RESET all stay where they were. --- docker/frontend/src/App.tsx | 24 ++++++++++++++++++++---- docker/frontend/src/Landing.tsx | 8 ++++---- docker/frontend/src/Library.tsx | 2 +- docker/frontend/src/ui/ChipColumn.tsx | 7 ++++++- docker/frontend/src/ui/ToolRail.tsx | 5 ++++- docker/frontend/src/ui/TopBar.tsx | 14 +++++++++----- 6 files changed, 44 insertions(+), 16 deletions(-) diff --git a/docker/frontend/src/App.tsx b/docker/frontend/src/App.tsx index ba63c69..3c8eec8 100644 --- a/docker/frontend/src/App.tsx +++ b/docker/frontend/src/App.tsx @@ -175,6 +175,13 @@ function hslToHex(h: number, s: number, l: number): string { return `#${chan(0)}${chan(8)}${chan(4)}`; } +// The way back: a band chip hands the eyedropper's readout the colour it is +// named after, and that readout is RGB bytes. +function hexToRgb(hex: string): { r: number; g: number; b: number } { + const n = parseInt(hex.slice(1), 16); + return { r: (n >> 16) & 255, g: (n >> 8) & 255, b: n & 255 }; +} + // The knobs that drive a colour get a swatch under their ruler, so the hue is // visible next to the number. TEMP is the only one left on this path: TINT's row // lives in the sidebar's WB panel now, so its ±10 axis draws no ruler here. @@ -2862,7 +2869,16 @@ export function Workspace() { color: hslToHex(b.hue, 70, 50), active: hslBand === b.id, amberValue: hslBand !== b.id && moved, - onClick: () => setHslBand(b.id), + // A band chip aims the mixer AND opens its panel, the same panel + // the eyedropper opens: the colour it starts from is the colour + // the chip is named after, and this band's knobs push it from + // there. A pick already on the photo keeps its spot — only a + // mixer with nowhere to hang takes the middle of the frame. + onClick: () => { + setHslBand(b.id); + setSample(hexToRgb(hslToHex(b.hue, 70, 50))); + setPickedAt((at) => at ?? { fx: 0.5, fy: 0.5 }); + }, }; }), // The divider keeps the three image-wide sliders out of the colour @@ -3249,7 +3265,7 @@ export function Workspace() { aria-pressed={recipe.name === entry.name} onClick={() => applyRecipe({ ...(entry.recipe as Recipe), name: entry.name })} > - {entry.name} + {entry.name} {/* Only this account can delete its own file — the API scopes the delete by user, the button mirrors it. */} @@ -3261,7 +3277,7 @@ export function Workspace() { aria-label={t('save.delete')} onClick={() => void deleteRecipeEntry(entry)} > - ✕ + ))} @@ -3359,7 +3375,7 @@ export function Workspace() { disabled={locating} onClick={() => locateMe()} > - {locating ? t('wm.locating') : t('wm.useLocation')} + {locating ? t('wm.locating') : t('wm.useLocation')}
diff --git a/docker/frontend/src/Landing.tsx b/docker/frontend/src/Landing.tsx index 5bcc0e9..de91473 100644 --- a/docker/frontend/src/Landing.tsx +++ b/docker/frontend/src/Landing.tsx @@ -613,14 +613,14 @@ export function Landing() { className={`chip${mode === 'light' ? ' on' : ''}`} onClick={() => setMode('light')} > - Light + Light
{/* The same colour groups the workspace offers; the page's own @@ -642,7 +642,7 @@ export function Landing() { className={`chip${accent === entry.id ? ' on' : ''}`} onClick={() => setAccent(entry.id)} > - {entry.label} + {entry.label} ))} diff --git a/docker/frontend/src/Library.tsx b/docker/frontend/src/Library.tsx index ca8c82f..8aff98c 100644 --- a/docker/frontend/src/Library.tsx +++ b/docker/frontend/src/Library.tsx @@ -684,7 +684,7 @@ export function Library() { title={t('lib.subsHint')} onClick={() => setDeep((prev) => !prev)} > - {t(deep ? 'lib.subsHide' : 'lib.subsShow')} + {t(deep ? 'lib.subsHide' : 'lib.subsShow')} ) : null} diff --git a/docker/frontend/src/ui/ChipColumn.tsx b/docker/frontend/src/ui/ChipColumn.tsx index 19558e7..d6d3966 100644 --- a/docker/frontend/src/ui/ChipColumn.tsx +++ b/docker/frontend/src/ui/ChipColumn.tsx @@ -99,7 +99,12 @@ export function ChipRow({ chips, grid }: { chips: ChipDef[]; grid?: boolean }) { }} > {chip.color ? : null} - {chip.label} + {/* The name is a box, not the bare text node it used to be: a flex + container turns a loose text node into an anonymous item of its + own, and on iOS that item stops being painted until the column + it sits in is touched. Same fault, same shape as the panel head + (see `dev-panel-label` in DevelopPanels). No rule of its own. */} + {chip.label} {chip.value ? {chip.value} : null} {chip.pro ? PRO : null} diff --git a/docker/frontend/src/ui/ToolRail.tsx b/docker/frontend/src/ui/ToolRail.tsx index 6963c3b..2a9b923 100644 --- a/docker/frontend/src/ui/ToolRail.tsx +++ b/docker/frontend/src/ui/ToolRail.tsx @@ -72,7 +72,10 @@ export function ToolRail({ }} > - {t(entry.label)} + {/* A box, not a bare text node: on a phone the glyph is display:none + and the name would be the button's only child — an anonymous flex + item, which is the one thing iOS stops painting until a touch. */} + {t(entry.label)} ))} {/* The build this page came off, at the foot of the rail: an image on the diff --git a/docker/frontend/src/ui/TopBar.tsx b/docker/frontend/src/ui/TopBar.tsx index d63a675..1edf55f 100644 --- a/docker/frontend/src/ui/TopBar.tsx +++ b/docker/frontend/src/ui/TopBar.tsx @@ -155,9 +155,13 @@ export function TopBar({ {menu === 'theme' ? (
- - - + {/* The name is a span, not a bare text node: a chip is a flex row, + and a loose text node in one is an anonymous item — the shape + iOS stops painting until the page is touched (same as the + panel heads and ChipRow's chips). */} + + +
{ACCENTS.map((entry) => ( @@ -167,7 +171,7 @@ export function TopBar({ className={`chip${accent === entry.id ? ' on' : ''}`} onClick={() => setAccent(entry.id)} > - {entry.label} + {entry.label} ))}
@@ -204,7 +208,7 @@ export function TopBar({ setMenu(null); }} > - {entry.label} + {entry.label} ))}