feat(history): drop the shared gallery's Linked images caption (D47 ruling)
The concept's "Shared gallery" heading is the gallery's only heading: the embedded collection's "Linked images" heading and "N loaded · N linked images" count repeated it. mountImages gains caption: false and a focusTarget; only the history page's shared gallery passes them, so the Images page and the All images and Repeat run dialogs keep their caption. The layout row and the selected-image side panel stay (TESTING.md D47).
This commit is contained in:
+9
-3
@@ -838,9 +838,15 @@ Recorded deviations on this page:
|
||||
star wherever the read returns `favorite`, no checkbox or selection bar (Images only,
|
||||
#259), the image opens the Viewer. A saved variant with no images shows the concept's
|
||||
`.empty` "Saved variant · no runs yet." beside the grid (the concept puts it inside
|
||||
`.image-grid`, which only holds positioned cards here). The embedded collection keeps
|
||||
production's layout row, "Linked images" caption with "N loaded · N linked images"
|
||||
and the selected-image side panel, which the concept's gallery does not show. With no
|
||||
`.image-grid`, which only holds positioned cards here). The concept's "Shared gallery"
|
||||
heading is the gallery's only heading and names its region: the embedded collection's
|
||||
"Linked images" heading and "N loaded · N linked images" count are removed, since they
|
||||
repeated it (maintainer ruling, 2026-10-21). Two production pieces stay, which the
|
||||
concept's gallery does not show: the layout row (Grid/List/Masonry and Thumbnail size),
|
||||
because the legacy gallery let people change layout and density and nothing else on the
|
||||
page offers it, and the selected-image side panel, the one shared inspector the Images
|
||||
page uses (spec §3.3), so an image's details stay reachable without opening the viewer.
|
||||
The All images and Repeat run dialogs keep their collection caption. With no
|
||||
saved variant the page shows every image of the positive under "All images for this
|
||||
positive, including unassigned images. …" and no empty copy (a state the concept
|
||||
never reaches).
|
||||
|
||||
@@ -969,6 +969,17 @@ test('shared gallery uses concept cards, image opens the viewer, empty copy',asy
|
||||
await expect(heading.locator(':scope > span.small')).toHaveText('No negative · same positive prompt');
|
||||
await expect(section.locator('.empty')).toHaveText('Saved variant · no runs yet.');await expect(section.locator('.empty')).toBeVisible();
|
||||
await expect(section.locator('article[data-image-key]')).toHaveCount(0);
|
||||
// D47 (maintainer ruling 2026-10-21): the concept's "Shared gallery" heading is the gallery's only heading, so the
|
||||
// collection's "Linked images" heading and its "N loaded · N linked images" count are gone; the layout row and the
|
||||
// selected-image side panel stay.
|
||||
await expect(section.locator('.section-caption')).toHaveCount(0);
|
||||
await expect(section.getByRole('heading',{name:'Linked images',exact:true})).toHaveCount(0);
|
||||
await expect(section.getByText(/ loaded · /)).toHaveCount(0);
|
||||
// Outside the side panel (whose own heading names the selected image), the gallery has one section heading.
|
||||
expect(await section.evaluate(node=>[...node.querySelectorAll('h1, h2')].filter(item=>!item.closest('aside')).map(item=>item.textContent))).toEqual(['Shared gallery']);
|
||||
await expect(section.getByRole('group',{name:'Image layout',exact:true})).toBeVisible();
|
||||
await expect(section.getByRole('combobox',{name:'Thumbnail size',exact:true})).toBeVisible();
|
||||
await expect(section.getByRole('complementary',{name:'Selected image details',exact:true})).toBeVisible();
|
||||
// As in the concept, the gallery follows the variants panel; recorded executions (#209) stay below it.
|
||||
const order=await page.locator('.library-history').evaluate(node=>[...node.querySelectorAll('section[aria-label]')].map(item=>item.getAttribute('aria-label')).filter(name=>['Saved negative variants','Shared gallery','Recorded executions'].includes(name)));
|
||||
expect(order).toEqual(['Saved negative variants','Shared gallery','Recorded executions']);
|
||||
@@ -997,6 +1008,11 @@ test('shared gallery uses concept cards, image opens the viewer, empty copy',asy
|
||||
await page.keyboard.press('Escape');await expect(viewer).toBeHidden();await expect(openButton).toBeFocused();
|
||||
await expect(section.locator(':scope > .panel-heading')).toHaveCSS('justify-content','space-between');
|
||||
await expect(section).toHaveCSS('margin-top','24px');
|
||||
// The D47 ruling covers the shared gallery only: the All images dialog's collection keeps its own caption.
|
||||
await variantsPanel(page).getByRole('button',{name:'All images for this positive',exact:true}).click();
|
||||
const all=page.getByRole('dialog',{name:'All images for this positive',exact:true});
|
||||
await expect(all.locator('.section-caption')).toContainText(/ loaded · /);
|
||||
await page.keyboard.press('Escape');await expect(all).toHaveCount(0);
|
||||
// At 390px the heading stays inside the gallery and the page never scrolls sideways.
|
||||
await page.setViewportSize({width:390,height:844});
|
||||
await expect.poll(()=>page.evaluate(()=>document.documentElement.scrollWidth-document.documentElement.clientWidth)).toBeLessThanOrEqual(1);
|
||||
|
||||
@@ -708,7 +708,8 @@ test('archive hides a pair, Show archived finds it and Restore keeps it selected
|
||||
// #249 D8 ruling (a): the Library-embedded gallery has no select checkbox and no selection bar.
|
||||
await expect(page.getByRole('region',{name:'Shared gallery',exact:true}).locator('.image-overlays input[type="checkbox"]')).toHaveCount(0);
|
||||
await expect(page.locator('#bulkbar, .bulkbar')).toHaveCount(0);
|
||||
await expect(page.getByRole('region',{name:'Shared gallery',exact:true}).getByText('1 loaded · 1 linked image',{exact:true})).toBeVisible();
|
||||
// D47: the archived pair's gallery is named by its "Shared gallery" heading, with no separate loaded count.
|
||||
await expect(page.getByRole('region',{name:'Shared gallery',exact:true}).locator('.panel-heading .small')).toHaveText(/ · Archived · same positive prompt$/);
|
||||
expect(galleryReads.at(-1).get('archive')).toBe('archived');expect(galleryReads.at(-1).get('pair_id')).toBe(String(f.pair_id));
|
||||
await expect(headingActions(page).getByRole('button',{name:/^Send prompt pair to ComfyUI/})).toBeEnabled();
|
||||
const writes=page.writes.length;
|
||||
|
||||
@@ -96,7 +96,11 @@ function deliverFavorite(key, favorite) {
|
||||
for (const target of embeddedFavoriteTargets) target(key, favorite);
|
||||
}
|
||||
|
||||
export function mountImages(main, api, parentScope, {embedded = false, fetchPage, emptyText} = {}) {
|
||||
/**
|
||||
* `caption: false` (the history page's shared gallery, D47) leaves out the embedded collection's "Linked images" heading
|
||||
* and count, whose page heading already names it; `focusTarget` then takes focus where that heading did.
|
||||
*/
|
||||
export function mountImages(main, api, parentScope, {embedded = false, fetchPage, emptyText, caption: showCaption = true, focusTarget = null} = {}) {
|
||||
const scope = createScope();
|
||||
const dispose = () => scope.dispose();
|
||||
parentScope.signal.addEventListener('abort', dispose, {once:true});
|
||||
@@ -194,7 +198,7 @@ export function mountImages(main, api, parentScope, {embedded = false, fetchPage
|
||||
collection.setAttribute('aria-label', 'Images');
|
||||
// The concept's caption replaces the old "All images" heading; the embedded gallery keeps its own heading.
|
||||
const caption = el('div', undefined, 'section-caption');
|
||||
const collectionHeading = embedded ? el('h2', 'Linked images') : null;
|
||||
const collectionHeading = embedded && showCaption ? el('h2', 'Linked images') : null;
|
||||
if (collectionHeading) { collectionHeading.tabIndex = -1; caption.append(collectionHeading); }
|
||||
const count = el('span', 'Loading images…');
|
||||
if (!embedded) count.id = 'result-count';
|
||||
@@ -202,7 +206,7 @@ export function mountImages(main, api, parentScope, {embedded = false, fetchPage
|
||||
captionDay.hidden = true;
|
||||
caption.append(count);
|
||||
if (!embedded) caption.append(captionDay);
|
||||
const focusFallback = collectionHeading || heading?.h1 || main;
|
||||
const focusFallback = collectionHeading || focusTarget || heading?.h1 || main;
|
||||
const grid = el('div', undefined, 'image-grid');
|
||||
grid.setAttribute('role', 'list'); grid.setAttribute('aria-label', 'Image collection');
|
||||
const continuation = el('div', undefined, 'infinite-continuation');
|
||||
@@ -217,7 +221,9 @@ export function mountImages(main, api, parentScope, {embedded = false, fetchPage
|
||||
const showsEmpty = !embedded || typeof emptyText === 'string';
|
||||
const empty = el('div', embedded ? emptyText : 'No matching images. Try another search or category.', 'empty');
|
||||
empty.hidden = true;
|
||||
collection.append(caption, grid);
|
||||
// D47: the history page's shared gallery is named by its own "Shared gallery" heading, so it has no caption.
|
||||
if (showCaption) collection.append(caption);
|
||||
collection.append(grid);
|
||||
if (showsEmpty) collection.append(empty);
|
||||
collection.append(continuation);
|
||||
// After a Load more or Retry press, the index of the first image that batch appends (concept: focus it).
|
||||
|
||||
@@ -616,7 +616,8 @@ export function mountLibrary(main,api,parentScope,{positiveId}={}) {
|
||||
// The concept's shared gallery (app.js a616-621): its heading names the selected variant; the embedded
|
||||
// collection renders the PR C media cards. Recorded executions (#209) stay below it. /* D47 */
|
||||
const context=el('span','','small');context.setAttribute('role','status');
|
||||
const galleryHeading=el('div',undefined,'panel-heading');galleryHeading.append(el('h2','Shared gallery'),context);
|
||||
const galleryTitle=el('h2','Shared gallery');galleryTitle.tabIndex=-1;
|
||||
const galleryHeading=el('div',undefined,'panel-heading');galleryHeading.append(galleryTitle,context);
|
||||
const gallery=el('section',undefined,'history-gallery');gallery.setAttribute('aria-label','Shared gallery');gallery.append(galleryHeading);
|
||||
const images=el('div',undefined,'library-linked-images');gallery.append(images);
|
||||
const recorded=el('div',undefined,'library-recorded-container');
|
||||
@@ -690,7 +691,7 @@ export function mountLibrary(main,api,parentScope,{positiveId}={}) {
|
||||
updateGalleryContext();
|
||||
galleryScope?.dispose();galleryScope=createScope();images.replaceChildren();
|
||||
// An archived pair's images are read in the archived scope; the gallery stays authoritative.
|
||||
mountImages(images,api,galleryScope,{embedded:true,emptyText:pairId===null?undefined:'Saved variant · no runs yet.',fetchPage:({parameters,signal})=>{const query=new URLSearchParams(parameters);if(pairId!==null) query.set('pair_id',String(pairId));if(archived) query.set('archive','archived');return api.get(`/prompt_manager/library/${positiveId}/images?${query}`,{signal});}});
|
||||
mountImages(images,api,galleryScope,{embedded:true,caption:false,focusTarget:galleryTitle,emptyText:pairId===null?undefined:'Saved variant · no runs yet.',fetchPage:({parameters,signal})=>{const query=new URLSearchParams(parameters);if(pairId!==null) query.set('pair_id',String(pairId));if(archived) query.set('archive','archived');return api.get(`/prompt_manager/library/${positiveId}/images?${query}`,{signal});}});
|
||||
updateSend();updateCompare();
|
||||
}
|
||||
/** A metadata receipt names no pair, so the page re-reads its own selection through the exact-ID route. */
|
||||
|
||||
Reference in New Issue
Block a user