fix(library): act on a loaded prompt only, keep focus on a gone page, close the menu on focus loss (#512)
Review fixes: Edit metadata and the More menu are disabled while the prompt loads or failed to load; on a gone prompt's page a focused control hands focus to Open the Prompt library; the menu closes when focus leaves it. A unit pins that a paired prompt with a missing digest is not called another prompt's negative.
This commit is contained in:
@@ -318,6 +318,9 @@ test('a negative used by another prompt is named as such when it is archived', a
|
||||
"that prompt's variant stays. To bring them back, press Show archived prompts, select them and press Restore."]);
|
||||
const mixed = reviewOf(preview({archive: [2, 5], deletable: [2], review_digests: {2: 'c'.repeat(64)}}));
|
||||
assert.deepEqual(reviewGrid(mixed)[0], ['Archive', "2 prompts in a saved pair (1 used as another prompt's negative): hidden, restorable"]);
|
||||
// A paired prompt whose review digest is missing is not called another prompt's negative (review M1).
|
||||
const noDigest = reviewOf(preview({archive: [2], deletable: [2], review_digests: {}}));
|
||||
assert.deepEqual(reviewGrid(noDigest), [['Archive', '1 prompt in a saved pair: hidden, restorable']]);
|
||||
const sentences = reviewSentences(mixed);
|
||||
assert.match(sentences[0], /^Prompts in a saved pair, as its positive or its negative, are archived instead of deleted/);
|
||||
assert.match(sentences[1], /^Prompts used as another prompt's negative are archived instead of deleted, so that prompt's variant stays\.$/);
|
||||
|
||||
@@ -67,6 +67,7 @@ function pairedOf(preview) {
|
||||
variants: count(counts.variants), keptNegatives: count(counts.kept_negatives) },
|
||||
pairedImageLinks: count(preview?.paired_image_links), choice: 'archive',
|
||||
// #512: archive ids the server says cannot be deleted (another prompt's negative); none from an older answer.
|
||||
// The server's own list, not the digest-checked one: a deletable prompt with a bad digest is still paired.
|
||||
archiveOnly: Array.isArray(preview?.deletable)
|
||||
? [...archive].filter(id => !ids(preview.deletable).includes(id)) : [],
|
||||
};
|
||||
|
||||
@@ -581,12 +581,16 @@ export function mountLibrary(main,api,parentScope,{positiveId,savedState=null,on
|
||||
contentScope?.dispose();contentScope=createScope();detail=null;updateBranches=null;updateParent=null;current=null;send.disabled=true;
|
||||
blocks?.dispose();blocks=null;galleryScope?.dispose();galleryScope=null;body.replaceChildren();
|
||||
// #512: a prompt that no longer exists (deleted, or a stale link) says so, with the way back; no Retry.
|
||||
// Edit metadata and ⋯ act on a loaded prompt only (review M2).
|
||||
const hadFocus=[edit,moreMenu.button].includes(document.activeElement);
|
||||
const loaded=!state.loading && !state.error;edit.disabled=!loaded;moreMenu.setDisabled(!loaded);
|
||||
if(state.error?.status===404) {
|
||||
edit.disabled=true;moreMenu.setDisabled(true);
|
||||
const message=el('p',PROMPT_GONE,'library-status');message.setAttribute('role','status');body.append(message);
|
||||
const open=createButton('Open the Prompt library');contentScope.listen(open,'click',()=>{window.location.hash='#/library';});body.append(open);return;
|
||||
const open=createButton('Open the Prompt library');contentScope.listen(open,'click',()=>{window.location.hash='#/library';});body.append(open);
|
||||
// A focused control that was just disabled hands focus to the way back (review M3).
|
||||
if(hadFocus) open.focus({preventScroll:true});
|
||||
return;
|
||||
}
|
||||
edit.disabled=false;moreMenu.setDisabled(false);
|
||||
if(state.loading || state.error) {
|
||||
const message=el('p',state.loading?'Loading prompt history…':'Prompt history could not be loaded.','library-status');message.setAttribute('role',state.error?'alert':'status');body.append(message);
|
||||
if(state.error) {const retry=createButton('Retry prompt history');contentScope.listen(retry,'click',()=>loader.load(positiveId));body.append(retry);}return;
|
||||
|
||||
@@ -71,6 +71,8 @@ export function createMoreMenu({ scope, label, items }) {
|
||||
event.preventDefault();
|
||||
keys[event.key]();
|
||||
});
|
||||
// Focus leaving the menu (a screen reader's cursor, a script) closes it too.
|
||||
scope.listen(element, 'focusout', event => { if (event.relatedTarget && !element.contains(event.relatedTarget)) close(false); });
|
||||
// An outside press closes it; capture, so a press that a page control stops still counts.
|
||||
const outside = event => { if (isOpen() && !element.contains(event.target)) close(false); };
|
||||
document.addEventListener('pointerdown', outside, true);
|
||||
|
||||
Reference in New Issue
Block a user