Revert "docs(testing): number #482's deviation D160 (D154-D159 are taken)"
This reverts commit 96c2a293a4.
This commit is contained in:
+8
-8
@@ -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
|
||||
|
||||
@@ -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)<firstSection),rowOf),
|
||||
sections:sections.map(sectionOf),
|
||||
tags:when(rows.find(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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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/]]) {
|
||||
|
||||
@@ -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})=>{
|
||||
|
||||
@@ -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})=>{
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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});
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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); }
|
||||
|
||||
Reference in New Issue
Block a user