From 067b32131ecf7010a0e90c7b6764aaa61214aba3 Mon Sep 17 00:00:00 2001 From: 3dtours Date: Mon, 28 Sep 2026 21:27:55 +0700 Subject: [PATCH] web: drop the heading over the folder column MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The column carried the name of the folder its tree belongs to, painted above the rows. The rows already say it — the head of the tree is a row like any other — so the heading only said it twice, and because it was not a row it stayed on screen when the tree was folded away: the one label left with nothing under it. The right click that raised it now finds the row itself, which is where a reader aims anyway. The label, its menu hook, its translation and its rule go; the tree, the row menus and fold-all keep working as before. Co-authored-by: PenguinHarness --- docker/frontend/scripts/library-check.mjs | 29 ++++++---------- docker/frontend/src/Library.tsx | 40 ++++++----------------- docker/frontend/src/i18n/en.ts | 1 - docker/frontend/src/i18n/vi.ts | 1 - docker/frontend/src/styles/app.css | 14 -------- 5 files changed, 20 insertions(+), 65 deletions(-) diff --git a/docker/frontend/scripts/library-check.mjs b/docker/frontend/scripts/library-check.mjs index 5b32a25..a2dd181 100644 --- a/docker/frontend/scripts/library-check.mjs +++ b/docker/frontend/scripts/library-check.mjs @@ -359,10 +359,12 @@ step( (await page.$('[data-key="lib-node-CheckRoll/2026"]')) !== null, await page.$eval('[data-key="lib-node-CheckRoll"] .adm-album-name', (e) => e.textContent) ); +// The folder's name is the row's, said once: a heading over the column would only +// repeat it, and that heading would outlive the rows when the tree is folded. step( - 'the column is titled with the folder the tree belongs to', - (await page.$eval('[data-key="lib-root-name"]', (e) => e.textContent)) === 'Roll A', - await page.$eval('[data-key="lib-root-name"]', (e) => e.textContent) + 'the folder is named on its row and nowhere over the column', + (await page.$$('[data-key="lib-root-name"], .lib-col-title')).length === 0 && + (await page.$eval('[data-key="lib-node-CheckRoll"] .adm-album-name', (e) => e.textContent)) === 'Roll A' ); // The empty part of the column is where one more folder comes from. @@ -373,10 +375,10 @@ step('a right click on the empty column offers a folder', blank.join(',') === 'l await page.keyboard.press('Escape'); step('escape closes the folder menu too', (await page.$('[data-key="lib-menu"]')) === null); -// The head of the tree is the whole tree's control: a right click on the column's -// own name, or on the row of a folder picked at the top level, folds the folders -// nested below the top level. The top-level row itself stays open — one click on -// its name already shuts it, so the menu item would only repeat that click — and +// The head of the tree is the whole tree's control: a right click on the row of a +// folder picked at the top level folds the folders nested below the top level. The +// top-level row itself stays open — one click on its name already shuts it, so the +// menu item would only repeat that click — and // the frames in the strip are the strip's business and do not move with the rows. // The subfolder opened above carries a fold of its own, so it is opened first: // the claim is what ONE click does to every row at once, not to one already shut. @@ -387,8 +389,7 @@ await page.waitForFunction(() => document.querySelectorAll('[data-key^="lib-node }); const deepRows = await page.$$eval('[data-key^="lib-node-"]', (els) => els.length); const deepTiles = await page.$$eval(thumbs, (els) => els.length); -// The row is where a reader right-clicks, so fold-the-tree leads that row's own -// menu rather than living only on the column's name above it. +// The row is where a reader right-clicks, so fold-the-tree leads that row's menu. await page.click('[data-key="lib-node-CheckRoll"]', { button: 'right' }); await page.waitForSelector('[data-key="lib-menu"]', { timeout: 10_000 }); const rowMenu = await page.$$eval('[data-key="lib-menu"] button', (els) => els.map((b) => b.dataset.key)); @@ -397,16 +398,6 @@ step( rowMenu.join(',') === 'lib-menu-collapse,lib-rename-CheckRoll,lib-rescan-CheckRoll,lib-drop-CheckRoll', JSON.stringify(rowMenu), ); -await page.keyboard.press('Escape'); -await page.click('[data-key="lib-root-name"]', { button: 'right' }); -await page.waitForSelector('[data-key="lib-menu"]', { timeout: 10_000 }); -const head = await page.$$eval('[data-key="lib-menu"] button', (els) => els.map((b) => b.dataset.key)); -step('a right click on the column name offers it alone', head.join(',') === 'lib-menu-collapse', JSON.stringify(head)); -step( - 'the column name still reads as the folder it belongs to', - (await page.$eval('[data-key="lib-root-name"]', (e) => e.textContent)) === 'Roll A', - await page.getAttribute('[data-key="lib-root-name"]', 'title') -); await page.click('[data-key="lib-menu-collapse"]'); await page.waitForFunction(() => document.querySelectorAll('[data-key^="lib-node-"]').length === 3, null, { timeout: 10_000, diff --git a/docker/frontend/src/Library.tsx b/docker/frontend/src/Library.tsx index f352858..63d587f 100644 --- a/docker/frontend/src/Library.tsx +++ b/docker/frontend/src/Library.tsx @@ -107,9 +107,8 @@ export function Library() { const [note, setNote] = useState(null); // The folder menu: what a right click opens, and where. No folder means the // right click landed on the empty part of the column, which is the way in for - // one more folder; `root` means it landed on the head of a tree — the column's - // own name, or the row of a folder picked at the top level — which carries - // folding the folders nested below the top level. + // one more folder; `root` means it landed on a row at the top level — the head + // of a tree — which also carries folding the folders nested below the top level. const [menu, setMenu] = useState<{ folder: LibraryFolder | null; root?: boolean; x: number; y: number } | null>(null); // Where the drag of the column divider started, and how wide the column was // then — the pointer only ever reports a position, not a distance moved. @@ -371,10 +370,6 @@ export function Library() { [nodes, folded] ); - // The folder the open node belongs to: its name titles the column, and its - // handle is what the menu acts on. - const root = folders.find((f) => f.name === rootName) ?? null; - // A remembered subfolder that no longer holds a frame falls back to the folder // it sits in, so the screen never opens on a node that is not drawn. useEffect(() => { @@ -519,21 +514,6 @@ export function Library() { setMenu({ folder: null, x: Math.min(e.clientX, window.innerWidth - 190), y: Math.min(e.clientY, window.innerHeight - 96) }); }} > - {/* The column is titled with the folder the tree belongs to, so - a renamed roll is named where its frames are named. The name - is the whole tree's handle as well: one right click here - folds the folders nested below the top level. */} - { - e.preventDefault(); - setMenu({ folder: null, root: true, x: Math.min(e.clientX, window.innerWidth - 190), y: Math.min(e.clientY, window.innerHeight - 96) }); - }} - > - {root?.label || root?.name} - {rows.map((n) => { const folder = folders.find((f) => f.name === n.key.split('/')[0]) ?? null; // A roll being read says so on its own row: while a scan runs @@ -571,9 +551,9 @@ export function Library() { // act on the folder that was picked, so a right click on a // subfolder row brings up its roll's menu. A row at the top // level is a whole tree's head, so it also carries folding - // what sits under it — the item the column's own name has, - // where a reader actually right-clicks. Shift+F10 raises - // the same event, which is the keyboard's way in. + // what sits under it, where a reader actually right-clicks. + // Shift+F10 raises the same event, which is the keyboard's + // way in. onContextMenu={(e) => { if (!folder) return; e.preventDefault(); @@ -729,10 +709,10 @@ export function Library() { permission has lapsed gets the one thing that can be done about it. */} {menu ? (
e.preventDefault()}> - {/* Fold-the-tree leads the menu of a top-level row, and stands alone on - the column's own name: the folders nested below the top level go, - one line per folder, and no top-level row ever leaves the column — - the row itself is one click away and its click already shuts it. */} + {/* Fold-the-tree leads the menu of a row at the top level: the folders + nested below it go, one line per folder, and no top-level row ever + leaves the column — the row itself is one click away and its click + already shuts it. */} {menu.root ? (