From fd20968bc811235fcbbed145cd043f9f5344862d Mon Sep 17 00:00:00 2001 From: Vito Sansevero Date: Thu, 29 Oct 2026 12:54:02 -0700 Subject: [PATCH] Revert "docs(testing): number #482's deviation D160 (D154-D159 are taken)" This reverts commit 96c2a293a4f168798771868ddb02289b5a954ce1. --- TESTING.md | 16 ++++++++-------- tests/browser/concept-inventory.mjs | 8 ++++---- tests/browser/full-inspector.spec.mjs | 4 ++-- tests/browser/inspector-concept.spec.mjs | 16 ++++++++-------- tests/browser/workspace-history.spec.mjs | 2 +- tests/browser/workspace-library-delete.spec.mjs | 2 +- tests/browser/workspace-tagging.spec.mjs | 8 ++++---- tests/js/workspace-file-inspector.test.js | 4 ++-- tests/js/workspace-library-delete.test.js | 2 +- tests/test_bulk_library_delete.py | 2 +- tests/test_library_delete_routes.py | 2 +- web/js/workspace/file-inspector.mjs | 10 +++++----- web/js/workspace/workspace.css | 2 +- 13 files changed, 39 insertions(+), 39 deletions(-) diff --git a/TESTING.md b/TESTING.md index 67a6a96c..99fe289f 100644 --- a/TESTING.md +++ b/TESTING.md @@ -732,7 +732,7 @@ embedded records, exact generation facts and recorded or inferred prompt evidenc It renders in two concept surfaces (#249 D20–D22, D38). The Images side panel shows only the concept content: cover, title, filename, the evidence badge with a category chip naming its origin (D146), the + and − prompt blocks with copy and send, the tag chips in labelled -sections after the negative prompt (D160: "Image tags · N" for the file's own image tags, D88, then "Prompt +sections after the negative prompt (D154: "Image tags · N" for the file's own image tags, D88, then "Prompt tags · N" or "Recipe tags · N" for the linked tags; image tags and the linked positive's own tags filter Images, D21), the six facts, and a footer with "Send to ComfyUI" and "Prompt history" only when prompt roles resolved or a link is saved; the sticky panel @@ -1524,7 +1524,7 @@ Recorded deviations on this page (spec §8): filters, while recipe, recorded-run and embedded tags stay plain `span.chip`. Amended by D88 (#249 PR C): the file's own current image tags lead the same row and are filters too; a linked tag already shown (case-insensitive) is left out. - Amended by D160 (#449): the image tags and the linked tags each sit in their own + Amended by D154 (#449): the image tags and the linked tags each sit in their own labelled section, and a linked tag stays in its section even when an image tag has the same name. Amended by D146 (#332): the category chip also names its origin in visible text @@ -1814,8 +1814,8 @@ Inspect plan keeps D60–D66 (plan D60 → D83, D61 → D84, D65 → D88, D68 concept image's tags saved as its image tags production still equals the concept. After a Tag save the selected file's inspection is re-read, so the panel redraws the row without reselecting and the viewer, which shares that inspection, shows it. - Amended by D160 (#449): the one row is now two labelled sections. -- **D160** (#449, beyond the concept): the inspector's tags no longer follow the + Amended by D154 (#449): the one row is now two labelled sections. +- **D154** (#449, beyond the concept): the inspector's tags no longer follow the negative prompt as a bare `.tags` row, where they read as part of it. Every surface of the shared inspector (the Images side panel, the viewer details, History's inline current file, the Library-embedded gallery and Activity) draws them in @@ -1833,10 +1833,10 @@ Inspect plan keeps D60–D66 (plan D60 → D83, D61 → D84, D65 → D88, D68 unchanged. History's current-file read carries the same `image_tags` facet as the Images read (#457), so History's inline current file and its viewer show the image tags section too (`test_current_inspection_carries_the_files_image_tags`). `tests/browser/inspector-concept.spec.mjs` wraps the - concept's row in the same sections at runtime (`withTagSections`, the D160 rule + concept's row in the same sections at runtime (`withTagSections`, the D154 rule injected) and compares the measured sections (frame, heading, label, row); a mutation of the label color, the row margin or the top rule fails it. Browser tests: - "D160: image tags sit in their own labelled section after the negative prompt, in + "D154: image tags sit in their own labelled section after the negative prompt, in the panel and the viewer" and "the inline current file labels its image tags after its negative prompt". - **D115** (beyond the plan's list): the concept always has a selected image, so @@ -1924,7 +1924,7 @@ production-versus-concept difference of PR C and its record: | Suggestions count field | D91 (ruling T3-bulk) | | No prototype note, "Suggestions from …", no " in this tab" | D29 | | Image tags first in the inspector and viewer chip row | D88 (ruling T7/C2) | -| Image tags and linked tags in labelled sections after the negative prompt | D160 (#449) | +| Image tags and linked tags in labelled sections after the negative prompt | D154 (#449) | | Auto-tag images `aria-disabled` without a selected image | D115 | | Retry leads the actions when the controller allows it | D116 (ruling T5) | | States the concept lacks (busy, conflict, unavailable model, video) | Global Constraints: `#tag-generation-status` text only | @@ -2700,7 +2700,7 @@ failed backup, busy, unconfirmed, restore fence); `tests/browser/library-delete- ### Deleting a prompt in a saved pair permanently (#482) -**D160** (beyond legacy, maintainer ruling 2026-10-29; follow-up to D152): when the Delete… selection +**D154** (beyond legacy, maintainer ruling 2026-10-29; follow-up to D152): when the Delete… selection holds prompts in a saved pair that no other positive uses as its negative ("deletable"), the review asks, in a D90 radio row "Prompts in a saved pair" after the grid: **Archive (hidden, restorable)** (checked on every open, never remembered, so the review is D152's) or **Delete permanently, with diff --git a/tests/browser/concept-inventory.mjs b/tests/browser/concept-inventory.mjs index 44446722..9234a50d 100644 --- a/tests/browser/concept-inventory.mjs +++ b/tests/browser/concept-inventory.mjs @@ -233,17 +233,17 @@ export async function withChipSource(page,root,origin,category=null) { } /** - * #449 D160: the inspector's tag sections in the concept's prompt-section frame (workspace.css `.tag-section`). The concept + * #449 D154: the inspector's tag sections in the concept's prompt-section frame (workspace.css `.tag-section`). The concept * has one bare `.tags` row after its prompt blocks (app.js a402), so the recorded rule is added and that row wrapped at * runtime, the browser laying out what follows: `sections` lists `[className, label, name]` per section, the first * holding the concept's row and each further one a copy of it. Returns the undo. */ -export const D160_TAG_SECTION=`.tag-section { border-top: 1px solid var(--line); padding-top: 15px; margin-top: 15px; } +export const D154_TAG_SECTION=`.tag-section { border-top: 1px solid var(--line); padding-top: 15px; margin-top: 15px; } .tag-section .role-label { color: var(--ca-muted); } .tag-section > .tags { margin-top: 0; }`; export const PROMPT_TAGS=[['linked-tags','Prompt tags','Prompt tags']]; export async function withTagSections(page,root,sections=PROMPT_TAGS) { - const restoreRule=await mutate(page,{css:D160_TAG_SECTION}); + const restoreRule=await mutate(page,{css:D154_TAG_SECTION}); const token=await page.evaluate(({root,sections})=>{ const container=document.querySelector(root); const inner=container?.querySelector(':scope > .detail-inner')??container?.querySelector(':scope > div'); @@ -755,7 +755,7 @@ export function measureInspector(selector) { badges:when(rows.find(row=>parts.indexOf(row)parts.indexOf(row)>firstSection),rowOf), - // #449 D160: the labelled tag sections after the prompt blocks, each with its frame, heading, label and chip row. + // #449 D154: the labelled tag sections after the prompt blocks, each with its frame, heading, label and chip row. tagSections:parts.filter(item=>item.matches('section.tag-section')).map(tagSectionOf), facts:when(parts.find(item=>item.matches('dl.meta-grid')),factsOf), // Disclosures (viewer only) by their summary and frame; their content is the D20 production facts. diff --git a/tests/browser/full-inspector.spec.mjs b/tests/browser/full-inspector.spec.mjs index 63e7ec70..c35e1ddd 100644 --- a/tests/browser/full-inspector.spec.mjs +++ b/tests/browser/full-inspector.spec.mjs @@ -139,7 +139,7 @@ test('panel matches the concept order and copy',async({page,request})=>{ // #332 MC8: the chip names where its value came from, in visible text after the category. await expect(badges.locator('span.chip:not(.blue)')).toHaveText('Recipe category: Inspection · recipe'); await expect(badges.locator('span.chip:not(.blue) .ca-visually-hidden')).toHaveText('Recipe category: '); - // Recipe tags are not the positive tags the Images Tags filter matches, so they stay plain chips (D21 ruling); D160 + // Recipe tags are not the positive tags the Images Tags filter matches, so they stay plain chips (D21 ruling); D154 // (#449) labels their own section after the negative prompt, named for the recipe as D146's category chip is. const recipeTags=panel.getByRole('region',{name:'Recipe tags',exact:true}); await expect(recipeTags).toHaveClass('tag-section linked-tags'); @@ -317,7 +317,7 @@ test('viewer details hold provenance, raw records and downloads',async({page,req await expect(provenance.getByText('Prompt evidence: workflow inferred',{exact:true})).toBeVisible(); await expect(provenance.getByText('Saved association; not verified against current file',{exact:true})).toBeVisible(); await expect(provenance.getByRole('link',{name:'Download original',exact:true})).toBeVisible(); - // D160 (#449) labels the recipe's tags in their own section; the other legacy labels stay gone. + // D154 (#449) labels the recipe's tags in their own section; the other legacy labels stay gone. await expect(details).not.toContainText(/Configuration evidence|Embedded parameter evidence|prompt evidence|extension/); await expect(details.locator('.tag-section.linked-tags .role-label')).toHaveText('Recipe tags · 2'); // Workflow & raw metadata: the concept's focusable `pre`, then the downloads with their unavailable reasons. diff --git a/tests/browser/inspector-concept.spec.mjs b/tests/browser/inspector-concept.spec.mjs index a58261b7..d2bcd606 100644 --- a/tests/browser/inspector-concept.spec.mjs +++ b/tests/browser/inspector-concept.spec.mjs @@ -51,7 +51,7 @@ function withCategoryScope(inventory) { } /** - * Recorded inspector deviations only (spec §8, TESTING.md D3, D20–D22, D34, D38, D117, D139, D146 and D160, whose labelled + * Recorded inspector deviations only (spec §8, TESTING.md D3, D20–D22, D34, D38, D117, D139, D146 and D154, whose labelled * tag sections the concept page gets at runtime from `withTagSections`); everything else must * equal production. `reference.viewerPlain` is the concept's own full details * with its tag chips made plain (D20), D117's summary button added and, under a coarse pointer, D34's targets, all at @@ -131,7 +131,7 @@ async function withCollectionsButton(page,selector) { } /** - * The concept's tag chips in the open viewer in D160's labelled sections (`sections`) as plain `span.chip`s (D20), D34's + * The concept's tag chips in the open viewer in D154's labelled sections (`sections`) as plain `span.chip`s (D20), D34's * coarse targets and D117's "Copy settings summary" after the six facts (LI-4), drawn as the concept's own plain button * (its "Download sample JSON", without its action); returns an undo. */ @@ -190,7 +190,7 @@ async function viewerOf(page,concept,mutation=null,sections=PROMPT_TAGS) { return {viewerDetails,viewerPlain,fileControls}; } async function inventory(page,concept=false,sections=PROMPT_TAGS) { - // D146: the concept panel's category chip with its visible origin; D139: Collections… after Prompt history; D160's + // D146: the concept panel's category chip with its visible origin; D139: Collections… after Prompt history; D154's // labelled tag sections (by default the fixture's D39 positive tags under "Prompt tags"), laid out by the concept page. const restoreSource=concept?await withChipSource(page,'#image-inspector',ORIGIN):null; const restoreCollections=concept?await withCollectionsButton(page,'#image-inspector > .detail-footer'):null; @@ -264,7 +264,7 @@ async function productionSendOf(page,context) { /** A tag chip: the collection it leaves (the count line), then the chip pressed again for the same result. */ async function tagOf(page) { const before=await page.locator('#result-count').textContent(); - // The concept's bare row, or production's linked-tags section (D160): the last chip row either way. + // The concept's bare row, or production's linked-tags section (D154): the last chip row either way. await panelOf(page).locator('.detail-inner .tags').last().getByRole('button',{name:'cinematic',exact:true}).click(); // Production's caption reads "Loading images…" until the tag's listing answers (#391); the concept's search is instant. await expect.poll(async()=>{const text=await page.locator('#result-count').textContent();return text!==before&&text!=='Loading images…';}).toBe(true); @@ -330,7 +330,7 @@ for(const [width,height] of SIZES)for(const theme of THEMES)test(`inspector pane expect.soft(panel.badges.chips.map(chip=>chip.text),'concept badges').toEqual(['Execution recorded','Landscapes · prompt']); expect.soft(reference.viewerDetails.structure.inner.slice(-2),'concept full details').toEqual(['details','details']); - // D160: both documents measured the labelled section, so the comparison covers it (never two absent sections). + // D154: both documents measured the labelled section, so the comparison covers it (never two absent sections). for(const [name,inventory] of [['concept panel',reference.panel],['concept viewer',reference.viewerPlain],['panel',actual.panel],['viewer',actual.viewerDetails]]) expect.soft(inventory.tagSections.map(section=>section.label.text),`${name} tag sections`).toEqual(['Prompt tags · 3']); expect.soft(diff,'differences after the recorded deviations').toEqual([]); @@ -368,7 +368,7 @@ test('a recipe-linked image without recipe metadata shows its positive prompt me await expect(badges.locator('.chip .ca-visually-hidden')).toHaveText('Positive prompt category: '); // D146 (#332): the visible origin names where the value came from. await expect(badges.locator('.chip .chip-source')).toHaveText(' · prompt'); - // D160: the positive's tags, so the section is "Prompt tags", not "Recipe tags". + // D154: the positive's tags, so the section is "Prompt tags", not "Recipe tags". const tags=panelOf(page).getByRole('region',{name:'Prompt tags',exact:true}); await expect(tags.locator('.role-label')).toHaveText('Prompt tags · 3'); await expect(tags.locator('.tags > button.chip')).toHaveText(['minimal','warm_light','editorial']); @@ -396,7 +396,7 @@ test('a tag chip collection reads as loading, not empty, until its listing answe }); // Task C3 (D88, #249 PR C): the concept's chip row is the image's own tags (`i.tags`, app.js a402). With those names -// saved as the file's current image tags, production draws them in D160's "Image tags" section (Tags filter buttons in +// saved as the file's current image tags, production draws them in D154's "Image tags" section (Tags filter buttons in // the panel, plain in the viewer) and the linked positive's equal tags in its "Prompt tags" section after it (#449), // and equals the concept with its row in those two sections, in both sizes. for(const [width,height] of SIZES)test(`image tags saved on the concept image keep the inspector equal to the concept ${width}x${height} (D88)`,async({page,request},testInfo)=>{ @@ -438,7 +438,7 @@ test('inspector comparison detects mutations',async({page})=>{ await restoreCss();await settle(page); assertInspectorMatch(reference,await measure(),viewport); - // D160: the tag section's muted label and its row's spacing are part of the mapped concept. + // D154: the tag section's muted label and its row's spacing are part of the mapped concept. for(const [css,path] of [['#image-inspector .tag-section .role-label { color: rgb(255, 0, 0) }',/panel\.tagSections\.0\.label\.color/], ['#image-inspector .tag-section > .tags { margin-top: 12px }',/panel\.tagSections\.0\.row\.marginTop/], ['#image-inspector .tag-section { border-top-width: 0 }',/panel\.tagSections\.0\.borderTopWidth/]]) { diff --git a/tests/browser/workspace-history.spec.mjs b/tests/browser/workspace-history.spec.mjs index 658a7fda..d24e391b 100644 --- a/tests/browser/workspace-history.spec.mjs +++ b/tests/browser/workspace-history.spec.mjs @@ -496,7 +496,7 @@ test('the inline current file names its prompts apart from the frozen run fields expect(await exact(dialog(page).getByRole('region',{name:'Positive prompt',exact:true}).locator('p.prompt-text'))).toBe(before); }); -// #449 (D160), #457: History's inline current file draws the file's own image tags in the shared labelled section, named +// #449 (D154), #457: History's inline current file draws the file's own image tags in the shared labelled section, named // with its context so they stay apart from the frozen run's fields. The tags are saved on the real file through the Tag // routes and come from History's own current-file read (its `image_tags` facet), as Images reads them. test('the inline current file labels its image tags after its negative prompt',async({page,request})=>{ diff --git a/tests/browser/workspace-library-delete.spec.mjs b/tests/browser/workspace-library-delete.spec.mjs index 0bf7da4d..306e42a6 100644 --- a/tests/browser/workspace-library-delete.spec.mjs +++ b/tests/browser/workspace-library-delete.spec.mjs @@ -109,7 +109,7 @@ test('Show archived prompts lists archived prompts, and Restore on its bar retur await expect(page.locator('.library-status')).toHaveText('No archived prompts.'); }); -// #482 (D160): a prompt in a saved pair can be deleted permanently, with its own saved pairs; Archive stays the default. +// #482 (D154): a prompt in a saved pair can be deleted permanently, with its own saved pairs; Archive stays the default. const choice=page=>review(page).getByRole('radiogroup',{name:'Prompts in a saved pair'}); const row=(page,term)=>review(page).locator('dl.meta-grid > div').filter({has:page.locator('dt',{hasText:new RegExp(`^${term}$`)})}).locator('dd'); test('Delete permanently removes a paired prompt with its own saved pair; its negative stays (#482)',async({page,request})=>{ diff --git a/tests/browser/workspace-tagging.spec.mjs b/tests/browser/workspace-tagging.spec.mjs index 3dcc7079..b61abbca 100644 --- a/tests/browser/workspace-tagging.spec.mjs +++ b/tests/browser/workspace-tagging.spec.mjs @@ -458,7 +458,7 @@ test('virtualized cards release listeners on unmount', async ({ page, request }) expect(await count()).toBe(mounted); }); -// Task C3 (D88), #449 (D160): the side panel and the viewer details draw the file's own current image tags in their own +// Task C3 (D88), #449 (D154): the side panel and the viewer details draw the file's own current image tags in their own // "Image tags" section, then D21's linked tags in a "Prompt tags" or "Recipe tags" section after it. Panel image tags are // Tags filter buttons; the viewer's chips are plain. const panel = page => page.getByRole('complementary', { name: 'Selected image details', exact: true }); @@ -479,7 +479,7 @@ test('inspector shows saved image tags after Save without reselecting', async ({ const reread = inspected(page, FIRST); await dialog(page).getByRole('button', { name: 'Save manual tags', exact: true }).click(); expect((await (await reread).json()).image_tags).toEqual({ state: 'current', tags: ['dunes_tag', 'Editorial'] }); - // The image tags lead as filter buttons in their own section; the recipe keeps its "editorial" in its own (D160). + // The image tags lead as filter buttons in their own section; the recipe keeps its "editorial" in its own (D154). await expect(imageTags(page).locator('.chip')).toHaveText(['dunes_tag', 'Editorial']); await expect(imageTags(page).locator('button.chip')).toHaveText(['dunes_tag', 'Editorial']); await expect(panel(page).locator('.tag-section.image-tags .role-label')).toHaveText('Image tags · 2'); @@ -524,7 +524,7 @@ test('viewer details show the same chips', async ({ page, request }) => { await expect(details.locator('.tag-section.image-tags')).toHaveCount(0); }); -// #449 (D160): the image tags are a labelled region of their own after the negative prompt, framed like a prompt section, +// #449 (D154): the image tags are a labelled region of their own after the negative prompt, framed like a prompt section, // so they never read as part of it; the same in the side panel and the viewer details. async function tagSectionPlacement(root) { return root.evaluate(node => { @@ -541,7 +541,7 @@ async function tagSectionPlacement(root) { const PLACED = {after: true, inside: false, below: true, border: '1px solid', paddingTop: '15px', marginTop: '15px', label: {transform: 'uppercase', size: '10px', sameColorAsNegative: false}}; -test('D160: image tags sit in their own labelled section after the negative prompt, in the panel and the viewer', async ({ page, request }) => { +test('D154: image tags sit in their own labelled section after the negative prompt, in the panel and the viewer', async ({ page, request }) => { await saveImageTags(request, FIRST, ['dunes_tag', 'sunlit']); await page.goto(images); const section = panel(page).getByRole('region', { name: 'Image tags', exact: true }); diff --git a/tests/js/workspace-file-inspector.test.js b/tests/js/workspace-file-inspector.test.js index 436c20e1..7d082a7b 100644 --- a/tests/js/workspace-file-inspector.test.js +++ b/tests/js/workspace-file-inspector.test.js @@ -212,7 +212,7 @@ function tagged(imageTags, library = {status:'linked', tags:['Minimal', 'warm_li test('renders current image tags before linked tags, each without repeats within its source', async () => { const {inspectorTagChips} = await load(); assert.equal(typeof inspectorTagChips, 'function', 'inspectorTagChips is exported'); - // D160 amends D88: a linked tag that is also an image tag stays in its own labelled section. + // D154 amends D88: a linked tag that is also an image tag stays in its own labelled section. assert.deepEqual(inspectorTagChips(tagged({state:'current', tags:['dunes', 'minimal', 'Dunes']}), {filterable:true}), [{name:'dunes', filter:true, source:'image'}, {name:'minimal', filter:true, source:'image'}, {name:'Minimal', filter:true, source:'linked'}, {name:'warm_light', filter:true, source:'linked'}, {name:'editorial', filter:true, source:'linked'}]); @@ -238,7 +238,7 @@ test('none and unavailable render no image chips', async () => { } }); -// #449 (D160): the image's own tags and the linked prompt's or recipe's tags each get a labelled section after the +// #449 (D154): the image's own tags and the linked prompt's or recipe's tags each get a labelled section after the // prompt blocks, so per-image tags never read as part of the negative prompt. function renderVariant(record, variant, options = {}) { const f = mountedDom(), scope = createScope(); diff --git a/tests/js/workspace-library-delete.test.js b/tests/js/workspace-library-delete.test.js index 825739fc..ce3c3aba 100644 --- a/tests/js/workspace-library-delete.test.js +++ b/tests/js/workspace-library-delete.test.js @@ -150,7 +150,7 @@ test('the archived notice says why (the saved pair) and names negative variants assert.equal(archivedNoticeText(undefined), archivedNoticeText(0)); }); -// #482 (D160): prompts in a saved pair can be deleted permanently, with their own saved pairs. +// #482 (D154): prompts in a saved pair can be deleted permanently, with their own saved pairs. const D1 = 'a'.repeat(64), D2 = 'b'.repeat(64); const paired = (patch = {}) => preview({archive: [3, 4, 5], deletable: [3, 4], review_digests: {3: D1, 4: D2}, paired_counts: {pairs: 3, archived_pairs: 1, variants: 2, kept_negatives: 2}, paired_image_links: 4, ...patch}); diff --git a/tests/test_bulk_library_delete.py b/tests/test_bulk_library_delete.py index de7a3a21..3f4c3730 100644 --- a/tests/test_bulk_library_delete.py +++ b/tests/test_bulk_library_delete.py @@ -543,7 +543,7 @@ if __name__ == "__main__": class TestPairedDelete(LibraryDeleteCase): - """#482 (D160): Delete permanently also removes a prompt's own saved pairs.""" + """#482 (D154): Delete permanently also removes a prompt's own saved pairs.""" def pair_rows(self, positive): return rows( diff --git a/tests/test_library_delete_routes.py b/tests/test_library_delete_routes.py index a3f403be..ad913255 100644 --- a/tests/test_library_delete_routes.py +++ b/tests/test_library_delete_routes.py @@ -315,7 +315,7 @@ class TestLibraryDeleteRoutes(LibraryDeleteRouteCase): class TestPairedDeleteRoutes(LibraryDeleteRouteCase): - """#482 (D160): Delete permanently with a prompt's own saved pairs.""" + """#482 (D154): Delete permanently with a prompt's own saved pairs.""" async def confirm_paired(self, review, ids, backup=None, digests=None): return await self.post( diff --git a/web/js/workspace/file-inspector.mjs b/web/js/workspace/file-inspector.mjs index e883e8bc..e35551a2 100644 --- a/web/js/workspace/file-inspector.mjs +++ b/web/js/workspace/file-inspector.mjs @@ -176,8 +176,8 @@ function extensionOf(record) { return (record.file?.extension || record.identity.path.split('.').at(-1)).replace(/^\./, '').toUpperCase(); } /** - * The tag chips after the prompt blocks (D88, D160): the file's current image tags (`source: 'image'`), then D21's linked - * tags (`source: 'linked'`), each without case-insensitive repeats within its own source (D160: a linked tag that is also + * The tag chips after the prompt blocks (D88, D154): the file's current image tags (`source: 'image'`), then D21's linked + * tags (`source: 'linked'`), each without case-insensitive repeats within its own source (D154: a linked tag that is also * an image tag stays in both labelled sections). With `filterable` (a Tags filter exists, as in the Images panel) every * image tag sets it, and a linked tag only when it is the positive's own (D21); otherwise, as in the viewer, every chip * is plain. @@ -197,7 +197,7 @@ export function inspectorTagChips(record, {filterable}) { if (library?.status === 'linked') add(library.tags, filterable && library.tags_scope === 'positive', 'linked'); return chips; } -/** D160 (#449): a tag section's visible heading; linked tags name their owner as D146's category chip does. */ +/** D154 (#449): a tag section's visible heading; linked tags name their owner as D146's category chip does. */ export function tagSectionLabel(source, library) { if (source === 'image') return 'Image tags'; return library?.tags_scope === 'pair' ? 'Recipe tags' : 'Prompt tags'; @@ -303,7 +303,7 @@ export function mountFileInspector(container, {api, sender, parentScope, variant // not what it matches, so they (and chips where no Tags filter exists, as in the viewer) stay plain. With the // D39 fallback a recipe without tags shows its positive's, which are filter buttons again. D88: the file's own // image tags lead the row and, where a Tags filter exists, always set it (it matches them too). - // D160 (#449): each source gets its own labelled section, so image tags never read as part of the negative prompt. + // D154 (#449): each source gets its own labelled section, so image tags never read as part of the negative prompt. const chips = inspectorTagChips(record, {filterable:!!onTagFilter}); for (const source of ['image', 'linked']) { const own = chips.filter(chip => chip.source === source); @@ -342,7 +342,7 @@ export function mountFileInspector(container, {api, sender, parentScope, variant if (!full && footer.childNodes.length) container.append(footer); } /** - * D160: `section.tag-section` in the prompt sections' frame, its `.role-label` heading naming the source and count. + * D154: `section.tag-section` in the prompt sections' frame, its `.role-label` heading naming the source and count. * The region's name leaves the count out and carries the prompt blocks' context ("Current file image tags"). */ function tagSection(source, label, chips) { diff --git a/web/js/workspace/workspace.css b/web/js/workspace/workspace.css index e68060c6..de08fa2f 100644 --- a/web/js/workspace/workspace.css +++ b/web/js/workspace/workspace.css @@ -244,7 +244,7 @@ main:focus-visible { outline: 2px solid var(--ca-focus); outline-offset: 4px; } details, and an unavailable download stays visibly inert next to its reason. */ .file-inspector { min-inline-size: 0; overflow-wrap: anywhere; } .file-inspector [aria-disabled="true"] { opacity: .6; cursor: not-allowed; } -/* #449 D160: the image's own tags and the linked prompt's or recipe's tags sit in the prompt sections' frame (concept +/* #449 D154: the image's own tags and the linked prompt's or recipe's tags sit in the prompt sections' frame (concept .prompt-section), under a muted .role-label; the heading's 8px spaces the chips, as it spaces a prompt's text. */ .file-inspector .tag-section { border-top: 1px solid var(--line); padding-top: 15px; margin-top: 15px; } .file-inspector .tag-section .role-label { color: var(--ca-muted); }