diff --git a/docker/frontend/src/App.tsx b/docker/frontend/src/App.tsx index 6c3a3e0..80bef23 100644 --- a/docker/frontend/src/App.tsx +++ b/docker/frontend/src/App.tsx @@ -2864,9 +2864,13 @@ export function Workspace() { setShowRecipes((v) => !v); }, }, + // IMPORT is not a look to open but the way a look gets into the list, + // so it stands at the tab's own level, beside the chip that opens the + // list — the list itself is a level down, and the way in is not one of + // the things it holds. + { key: 'recipes-import', label: 'IMPORT .RECIPE', onClick: pickRecipeFile }, // ponytail: no CREATE chip — making a recipe and filing the look on // the stage are their own rail tabs (SAVE RECENT, CREATE RECIPES). - // IMPORT is the chip under RECIPES in the tab's own column. ]; case 'favorited': return saved.length === 0 @@ -3215,11 +3219,6 @@ export function Workspace() { // but never filed + the bundled presets. A CREATE that a signed-in account // stored lands in `saved` too, so it is dropped from `created` by name here. // - // IMPORT is not one of them: it is not a look to open but the way a look gets - // into the list, so it stands in the tab's own column under the RECIPES chip - // that opens the list — the file the phone (or another account) exports, same - // envelope, same parser, so a look crosses devices intact. - const importChip: ChipDef = { key: 'recipes-import', label: 'IMPORT .RECIPE', onClick: pickRecipeFile }; const recipeStrip: ChipDef[] = [ ...saved.map((entry) => ({ key: `saved-${entry.id}`, @@ -3385,8 +3384,15 @@ export function Workspace() { onClick: () => { setOpenGroup(null); setOpenParam(null); + setShowRecipes(false); }, }, + // The RECIPES list is a level of its own, though no group holds it: on a + // phone it takes the bar's slot (see .col-sub[data-col="recipes"]), so the + // name here is the only way back to the tab's own chips. + ...(tab === 'presets' && showRecipes + ? [{ key: 'recipes', label: t('sec.recipes'), onClick: () => setShowRecipes(false) }] + : []), ...(openGroup ? [{ key: 'strip', label: stripLabel(openGroup), onClick: () => setOpenParam(null) }] : []), // A ruler is a row, not a strip: its name is the knob's, and the strip it // came from is the level above — COLOR TEMP under WB. @@ -3557,13 +3563,7 @@ export function Workspace() { /> ) : ( - <> - - {/* IMPORT stands under the chip that opens the RECIPES - list: a look is imported INTO that list, so the way in - belongs to the chip, not to the list it fills. */} - {tab === 'presets' && showRecipes ? : null} - + )} )} diff --git a/docker/frontend/src/styles/app.css b/docker/frontend/src/styles/app.css index cbf6fc1..06298ab 100644 --- a/docker/frontend/src/styles/app.css +++ b/docker/frontend/src/styles/app.css @@ -1647,12 +1647,16 @@ input[type="range"] { width: 100%; accent-color: var(--accent); } them is the path under the bar (.crumb), which names the strip they are — so a second row of chips over the first would only repeat where the finger already is, and charge the photo a row for it while a knob is being moved. - Only the two columns that ARE a strip a chip opened displace the bar: the - RECIPES list, a photo's history and the mask's own column are an open - chip's content, not the strip its chip opened, and they keep the bar in - sight under them. Both wear the bar's slot and the bar's own skin, not the + Only the columns that ARE a strip a chip opened displace the bar: the + option groups, the watermark, and RECIPES. Each of those is what a chip of + theirs opened, so the bar would draw the level the finger just asked for + twice — once as the chip that opened it, once as the strip itself — and the + second is the one the finger is reaching into. A photo's history and the + mask's own column are not that: they are an open chip's content, not the + strip its chip opened, and they keep the bar in sight under them. The + displacing three wear the bar's slot and the bar's own skin, not the sub-column's — this row IS the bar now. */ - .col-sub[data-col="options"], .col-sub[data-col="wm"] { + .col-sub[data-col="options"], .col-sub[data-col="wm"], .col-sub[data-col="recipes"] { order: 3; background: var(--bg); max-height: 46vh; @@ -1661,7 +1665,8 @@ input[type="range"] { width: 100%; accent-color: var(--accent); } the slot, with the bar's rule over it and the same ceiling. */ .col-sub[data-col="wm"] { border-top: 1px solid var(--border-soft); overflow-y: auto; } .workspace:has(.chips > .col-sub[data-col="options"]) .col-main, - .workspace:has(.chips > .col-sub[data-col="wm"]) .col-main { display: none; } + .workspace:has(.chips > .col-sub[data-col="wm"]) .col-main, + .workspace:has(.chips > .col-sub[data-col="recipes"]) .col-main { display: none; } /* One strip at a time holds here too: the ruler a knob opens is the last strip of the walk, so it takes the place of the strip that opened it. The