diff --git a/TESTING.md b/TESTING.md index 09187454..7dacb574 100644 --- a/TESTING.md +++ b/TESTING.md @@ -2135,10 +2135,8 @@ npm run --prefix tools/vendor test:workflows -- library-concept.spec.mjs \ ### Selecting prompts and Set category (#437) **D148** (beyond the concept, whose Library cards have no checkbox): the Prompt library -selects prompts and adds tags to them (#442) or sets their category (#437) in one step, -the legacy admin page's bulk "Add Tags" and "Set Category", and selects every loaded -prompt as its select-all checkbox did (#444 ruling). It is built only from the concept's -Images parts: +selects prompts and sets their category in one step, the legacy admin page's bulk "Set +Category". It is built only from the concept's Images parts: - Each `article.prompt-card` leads with the Images `.image-overlays > label > input` checkbox ("Select "), placed by the concept's overlay CSS over the art; the card @@ -2147,36 +2145,15 @@ Images parts: Shift+Tab across windowed cards reach it (the card's controls are `button, input[type=checkbox]`); Home, End and the arrow keys still move between titles. - `div#library-bulkbar` sits between the toolbar and the caption, empty until something - is checked, then the concept's `.bulkbar`: "N selected", Add tags, Set category, Clear - (the Images bar's order). The + is checked, then the concept's `.bulkbar`: "N selected", Set category, Clear (D151 + adds Add tags before Set category, in the Images bar's order). The selection is a set of prompt ids over the loaded shelf: windowed-out cards keep their state, Load more keeps it, and any shelf reload (search, category, sort, tags, a write) and leaving the page clear it. Clicking a checkbox toggles it and sets the anchor; Shift+click applies the anchor's range with the box's new state (the D135 - helpers). Escape in the bar and Clear clear it, focus to the heading. -- Select all of the loaded shelf (no concept counterpart): Ctrl/Cmd+A in the grid (Images' - key check; the search box keeps its text meaning) selects every loaded prompt, - windowed-out cards included, and Escape in the grid clears it with focus kept on the - card. "Select all N loaded" follows the count while some loaded prompts are unchecked - and at most 1,000 are loaded; it selects them all and focus moves to Add tags; N follows - each loaded batch. It sits where D135's "Select all N matching" sits, but that offer - appears once every loaded image is checked; this one never reaches beyond the loaded - prompts. There is no Shift+arrow, long-press or "all matching" on the Library. -- Add tags opens the shared `simpleInput` dialog ("Tag selected prompts", "Tags, - separated by commas", the tag vocabulary `GET /prompt_manager/tags` as suggestions, the - list the editor offers). The field refuses no tag, more than 20, a tag over 50 - characters or with a control character, each with its sentence. Apply posts `{prompt_ids, - tags, expected_epoch}` to `POST /prompt_manager/bulk/tags`: prompt tags only, never - image tags, auto-tags or pair tags. Names are normalized as image tags are (trim, no - commas, casefold repeats folded), registry rows are found and inserted by their exact - name and only when some prompt gets them, and a prompt gets only names it lacks by - casefold. A prompt whose tags would pass 20 is skipped (`over_limit`), so the editor can - still save it. The answer is `{library_epoch, tags, updated, unchanged, not_found, - over_limit}`; toasts "Added “a, b” to N prompts." plus already-had, skipped and not-found - sentences. Storage restored: "… No tags were added."; unconfirmed: "Adding the tags could - not be confirmed. The Library was reloaded."; refused: "Tags could not be added. Try - again." The legacy `{prompt_ids, tags}` body is unchanged; both forms refuse a body - that is not a JSON object with 400. + helpers). Escape in the bar and Clear clear it, focus to the heading. There is no + Shift+arrow, long-press or "Select all N matching" on the Library (D151 adds + Ctrl/Cmd+A and "Select all N loaded"). - Set category opens the shared `simpleInput` dialog ("Categorize selected prompts", `input#action-input` labelled Category, at most 100 characters, the Library's categories as suggestions). A blank value keeps it open; a control character is @@ -2198,22 +2175,57 @@ Images parts: storage was restored since this page loaded. No category was changed." and a reload. No answer, 502, 503 or 504: "The category change could not be confirmed. The Library was reloaded." Any other refusal: "Category could not be set. Try again.", the - selection kept. Every bar action and the select-all offer are `aria-disabled` while a - write runs. + selection kept. Every bar action (and D151's select-all offer) is `aria-disabled` while + a write runs. - The bar's DOM and write loop live in `library-selection.mjs`; the toast text (`setOutcome` with the noun "prompt"), the range helpers, the count and the dialog are the Images bar's. One bar core shared with `images-selection.mjs` is a follow-up. Tests: `tests/browser/workspace-library-selection.spec.mjs` (set, blank and control characters, Clear/Escape/filter, Shift+click range and Tab across the window edge, the -fence after a return visit, an unseen restore, refused and unconfirmed writes, Add tags -and its field and restore refusal, Ctrl/Cmd+A and Select all N loaded); +fence after a return visit, an unseen restore, refused and unconfirmed writes); `tests/browser/library-concept.spec.mjs` ("the Library selection checkbox and bar match the concept's Images ones", 1600x1100 and 390x844, both themes, with a moved-checkbox mutation); units `tests/js/workspace-library-selection.test.js`, `tests/js/workspace-library-state.test.js` (the epoch); Python -`tests/test_bulk_library_category.py`, `tests/test_bulk_library_tags.py`. A Python -change: ComfyUI needs a restart. +`tests/test_bulk_library_category.py`. A Python change: ComfyUI needs a restart. + +### Adding tags to selected prompts and selecting all (#442) + +**D151** (beyond the concept, on D148's bar): the Prompt library adds tags to the selected +prompts in one step, the legacy admin page's bulk "Add Tags", and selects every loaded +prompt, as its select-all checkbox did (#444 ruling). The concept has neither. + +- The bar reads "N selected", Add tags, Set category, Clear: the Images bar's order. +- Select all of the loaded shelf: Ctrl/Cmd+A in the grid (Images' key check; the search + box keeps its text meaning) selects every loaded prompt, windowed-out cards included, + and Escape in the grid clears it with focus kept on the card. "Select all N loaded" + follows the count while some loaded prompts are unchecked and at most 1,000 are loaded; + it selects them all and focus moves to Add tags; N follows each loaded batch. It sits + where D135's "Select all N matching" sits, but that offer appears once every loaded + image is checked; this one never reaches beyond the loaded prompts. No "all matching". +- Add tags opens the shared `simpleInput` dialog ("Tag selected prompts", "Tags, + separated by commas", the tag vocabulary `GET /prompt_manager/tags` as suggestions, the + list the editor offers). The field refuses no tag, more than 20, a tag over 50 + characters or with a control character, each with its sentence. Apply posts + `{prompt_ids, tags, expected_epoch}` to `POST /prompt_manager/bulk/tags`: prompt tags + only, never image tags, auto-tags or pair tags. Names are normalized as image tags are + (trim, no commas, casefold repeats folded); registry rows are found and inserted by + their exact name and only when some prompt gets them; a prompt gets only the names it + lacks by casefold. A prompt whose tags would pass 20 is skipped (`over_limit`), so the + editor can still save it. The answer is `{library_epoch, tags, updated, unchanged, + not_found, over_limit}`; the toast "Added “a, b” to N prompts." adds already-had, + skipped and not-found sentences. Storage restored: "Prompt storage was restored since + this page loaded. No tags were added."; unconfirmed: "Adding the tags could not be + confirmed. The Library was reloaded."; refused: "Tags could not be added. Try again." + The legacy `{prompt_ids, tags}` body is unchanged; both forms refuse a body that is not + a JSON object with 400. + +Tests: `tests/browser/workspace-library-selection.spec.mjs` (Add tags with its suggestions, +field refusals and restore refusal; Ctrl/Cmd+A and Select all N loaded across batches and +during a write); `tests/browser/library-concept.spec.mjs` (the bar comparison now includes +Add tags); units `tests/js/workspace-library-selection.test.js`; Python +`tests/test_bulk_library_tags.py`. A Python change: ComfyUI needs a restart. ### Prompt history page and recorded deviations (#249 PR F-a) @@ -6058,7 +6070,7 @@ Deploying this backend needs a ComfyUI restart (Python and route changes). longer hands off to the legacy tag manager. That page stays reachable from Overview's "Open existing tag manager" card for tag search and sort, the untagged entry and multi-tag AND/OR browsing; bulk prompt delete stays on the legacy admin page (bulk tags: -the Prompt library's selection bar, D148). A +the Prompt library's selection bar, D151). A prompt's category is changed in the Prompt library editor, on many prompts at once with the Prompt library's selection bar (D148, #437), or with the legacy admin page's "Set Category" (both `POST /prompt_manager/bulk/category`); diff --git a/tests/browser/workspace-library-selection.spec.mjs b/tests/browser/workspace-library-selection.spec.mjs index 82d9f4d2..d92eeb76 100644 --- a/tests/browser/workspace-library-selection.spec.mjs +++ b/tests/browser/workspace-library-selection.spec.mjs @@ -61,7 +61,7 @@ test('Set category writes the selected prompts on the server, reloads the shelf await expect(box(page,first.id)).toHaveAccessibleName(new RegExp(`^Select `)); await box(page,first.id).check();await expect(bar(page).locator('strong')).toHaveText('1 selected'); await box(page,second.id).check();await expect(bar(page).locator('strong')).toHaveText('2 selected'); - // Two of three loaded are checked, so Select all N loaded follows the count (D148, #444 ruling). + // Two of three loaded are checked, so Select all N loaded follows the count (D151, #444 ruling). await expect(bar(page).getByRole('button')).toHaveText([`Select all ${items.length} loaded`,'Add tags','Set category','Clear']); await setCategory(page,' Studio set '); await expect(toast(page)).toHaveText('Category “Studio set” set on 2 prompts.'); @@ -242,7 +242,7 @@ test('the Add tags field states what it refuses, and a restore the page did not expect((await(await request.get('/prompt_manager/tags')).json()).tags).not.toContain('after-restore'); }); -// #444 ruling (D148): select every loaded prompt, as the legacy page's select-all checkbox did for its page. +// #444 ruling (D151): select every loaded prompt, as the legacy page's select-all checkbox did for its page. test('Ctrl/Cmd+A and Select all N loaded select every loaded prompt, windowed ones included',async({page,request,isMobile})=>{ await expect(await request.post('/__test__/reset',{data:{native_library:true,extra_prompts:200}})).toBeOK(); if(!isMobile)await page.setViewportSize({width:1600,height:1100}); diff --git a/web/js/workspace/library-selection.mjs b/web/js/workspace/library-selection.mjs index 9535e450..27f2b883 100644 --- a/web/js/workspace/library-selection.mjs +++ b/web/js/workspace/library-selection.mjs @@ -8,7 +8,7 @@ import { countOf } from './plural.mjs'; import { showWorkspaceToast } from './toast.mjs'; /** - * The Prompt library's selection and its bulk actions (D148, beyond the concept): "Add tags" (#442) and "Set + * The Prompt library's selection and its bulk actions (D148, D151, beyond the concept): "Add tags" (#442, D151) and "Set * category" (#437), the legacy admin page's bulk Add Tags and Set Category in the workspace. Built from the concept's * Images parts only: each card's `.image-overlays` checkbox and the `.bulkbar` row ("N selected", the actions in the * Images bar's order, Clear). The selection is a set of @@ -91,7 +91,7 @@ const WRITES = Object.freeze({ }); /** - * "Select all N loaded" (#444 ruling, D148; the legacy page's select-all checkbox): offered while some loaded prompts + * "Select all N loaded" (#444 ruling, D151; the legacy page's select-all checkbox): offered while some loaded prompts * are unchecked and no more are loaded than an action accepts. It sits where D135's "Select all N matching" sits on * Images, but that offer appears once every loaded image is checked; this one reaches only the loaded prompts. */ diff --git a/web/js/workspace/library.mjs b/web/js/workspace/library.mjs index 0a307ae7..3baa22bc 100644 --- a/web/js/workspace/library.mjs +++ b/web/js/workspace/library.mjs @@ -364,7 +364,7 @@ export function mountLibrary(main,api,parentScope,{positiveId,savedState=null,on scope.listen(grid,'keydown',event=>{ const card=event.target.closest('article[data-prompt-id]');if(!card) return; const index=Number(card.dataset.index); - // D148 (#444 ruling): Ctrl/Cmd+A selects every loaded prompt and Escape clears, as on Images (focus stays). + // D151 (#444 ruling): Ctrl/Cmd+A selects every loaded prompt and Escape clears, as on Images (focus stays). if((event.ctrlKey || event.metaKey) && !event.altKey && event.key.toLowerCase()==='a') {event.preventDefault();selection.selectAll();return;} if(event.key==='Escape' && selection.hasSelection()) {event.preventDefault();selection.clear();return;} if(event.target.matches('[data-history]') && ['Home','End','ArrowLeft','ArrowRight'].includes(event.key)) {event.preventDefault();focusCard(event.key==='Home'?0:event.key==='End'?state.items.length-1:index+(event.key==='ArrowLeft'?-1:1),false,true);} @@ -385,7 +385,7 @@ export function mountLibrary(main,api,parentScope,{positiveId,savedState=null,on } else if(retainedAnchor) {pendingAnchor=retainedAnchor;retainedAnchor=null;} } state=next; - // A loaded batch: the selection bar's count and Select all N loaded follow it (D148). + // A loaded batch: the selection bar's count and Select all N loaded follow it (D151). if(batch) selection.listingChanged(); if(next.variantTotal!==null) { const count=`${countOf(next.total,'positive prompt')} · ${countOf(next.variantTotal,'negative variant')}`;