diff --git a/TESTING.md b/TESTING.md index 2d6ae598..2a3993d3 100644 --- a/TESTING.md +++ b/TESTING.md @@ -2668,8 +2668,9 @@ rulings of 2026-10-28): shelf. Restore posts `{prompt_ids, expected_epoch}` to `POST /prompt_manager/library/restore` (every requested archived id, protected or not): "Restored N prompts to the Prompt library." -- An archived prompt's page shows the concept's `.notice` ("Archived" / "This prompt and its - negative variants are hidden from the Prompt library; nothing was removed.") with Restore; +- An archived prompt's page shows the concept's `.notice` ("Archived" / "This prompt is archived + because it is in a saved pair. It and its N negative variants are hidden from the Prompt library; nothing was + removed.", or "It is hidden from…" when it has none) with Restore; everything else on the page works. New prompt or Add negative on an archived prompt's text keeps it archived and toasts "This text is an archived prompt. Restore it to show it in the Prompt library again." diff --git a/tests/browser/workspace-library-delete.spec.mjs b/tests/browser/workspace-library-delete.spec.mjs index 6c051316..59b72af8 100644 --- a/tests/browser/workspace-library-delete.spec.mjs +++ b/tests/browser/workspace-library-delete.spec.mjs @@ -111,7 +111,9 @@ test('an archived prompt\'s page says so and restores it from its notice',async( await page.goto(`/prompt_manager/workspace#/library/${cat.id}`); const notice=page.locator('.library-archived-notice'); await expect(notice.locator('h2')).toHaveText('Archived'); - await expect(notice.locator('p').first()).toHaveText('This prompt and its negative variants are hidden from the Prompt library; nothing was removed.'); + // The notice says why (the saved pair) and counts the prompt's negative variants (live note on #470). + expect(cat.variant_count).toBeGreaterThan(1); + await expect(notice.locator('p').first()).toHaveText(`This prompt is archived because it is in a saved pair. It and its ${cat.variant_count} negative variants are hidden from the Prompt library; nothing was removed.`); await notice.getByRole('button',{name:'Restore this prompt to the Prompt library'}).click(); await expect(toast(page)).toHaveText('Restored 1 prompt to the Prompt library.'); await expect(notice).toHaveCount(0); diff --git a/tests/js/workspace-library-delete.test.js b/tests/js/workspace-library-delete.test.js index ecbe429a..b80eb68e 100644 --- a/tests/js/workspace-library-delete.test.js +++ b/tests/js/workspace-library-delete.test.js @@ -136,3 +136,15 @@ test('reviewOf keeps only well-formed ids and counts from an answer', async () = assert.deepEqual(review, {epoch: null, delete: [1, 2], archive: [], protected: [], alreadyArchived: 0, notFound: 0, imageLinks: 2, backup: 'on', disk: null}); }); + +test('the archived notice says why (the saved pair) and names negative variants only when there are any', async () => { + const {archivedNoticeText} = await source; + assert.equal(archivedNoticeText(0), 'This prompt is archived because it is in a saved pair. It is hidden from the Prompt ' + + 'library; nothing was removed.'); + assert.equal(archivedNoticeText(1), 'This prompt is archived because it is in a saved pair. It and its 1 negative variant ' + + 'are hidden from the Prompt library; nothing was removed.'); + assert.equal(archivedNoticeText(3), 'This prompt is archived because it is in a saved pair. It and its 3 negative variants ' + + 'are hidden from the Prompt library; nothing was removed.'); + // An unknown count (an older server) never claims variants. + assert.equal(archivedNoticeText(undefined), archivedNoticeText(0)); +}); diff --git a/web/js/workspace/library-delete.mjs b/web/js/workspace/library-delete.mjs index e552d31d..51de4868 100644 --- a/web/js/workspace/library-delete.mjs +++ b/web/js/workspace/library-delete.mjs @@ -28,7 +28,6 @@ export const COPY = Object.freeze({ restoreStorage: 'The library was restored or moved since this page loaded. No prompt was returned to the Library.', restoreUnconfirmed: 'Returning the prompts could not be confirmed. The Library was reloaded.', restoreFailed: 'The prompts could not be returned to the Library. Try again.', - archivedNotice: 'This prompt and its negative variants are hidden from the Prompt library; nothing was removed.', archivedText: 'This text is an archived prompt. Restore it to show it in the Prompt library again.', }); @@ -61,6 +60,14 @@ export function protectedNames(review, titleOf) { return more > 0 ? `${shown}${shown ? ' and ' : ''}${more.toLocaleString('en-US')} more` : shown; } +/** An archived prompt's page notice: why it is archived, and its negative variants only when it has any. */ +export function archivedNoticeText(variantCount) { + const hidden = Number.isSafeInteger(variantCount) && variantCount > 0 + ? `It and its ${countOf(variantCount, 'negative variant')} are hidden` + : 'It is hidden'; + return `This prompt is archived because it is in a saved pair. ${hidden} from the Prompt library; nothing was removed.`; +} + /** The review's `[term, value]` rows (design §7.2); rows that do not apply are left out. */ export function reviewGrid(review, titleOf = () => null) { const rows = [ diff --git a/web/js/workspace/library.mjs b/web/js/workspace/library.mjs index f36dff03..98f989b2 100644 --- a/web/js/workspace/library.mjs +++ b/web/js/workspace/library.mjs @@ -17,7 +17,7 @@ import {libraryFilters,libraryViewState} from './saved-views.mjs'; import {openRefineDialog,openContentDialog} from './workspace-dialogs.mjs'; import {mountLibrarySelection} from './library-selection.mjs'; import {fallBackToOriginal,replaceOnError,showsThumbnail,thumbnailSource} from './thumbnail-support.mjs'; -import {COPY as DELETE_COPY,RESTORE_URL,restoreOutcome} from './library-delete.mjs'; +import {COPY as DELETE_COPY,RESTORE_URL,archivedNoticeText,restoreOutcome} from './library-delete.mjs'; let shelfVisit=null; // Every confirmed receipt advances this; a shelf mounted before it never restores its cached stream. @@ -521,7 +521,7 @@ export function mountLibrary(main,api,parentScope,{positiveId,savedState=null,on const notice=el('div',undefined,'notice library-archived-notice'),copy=el('div'),row=el('div',undefined,'row-actions'); const status=el('p','','small');status.setAttribute('role','status');status.hidden=true; const restore=createButton('Restore');restore.setAttribute('aria-label','Restore this prompt to the Prompt library'); - copy.append(el('h2','Archived'),el('p',DELETE_COPY.archivedNotice),status);row.append(restore);notice.append(copy,row); + copy.append(el('h2','Archived'),el('p',archivedNoticeText(shown.positive.variant_count)),status);row.append(restore);notice.append(copy,row); content.listen(restore,'click',async()=>{ if(restore.getAttribute('aria-disabled')==='true') return; restore.setAttribute('aria-disabled','true');status.hidden=true;