feat(history): drop the Linked images caption from the history dialogs (D47 ruling)

The maintainer extended the D47 trim to the history page's two media
dialogs. All images for this positive and Repeat run are named by their
own dialog title, as in the concept (app.js a1282-1304), so their
embedded collection no longer shows the "Linked images" heading or the
"N loaded · N linked images" count. A stranded focus returns to the
dialog's close button. The Images page keeps its caption.

The history concept comparison now records each dialog's caption count
in both documents, and TESTING.md D47 covers the dialogs.
This commit is contained in:
Vito Sansevero
2026-10-21 03:36:37 -07:00
parent ea36849e23
commit 12b591a7e6
5 changed files with 32 additions and 13 deletions
+9 -5
View File
@@ -846,10 +846,13 @@ Recorded deviations on this page:
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).
The same ruling covers the page's two media dialogs, All images for this positive and
Repeat run: as in the concept (app.js a1282-1304), each is named by its own dialog
title, with no "Linked images" heading or "N loaded · N linked images" count, and a
stranded focus returns to the dialog's close button (maintainer ruling, 2026-10-21).
The Images page keeps its 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).
The run dot reads `GET /prompt_manager/library/{positive_id}/history/pairs/{pair_id}/newest`
(see "Chronological Library history"), and each lane's "N runs" is the pair's
@@ -874,7 +877,8 @@ Images collection, so its height is not compared). D25: each compared block moun
production class or margin. Behaviors are compared in both documents: a red dot changes
the negative block and the gallery, Compare toggles `aria-pressed`, All images opens a
dialog with the positive's four images, the run dot opens a dialog (D26: Recorded run
where the concept shows Repeat run), the heading send previews the selected pair, and
where the concept shows Repeat run), neither dialog has a collection caption (D47), the
heading send previews the selected pair, and
← Prompt library navigates. `history comparison detects mutations` injects
`.ca-tools .variant-dot { width: 20px }` and removes Compare negatives. The older
`history-canonical-comparison.mjs` stays for `workspace-visual.spec.mjs`'s recorded
+4 -2
View File
@@ -144,13 +144,15 @@ const negativeText=page=>page.locator('article.surface .prompt-section.negative
const galleryOf=async page=>({context:await page.locator('.history-gallery .panel-heading .small').textContent(),
cards:await galleryCards(page).locator('.image-open').evaluateAll(nodes=>nodes.map(node=>node.getAttribute('aria-label')))});
/** A dialog the page opens: its accessible name and how many media cards it shows once loaded; then closed. */
/** A dialog the page opens: its accessible name, how many media cards it shows once loaded and its captions; then closed. */
async function dialogOf(page,trigger,{name,cards}) {
await trigger.click();
const dialog=page.getByRole('dialog',{name,exact:true});
await expect(dialog).toBeVisible();
if(cards)await expect(dialog.locator('.media-card')).toHaveCount(cards);
const result=cards?{name,cards:await dialog.locator('.media-card').count()}:{name};
// D47: neither document's media dialog carries a collection caption ("Linked images", "N loaded · …").
const captions=await dialog.locator('.section-caption').count();
const result=cards?{name,cards:await dialog.locator('.media-card').count(),captions}:{name,captions};
await page.keyboard.press('Escape');await expect(dialog).toHaveCount(0);
return result;
}
+12 -2
View File
@@ -832,6 +832,11 @@ test('run dot appears only for pairs with more than one run and opens its run',a
await clean.click();const repeat=modalDialog(page,'Repeat run');await expect(repeat).toBeVisible();
await expect(repeat.locator('#modal-body > p.small')).toHaveText('Same positive and negative, another execution.');
await expect(repeat.locator('#modal-body > .spaced-15 article[data-image-key]')).toHaveCount(1);
// D47: as in the concept (a1291-1304), the dialog title names the image; no collection heading or count repeats it.
await expect(repeat.locator('.section-caption')).toHaveCount(0);
await expect(repeat.getByRole('heading',{name:'Linked images',exact:true})).toHaveCount(0);
await expect(repeat.getByText(/ loaded · /)).toHaveCount(0);
expect(await repeat.evaluate(node=>[...node.querySelectorAll('h1, h2')].filter(item=>!item.closest('aside')).map(item=>item.textContent))).toEqual(['Repeat run']);
expect(reads.at(-1).get('pair_id')).toBe(String(f.clean_pair));expect(reads.at(-1).get('limit')).toBe('1');
await page.keyboard.press('Escape');await expect(repeat).toHaveCount(0);await expect(clean).toBeFocused();
});
@@ -1008,10 +1013,15 @@ 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.
// D47 (maintainer ruling 2026-10-21) covers the history page's dialogs too: the All images dialog is named by its
// own title, with no "Linked images" heading or "N loaded · N linked images" count, as the concept's (a1282-1290).
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 expect(all.locator('article[data-image-key]').first()).toBeVisible();
await expect(all.locator('.section-caption')).toHaveCount(0);
await expect(all.getByRole('heading',{name:'Linked images',exact:true})).toHaveCount(0);
await expect(all.getByText(/ loaded · /)).toHaveCount(0);
expect(await all.evaluate(node=>[...node.querySelectorAll('h1, h2')].filter(item=>!item.closest('aside')).map(item=>item.textContent))).toEqual(['All images for this positive']);
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});
+4 -3
View File
@@ -97,8 +97,9 @@ function deliverFavorite(key, favorite) {
}
/**
* `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.
* `caption: false` (the history page's shared gallery and its All images and Repeat run dialogs, D47) leaves out the
* embedded collection's "Linked images" heading and count, whose page heading or dialog title 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();
@@ -221,7 +222,7 @@ 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;
// D47: the history page's shared gallery is named by its own "Shared gallery" heading, so it has no caption.
// D47: the history page's shared gallery and dialogs are named by their own heading or title, so they have no caption.
if (showCaption) collection.append(caption);
collection.append(grid);
if (showsEmpty) collection.append(empty);
+3 -1
View File
@@ -765,7 +765,9 @@ export function mountLibrary(main,api,parentScope,{positiveId}={}) {
const dialogScope=createScope();imagesDialogScope=dialogScope;
const grid=el('div',undefined,className);
openContentDialog({scope:content,title,nodes:intro?[el('p',intro,'small'),grid]:[grid],trigger,onClose:()=>{dialogScope.dispose();if(imagesDialogScope===dialogScope) imagesDialogScope=null;}});
mountImages(grid,api,dialogScope,{embedded:true,fetchPage});
// D47: the dialog's own title names its images, as in the concept (a1282-1304), so the collection has no
// "Linked images" heading or count; a stranded focus returns to the dialog's close button.
mountImages(grid,api,dialogScope,{embedded:true,caption:false,focusTarget:grid.closest('dialog')?.querySelector('.dialog-header > button') ?? null,fetchPage});
}
const imagesPage=(id,query,signal)=>api.get(`/prompt_manager/library/${id}/images?${query}`,{signal});
/** Concept "All images for this positive" (a1282-1290): every image of the positive, Load more bounded. */