b264dac2e8d1bea9ff7ef7cd6c61d3c19145dc98
593
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b264dac2e8 | fix(images): start no image thread after the teardown; join retired job pools (#523) | ||
|
|
77f9f09443 |
perf(images): run image work on two own pools and trim after jobs (#523)
Image work no longer spreads over the default executor's threads, where glibc kept an image-sized malloc arena per thread. - Interactive image work (view previews behind their gate, workspace card facts, both inspector routes) runs on the API's fixed pm-image pool of 3 threads; the on-demand preview pool is merged into it. - map_parallel (scan metadata, thumbnail and convert jobs, the legacy gallery generation, capture previews) shares one long-lived pm-worker pool of Worker threads, reused between jobs. The package teardown stops it within its bound (part "workers"). - After a scan, thumbnail or convert job, glibc's malloc_trim(0) returns free pages once; a no-op on Windows, macOS and musl. - tools/perf/memory_drive.py measures RSS, heap, threads and arenas under the #523 load on the disposable browser test server (manual only). |
||
|
|
d92b2eabd9 | test(images): cover the text budget, XMP and layout guard of the PNG text walk (#523) | ||
|
|
e5868d96f2 |
perf(images): read PNG text without decoding pixels (#523)
Pillow's PngImageFile.text loads every pixel to reach text chunks written after IDAT. The scan's metadata batch and the image monitor now read PNG text through utils/png_text.py, which runs Pillow's own chunk handlers after the first IDAT and seeks over the pixel data. The legacy .text read is the floor: a differential test compares both readers on every fixture image, a curated corpus and a seeded fuzz. The only difference is a file whose pixel data is undecodable or cut short: it now keeps the text read before its pixels instead of reading nothing. Animated PNGs still use .text. |
||
|
|
e29c23de8d |
chore(init): print a one-line startup banner (#574)
Replace the version line, one Loaded line per node and the total with "[ComfyUI-PromptManager] Version: <v> - <n> tools loaded". Closes #573 |
||
|
|
b82e68a9d0 |
ci: stop testing macOS in CI (#572)
macOS is not a popular ComfyUI platform. Drop the macOS legs from the portability workflow and its native-trash proof; Windows keeps its Python 3.12-3.14 legs. The macOS code stays, and README calls its support best-effort. Closes #571 |
||
|
|
fb02066cda |
test(vocabulary): open the long-list check as a new page, not a reload (#567) (#570)
Firefox restores the scroll position of a reloaded page's tag window, so its end scrolled into view and appended a second page before the 100-row check. Scrolling the window to its top before the reload did not always stick on CI. A fresh navigation has no position to restore. |
||
|
|
ab17818792 |
fix(system): show the Images page count in System Overview (#568)
System > Overview's Images metric read the overview's indexed generated_images rows, while the workspace Overview card and the Images sidebar badge show the Images page's own total, which also lists files no row links (110 against 122 on the demo library). The concept's System metric and sidebar badge are both `images.length`, so the metric now reads the shell's Images count: one source for all three, a dash until it is read or when its read failed, re-read on each visit. The Statistics dialog's "Library totals" were the legacy /stats raw row counts under the same nouns; they now read "Database records" with "Prompt records", "Tags on prompts" and "Image records". /stats and the legacy admin page are unchanged. Closes #555 |
||
|
|
ef97d01dbd |
ci: run frontend coverage in four shards (#565)
* ci: run frontend coverage in four shards Coverage shards reached their 18-minute step limit three times on 2026-10-31 with every test passing. Split the run into four shards with the limits unchanged; the merge job now expects four raw coverage artifacts, and the vendor checks move to shard 4. Closes #563 * test(tags): pin the 390px window check to the first page's last row Reaching the last row loads the next page (D183), and last() then followed row 200, far outside the window, so the WebKit check failed in 4-5 of 20 runs on 2 CPUs. Pinned to row 100, it passed 90 of 90 across WebKit, Chromium and Firefox. Closes #566 |
||
|
|
b7a43755ae |
chore: release 3.4.1 (#564)
Fixes the Windows exit hang with watchdog 7 (#562), stops LoRA scan errors from showing server details (#560), and carries the code-quality sweep (#558).v3.4.1 |
||
|
|
2c7e84dedd |
fix(security): send a generic LoRA scan error to the browser (#560)
An unexpected exception's text could reach the page in the LoRA scan's error frame, including a path or a model name. Those failures now send a fixed message and keep the detail in the server log. Expected refusals keep their wording, and an OSError still shows its reason and the file's basename. Refs #552 |
||
|
|
cc65d15d14 |
fix(deps): keep watchdog below 7 (#562)
watchdog 7.0.0 (released 2026-10-31) makes the package's exit tests hang on Windows on Python 3.12 to 3.14; the same commit passed with 6.0.0. Cap it until observer.stop() is bounded at exit. Refs #561 |
||
|
|
d88cf9e7c6 |
fix(quality): sweep the Code Quality standard findings (#558)
* fix(quality): preserve cursor iteration and option validation Keep prefetched rows on ManagedCursor so partial loops and explicit fetches share one position, and stop existing iterators after close or retirement. Preserve chunked reads and native SQLite result lifetime on reexecution and errors. Catch OptionError before ValueError so legacy auto-tag requests receive the actual validation error. Remove the overwritten ViewerJS title option. Regression tests failed before the fixes for lost rows, reads after close/retirement, and misleading option errors. Focused Python checks pass; cursor checks also pass on Python 3.13 and 3.14. Refs #321. * fix(quality): make the test doubles legal raises and hashes The fake queue no longer raises an attribute that starts as None. The oversized kind records hashes instead of raising from __hash__, which also catches a hash that production code swallows. Hostile.__bool__ raises the TypeError a truth test may raise. * fix(quality): drop the extra saveCoverage argument * refactor(quality): remove assignments that are overwritten before use * refactor(quality): remove the remaining overwritten test assignments * refactor(quality): drop imports repeated inside functions * refactor(quality): give duplicate identity keys one shape * refactor(quality): name the unused job event loop variable * refactor(quality): release model gates in finally, not except BaseException * refactor(quality): end the job runners with an explicit return * refactor(quality): use the precise unittest asserts * refactor(quality): import unittest.mock one way in the tests * fix(quality): say why each empty except is safe, log the broad ones * refactor(quality): stop binding values the tests never read * refactor(quality): remove the unused preview aliases and tag page query * refactor(quality): remove re-exports nothing imports * refactor(quality): parenthesize deliberate string joins in lists * refactor(quality): remove unused JavaScript variables and imports * refactor(quality): use the precise assert in the model status test |
||
|
|
df70b5dfd6 |
docs: new README as the product page, and release bump to 3.4.0 (#545)
* docs: new README as the product page, keep the old one as README-legacy Rename README.md to README-legacy.md with a short note saying what it is, and write a new README: hero, visual feature tour, all six nodes, then install, data locations, upgrade notes, legacy pages, limits and testing. Images are compressed WebP in assets/readme. Point the TESTING.md link at the section's new home. * docs: retake README screenshots, add shortcuts, pinned bar and trash limits * chore: release 3.4.0v3.4.0 |
||
|
|
7e1847955b |
fix(viewer): destroy a pending viewer whose element left the stage
rebuild() empties the stage while a view is pending, and the deferred destroy then failed on the viewer's null parent: a page error, and the image kept its src. The destroy now finishes and the src is released. |
||
|
|
ecb6b09813 |
test(viewer): wait for the viewer's first fit before reading it
The keys spec read the fit as soon as Zoom enabled, a frame before the viewer fits the image to the stage. Under load it then compared 873.6px with 901.2px. Failed 3 of 3 under --cpus=2 on WebKit, on the combined head and on #534's own head; with the wait the spec passes 96 of 96. |
||
|
|
cdb791d4a9 |
fix(nodes): Pair node fills the prompt's empty category and tags (#551)
Closes #546 |
||
|
|
f924d7195d |
fix(workspace): Overview cards show the sidebar's counts (#549)
Closes #547 |
||
|
|
3789d85e58 | docs(testing): record D190, the trash review primary that cannot act | ||
|
|
fb6452934f |
test(trash): classify the Needs review link route and prove it refuses a trashed file
POST /review/items/{id}/link needs a capture of the item's bytes. A capture is refused for a path in a trash folder, and a capture taken earlier stops working when its file moves to a trash folder or its folder becomes a link to one, so no code change is needed. The decision route takes the same path, so it moves to the same audit group.
|
||
|
|
395dc74cbe |
fix(trash): trash on an output folder that is a mount point (#543) (#544)
Closes #543 |
||
|
|
2f061904a2 | test(workspace): wait for the delete job before its end toast in the sticky chrome spec | ||
|
|
f80e67c812 |
feat(workspace): selection bars stay in view, Back to top, side panels stay with you (#531) (#541)
Closes #531 |
||
|
|
ab6fee3a82 |
fix(viewer): arrow keys navigate from anywhere; a shortcuts overlay (#532) (#534)
Closes #532 |
||
|
|
f53a43152d |
feat(review): link an image to a library prompt from Needs review (#536) (#540)
Closes #536 |
||
|
|
fe63f01214 |
fix(send): name ComfyUI windows by their tab and explain why a send is blocked (#539)
Closes #535 |
||
|
|
aab0fedc10 |
fix(workspace): level two-column cards, window the Reusable tags list, full-width System (#530) (#538)
Closes #530 |
||
|
|
085e45e475 |
fix(images): show every Show in folder outcome above the viewer and report opener failures (#537)
Closes #533 |
||
|
|
89572e03c6 |
test(images): wait for the delete and return jobs, not 5 s (#554)
Closes #553 |
||
|
|
ae3e191990 |
feat(workspace): show the one-time older-images notice in the shell (#529)
* feat(workspace): show the one-time older-images notice in the shell After the switch everyone lands on Library, so someone who has not dismissed the notice might never be prompted. It now shows above every workspace page until dismissed, sharing pm_update_notice_dismissed with System > Sources and the admin modal. Browser specs start with it dismissed. Closes #527 * test(workspace): turn the older-images notice on where specs expect it Two System specs read the Sources notice from empty storage, and the new spec read a job list earlier specs had filled. |
||
|
|
cd284432f5 |
feat(cutover): every entry point opens the workspace; legacy pages stay reachable (#528)
* feat(cutover): every entry point opens the workspace; legacy pages stay reachable The top-bar PM button, the nodes' Web UI buttons and /prompt_manager/web now open the workspace Library. Overview's cards open their native pages. The legacy pages stay at their URLs, with no route changed. Classic Web UI no longer navigates the canvas away when a popup is blocked; it shows a notification, as the focused node does. Closes #525 * feat(cutover): link the legacy pages from System and when the workspace fails to load System's Overview section ends with one small Legacy pages line (D165). If a workspace module fails to load or throws while starting, the page shows a static message with the same links instead of an empty shell. Part of #525 * test(cutover): prove the Pair node's Web UI on the real host Part of #525 * test(cutover): count the new real-host cases in the stock matrix pin The PM button test and three renderer-and-reload tests, in 2 projects, take the stock matrix from 110 to 118. Part of #525 |
||
|
|
a6d6129ec6 |
fix(library): keep the prompt page heading on one row; no history subtitle on a deleted prompt's page (#524) (#526)
* fix(library): keep the prompt page heading on one row; no history subtitle on a deleted prompt's page (#524) From 951px the heading actions keep one row and a long title clamps to two lines beside them (D164). A deleted prompt's page hides the Prompt history description. * fix(library): drop the CSS file name from a comment the consumer check reads (#524) |
||
|
|
e8d96a0f55 |
fix(library): delete review and prompt page polish from the live test, and a More menu on the prompt page (#512) (#522)
* feat(library): set the category on many selected prompts (#437)
The Prompt library shelf gets the concept's Images selection checkbox on each
card and its .bulkbar (N selected, Set category, Clear), recorded as D148.
Set category opens the shared one-value dialog and posts
{prompt_ids, category, expected_epoch} to POST /prompt_manager/bulk/category:
the server checks the storage epoch the shelf was read under, then sets the
category on the Library parents in one BEGIN IMMEDIATE transaction, writing
only rows that change. A restored storage answers 409 storage-changed and
writes nothing. The legacy admin and images_filter bodies are unchanged.
This replaces the legacy admin page's bulk Set Category, a cutover
prerequisite (#334).
Closes #437
* feat(library): add tags to many selected prompts and select every loaded prompt (#442)
The Library selection bar (D148) gets Add tags and select-all of the loaded
shelf, the legacy admin page's bulk Add Tags and select-all checkbox.
Add tags posts {prompt_ids, tags, expected_epoch} to
POST /prompt_manager/bulk/tags: in one BEGIN IMMEDIATE transaction the server
checks the storage epoch, normalizes the names as image tags are, gives each
Library parent only the names it lacks (casefold), finds or inserts tag rows
by exact name only when some prompt gets them, and skips prompts that would
pass 20 tags. Prompt tags only. Both bulk/tags forms refuse a non-object body
with 400.
Ctrl/Cmd+A in the grid and "Select all N loaded" in the bar select every
loaded prompt; the offer follows each loaded batch.
Closes #442
* feat(vocabulary): tag search, tag sort and an Untagged prompts entry (#444)
Tags & categories gains a server-side tag search and sort (keyset pages,
cursor scoped to the query) and an "Untagged prompts" row whose Browse opens
the Library with tag_mode=untagged. Refine this view gets Tag match, saved
Library views keep it, and the routes echo the applied query so a stale
server is an error, never an unfiltered list. Recorded as D149.
Refs #444
* feat(library): filter prompts by output folder and browse tags with Any (#444)
Refine this view on the Prompt library gains the concept's Folder (folders
below an output root, read when the dialog opens) and a Tag match select
(All / Any). The listing takes folder and tag_mode, matches folders on the
normalized file_path under every spelling of each output root, echoes both
so a stale server is an error, and saved Library views keep them. Recorded
as D150.
Refs #444
* docs(library): record Add tags and select-all as D151 (#442)
* feat(library): archive paired and delete unpaired selected prompts on the server (#443)
A prompt_archive table marks Library parents hidden from the shelf; older
builds ignore it. New routes /prompt_manager/library/delete/preview,
/library/delete and /library/restore classify a selection (delete,
archive, protected, already archived, not found), execute exactly what was
reviewed under a records hold, take a verified safety backup (disk checked)
before any delete, and restore archived prompts. The shelf, search and the
shelf-facing counts honor the archive (?shelf=active|archived); archiving
repairs a stale prompt hash so scans find the archived row.
* feat(library): Delete… on the selection bar, Show archived prompts and Restore (#443)
The Library bar's Delete… previews the selection and opens the shared
review: what is deleted (in no saved pair), archived (paired: hidden,
restorable) or kept (tagged __protected__), the safety backup and what
cannot be undone. Show archived prompts (N) lists archived prompts with
Restore on their bar; an archived prompt's page shows an Archived notice
with Restore. TESTING.md records D152; the route snapshot gains the three
workspace routes.
* test(library): cover the Delete… review failure and close-only paths; record coverage floors (#443)
* fix(library): let saved-view defaults fill the missing #444 filters
libraryViewState passed `?? 'all'` and `?? ''` fallbacks that
normalizeViewState already applies to an undefined value. The redundant
branches were never taken in the page, which dropped saved-views.mjs below
its 95% branch floor. A unit now covers a shelf without the new filters.
Refs #444
* fix(library): lay out a loaded page of prompts without waiting for an animation frame (#443)
A starved renderer (headless WebKit under CPU load, a background tab) can
hold requestAnimationFrame for seconds. The shelf laid out each loaded page
only in its next frame, so the grid stayed empty after the first load or a
delete's reload while the caption already showed the new count. A loaded
batch is now laid out at once; measurements still flush per frame.
The regression test holds every animation frame and fails before the fix.
* test(saved-views): give the D153 Library view the tag_mode key (#444)
The Library saved-view state now holds tag_mode, and the API requires
every key, so main's new "Highest rated" spec posts it too.
Refs #444
* test(saved-views): give the D153 Library view the folder key (#444)
Refs #444
* fix(library): name the saved pair, not negative variants, as the archive reason (#443)
A prompt in a saved pair with no negative variants was described in the
Delete… review as "1 prompt with negative variants: hidden, restorable".
Paired prompts are archived because of the pair, so the Archive row now
reads "N prompts in a saved pair: hidden, restorable" and the first
sentence explains the pair. Found in the live test of #470.
* fix(library): nest Refine folders by path segment; backslash separates only on Windows (#444)
Live test B1: the Folder list indented a server list sorted as full-path
text, so "-" and "_" sorted before "/" and children landed under the wrong
parent (kiko/char under kiko-v6), with case variants interleaved. The
options are now a tree built by segment: each folder directly under its own
parent, case variants distinct siblings.
On POSIX a folder named "Metadata\Klein..." was listed but filtered to no
prompts, because normalize_folder read "\" as a separator. It now does so
only on Windows (ntpath), as the listing already did.
Refs #444
* fix(library): say why an archived prompt is archived, and name variants only when it has any (#443)
The archived prompt page's notice always said "This prompt and its
negative variants are hidden", also for a saved-pair prompt with none.
It now says the prompt is archived because it is in a saved pair, and
counts its negative variants only when there are any. From the live
test of #470.
* feat(library): delete a prompt in a saved pair permanently, with its own pairs (#482, server)
The Delete review's preview now marks which archive ids may be deleted
permanently (in a saved pair, and nobody else's negative), with a review
digest of the prompt and its pairs and the counts the review shows. The
write accepts an optional delete_paired_ids with their digests. Each is
deleted with its own saved pairs (image-pair links, then pairs, then the
prompt as #443 deletes it) only while its digest is unchanged. Any drift
archives it and counts it changed. Paired deletes are backed up and
disk-checked like deletes.
* feat(library): ask Archive or Delete permanently for prompts in a saved pair (#482, review)
The Delete… review asks, for prompts in a saved pair, Archive (checked
on every open, D152's review) or Delete permanently, with their saved
pairs, in a D90 radio row. The choice rewrites the grid, sentences, title
and primary in place (openReviewDialog setRows/setPrimary), and the body
carries the chosen ids with their review digests. On the archived shelf,
Show archived prompts now reads the concept's way back, "← Prompt
library", as a plain button. TESTING.md records D154.
* docs(testing): number #482's deviation D160 (D154-D159 are taken)
* Revert "docs(testing): number #482's deviation D160 (D154-D159 are taken)"
This reverts commit
|
||
|
|
d0134b2d13 |
feat(library): delete archived prompts from the archived shelf (#492) (#509)
* feat(library): set the category on many selected prompts (#437)
The Prompt library shelf gets the concept's Images selection checkbox on each
card and its .bulkbar (N selected, Set category, Clear), recorded as D148.
Set category opens the shared one-value dialog and posts
{prompt_ids, category, expected_epoch} to POST /prompt_manager/bulk/category:
the server checks the storage epoch the shelf was read under, then sets the
category on the Library parents in one BEGIN IMMEDIATE transaction, writing
only rows that change. A restored storage answers 409 storage-changed and
writes nothing. The legacy admin and images_filter bodies are unchanged.
This replaces the legacy admin page's bulk Set Category, a cutover
prerequisite (#334).
Closes #437
* feat(library): add tags to many selected prompts and select every loaded prompt (#442)
The Library selection bar (D148) gets Add tags and select-all of the loaded
shelf, the legacy admin page's bulk Add Tags and select-all checkbox.
Add tags posts {prompt_ids, tags, expected_epoch} to
POST /prompt_manager/bulk/tags: in one BEGIN IMMEDIATE transaction the server
checks the storage epoch, normalizes the names as image tags are, gives each
Library parent only the names it lacks (casefold), finds or inserts tag rows
by exact name only when some prompt gets them, and skips prompts that would
pass 20 tags. Prompt tags only. Both bulk/tags forms refuse a non-object body
with 400.
Ctrl/Cmd+A in the grid and "Select all N loaded" in the bar select every
loaded prompt; the offer follows each loaded batch.
Closes #442
* feat(vocabulary): tag search, tag sort and an Untagged prompts entry (#444)
Tags & categories gains a server-side tag search and sort (keyset pages,
cursor scoped to the query) and an "Untagged prompts" row whose Browse opens
the Library with tag_mode=untagged. Refine this view gets Tag match, saved
Library views keep it, and the routes echo the applied query so a stale
server is an error, never an unfiltered list. Recorded as D149.
Refs #444
* feat(library): filter prompts by output folder and browse tags with Any (#444)
Refine this view on the Prompt library gains the concept's Folder (folders
below an output root, read when the dialog opens) and a Tag match select
(All / Any). The listing takes folder and tag_mode, matches folders on the
normalized file_path under every spelling of each output root, echoes both
so a stale server is an error, and saved Library views keep them. Recorded
as D150.
Refs #444
* docs(library): record Add tags and select-all as D151 (#442)
* feat(library): archive paired and delete unpaired selected prompts on the server (#443)
A prompt_archive table marks Library parents hidden from the shelf; older
builds ignore it. New routes /prompt_manager/library/delete/preview,
/library/delete and /library/restore classify a selection (delete,
archive, protected, already archived, not found), execute exactly what was
reviewed under a records hold, take a verified safety backup (disk checked)
before any delete, and restore archived prompts. The shelf, search and the
shelf-facing counts honor the archive (?shelf=active|archived); archiving
repairs a stale prompt hash so scans find the archived row.
* feat(library): Delete… on the selection bar, Show archived prompts and Restore (#443)
The Library bar's Delete… previews the selection and opens the shared
review: what is deleted (in no saved pair), archived (paired: hidden,
restorable) or kept (tagged __protected__), the safety backup and what
cannot be undone. Show archived prompts (N) lists archived prompts with
Restore on their bar; an archived prompt's page shows an Archived notice
with Restore. TESTING.md records D152; the route snapshot gains the three
workspace routes.
* test(library): cover the Delete… review failure and close-only paths; record coverage floors (#443)
* fix(library): let saved-view defaults fill the missing #444 filters
libraryViewState passed `?? 'all'` and `?? ''` fallbacks that
normalizeViewState already applies to an undefined value. The redundant
branches were never taken in the page, which dropped saved-views.mjs below
its 95% branch floor. A unit now covers a shelf without the new filters.
Refs #444
* fix(library): lay out a loaded page of prompts without waiting for an animation frame (#443)
A starved renderer (headless WebKit under CPU load, a background tab) can
hold requestAnimationFrame for seconds. The shelf laid out each loaded page
only in its next frame, so the grid stayed empty after the first load or a
delete's reload while the caption already showed the new count. A loaded
batch is now laid out at once; measurements still flush per frame.
The regression test holds every animation frame and fails before the fix.
* test(saved-views): give the D153 Library view the tag_mode key (#444)
The Library saved-view state now holds tag_mode, and the API requires
every key, so main's new "Highest rated" spec posts it too.
Refs #444
* test(saved-views): give the D153 Library view the folder key (#444)
Refs #444
* fix(library): name the saved pair, not negative variants, as the archive reason (#443)
A prompt in a saved pair with no negative variants was described in the
Delete… review as "1 prompt with negative variants: hidden, restorable".
Paired prompts are archived because of the pair, so the Archive row now
reads "N prompts in a saved pair: hidden, restorable" and the first
sentence explains the pair. Found in the live test of #470.
* fix(library): nest Refine folders by path segment; backslash separates only on Windows (#444)
Live test B1: the Folder list indented a server list sorted as full-path
text, so "-" and "_" sorted before "/" and children landed under the wrong
parent (kiko/char under kiko-v6), with case variants interleaved. The
options are now a tree built by segment: each folder directly under its own
parent, case variants distinct siblings.
On POSIX a folder named "Metadata\Klein..." was listed but filtered to no
prompts, because normalize_folder read "\" as a separator. It now does so
only on Windows (ntpath), as the listing already did.
Refs #444
* fix(library): say why an archived prompt is archived, and name variants only when it has any (#443)
The archived prompt page's notice always said "This prompt and its
negative variants are hidden", also for a saved-pair prompt with none.
It now says the prompt is archived because it is in a saved pair, and
counts its negative variants only when there are any. From the live
test of #470.
* feat(library): delete a prompt in a saved pair permanently, with its own pairs (#482, server)
The Delete review's preview now marks which archive ids may be deleted
permanently (in a saved pair, and nobody else's negative), with a review
digest of the prompt and its pairs and the counts the review shows. The
write accepts an optional delete_paired_ids with their digests. Each is
deleted with its own saved pairs (image-pair links, then pairs, then the
prompt as #443 deletes it) only while its digest is unchanged. Any drift
archives it and counts it changed. Paired deletes are backed up and
disk-checked like deletes.
* feat(library): ask Archive or Delete permanently for prompts in a saved pair (#482, review)
The Delete… review asks, for prompts in a saved pair, Archive (checked
on every open, D152's review) or Delete permanently, with their saved
pairs, in a D90 radio row. The choice rewrites the grid, sentences, title
and primary in place (openReviewDialog setRows/setPrimary), and the body
carries the chosen ids with their review digests. On the archived shelf,
Show archived prompts now reads the concept's way back, "← Prompt
library", as a plain button. TESTING.md records D154.
* docs(testing): number #482's deviation D160 (D154-D159 are taken)
* Revert "docs(testing): number #482's deviation D160 (D154-D159 are taken)"
This reverts commit
|
||
|
|
9af3271dbb |
feat(images): Show removed images and Return to library (#498) (#514)
* fix(images): background jobs skip files hidden by the removal mark; walkers skip trash folders inside output roots (#501) - Thumbnail jobs and the legacy scan_output_dir listing leave library-removed files out (one mark check, mark_hides). - Missing-file links also forgets removal marks that hide nothing any more (file gone from a folder that exists, or changed), only under a readable, non-empty source, re-checked at apply. - The sidebar Images, Favorites and Needs review counts and the collection counts are read again when an image_delete job ends. - utils/media_folders.py: one skip for thumbnails and trash folders (.Trash, .Trash-<uid>, .Trashes, $RECYCLE.BIN) used by every walker, the image monitor and the delete review. * feat(images): Show removed images and Return to library (#498) - GET /images/output?mode=workspace&removed=1 lists only files a removal mark hides; every workspace page carries removed_total. - POST /images/removed/return {items | filter} starts a scan job, "Return images to the library": the marks that still hide their file go in one transaction, then exactly those files are scanned, so their prompt links come back from their metadata. Tags, categories, collections and favorites do not come back. - The marks generation moves again after the commit, so a reader that cached the marks mid-transaction reloads them. - Images: Show removed images (N) heading toggle (D162), Removed chips, a bar with only Return to library and Clear, Return to library in the side panel; the view is never saved or restored. * test(images): record the return route in the legacy contract snapshot; removed_total in the workspace keys (#498) The scan runner passes returned only when set, so a stand-in events function without that keyword still runs. * test(browser): wait for the answers two specs raced in the merge queue (#501) - System diagnostics "level filter": wait for each level's own log answer, and poll the shown levels; the CRITICAL line is in the previous level's answer too, so it proved nothing (firefox). - Library editor "restored storage after a confirmed save": wait for the PATCH answer, which waits for a whole restore in the test's route, instead of the 5 s expect default (webkit). * fix(monitor): skip trash folders only below the watched folder (#501 review) A source named .Trash-1000 (or another trash name) was ignored by the image monitor while every walker still listed it; the monitor now tests the folders below the longest watched folder holding the new file. * fix(images): no default removedView function in the selection bar (#498) The default arrow was never called, so images-selection.mjs fell under its function coverage floor (97.29 < 98). * fix(images): a category or tags chosen in Show removed images ends the view (#498 review) The toolbar's Category and Refine's Tags stayed usable in the removed view, and the listing then refused the combination with a page error. Choosing one now leaves the view, as a removed file has neither. |
||
|
|
98ddded73c |
feat(library): Delete prompt… on a prompt's page (#481) (#507)
* feat(library): set the category on many selected prompts (#437)
The Prompt library shelf gets the concept's Images selection checkbox on each
card and its .bulkbar (N selected, Set category, Clear), recorded as D148.
Set category opens the shared one-value dialog and posts
{prompt_ids, category, expected_epoch} to POST /prompt_manager/bulk/category:
the server checks the storage epoch the shelf was read under, then sets the
category on the Library parents in one BEGIN IMMEDIATE transaction, writing
only rows that change. A restored storage answers 409 storage-changed and
writes nothing. The legacy admin and images_filter bodies are unchanged.
This replaces the legacy admin page's bulk Set Category, a cutover
prerequisite (#334).
Closes #437
* feat(library): add tags to many selected prompts and select every loaded prompt (#442)
The Library selection bar (D148) gets Add tags and select-all of the loaded
shelf, the legacy admin page's bulk Add Tags and select-all checkbox.
Add tags posts {prompt_ids, tags, expected_epoch} to
POST /prompt_manager/bulk/tags: in one BEGIN IMMEDIATE transaction the server
checks the storage epoch, normalizes the names as image tags are, gives each
Library parent only the names it lacks (casefold), finds or inserts tag rows
by exact name only when some prompt gets them, and skips prompts that would
pass 20 tags. Prompt tags only. Both bulk/tags forms refuse a non-object body
with 400.
Ctrl/Cmd+A in the grid and "Select all N loaded" in the bar select every
loaded prompt; the offer follows each loaded batch.
Closes #442
* feat(vocabulary): tag search, tag sort and an Untagged prompts entry (#444)
Tags & categories gains a server-side tag search and sort (keyset pages,
cursor scoped to the query) and an "Untagged prompts" row whose Browse opens
the Library with tag_mode=untagged. Refine this view gets Tag match, saved
Library views keep it, and the routes echo the applied query so a stale
server is an error, never an unfiltered list. Recorded as D149.
Refs #444
* feat(library): filter prompts by output folder and browse tags with Any (#444)
Refine this view on the Prompt library gains the concept's Folder (folders
below an output root, read when the dialog opens) and a Tag match select
(All / Any). The listing takes folder and tag_mode, matches folders on the
normalized file_path under every spelling of each output root, echoes both
so a stale server is an error, and saved Library views keep them. Recorded
as D150.
Refs #444
* docs(library): record Add tags and select-all as D151 (#442)
* feat(library): archive paired and delete unpaired selected prompts on the server (#443)
A prompt_archive table marks Library parents hidden from the shelf; older
builds ignore it. New routes /prompt_manager/library/delete/preview,
/library/delete and /library/restore classify a selection (delete,
archive, protected, already archived, not found), execute exactly what was
reviewed under a records hold, take a verified safety backup (disk checked)
before any delete, and restore archived prompts. The shelf, search and the
shelf-facing counts honor the archive (?shelf=active|archived); archiving
repairs a stale prompt hash so scans find the archived row.
* feat(library): Delete… on the selection bar, Show archived prompts and Restore (#443)
The Library bar's Delete… previews the selection and opens the shared
review: what is deleted (in no saved pair), archived (paired: hidden,
restorable) or kept (tagged __protected__), the safety backup and what
cannot be undone. Show archived prompts (N) lists archived prompts with
Restore on their bar; an archived prompt's page shows an Archived notice
with Restore. TESTING.md records D152; the route snapshot gains the three
workspace routes.
* test(library): cover the Delete… review failure and close-only paths; record coverage floors (#443)
* fix(library): let saved-view defaults fill the missing #444 filters
libraryViewState passed `?? 'all'` and `?? ''` fallbacks that
normalizeViewState already applies to an undefined value. The redundant
branches were never taken in the page, which dropped saved-views.mjs below
its 95% branch floor. A unit now covers a shelf without the new filters.
Refs #444
* fix(library): lay out a loaded page of prompts without waiting for an animation frame (#443)
A starved renderer (headless WebKit under CPU load, a background tab) can
hold requestAnimationFrame for seconds. The shelf laid out each loaded page
only in its next frame, so the grid stayed empty after the first load or a
delete's reload while the caption already showed the new count. A loaded
batch is now laid out at once; measurements still flush per frame.
The regression test holds every animation frame and fails before the fix.
* test(saved-views): give the D153 Library view the tag_mode key (#444)
The Library saved-view state now holds tag_mode, and the API requires
every key, so main's new "Highest rated" spec posts it too.
Refs #444
* test(saved-views): give the D153 Library view the folder key (#444)
Refs #444
* fix(library): name the saved pair, not negative variants, as the archive reason (#443)
A prompt in a saved pair with no negative variants was described in the
Delete… review as "1 prompt with negative variants: hidden, restorable".
Paired prompts are archived because of the pair, so the Archive row now
reads "N prompts in a saved pair: hidden, restorable" and the first
sentence explains the pair. Found in the live test of #470.
* fix(library): nest Refine folders by path segment; backslash separates only on Windows (#444)
Live test B1: the Folder list indented a server list sorted as full-path
text, so "-" and "_" sorted before "/" and children landed under the wrong
parent (kiko/char under kiko-v6), with case variants interleaved. The
options are now a tree built by segment: each folder directly under its own
parent, case variants distinct siblings.
On POSIX a folder named "Metadata\Klein..." was listed but filtered to no
prompts, because normalize_folder read "\" as a separator. It now does so
only on Windows (ntpath), as the listing already did.
Refs #444
* fix(library): say why an archived prompt is archived, and name variants only when it has any (#443)
The archived prompt page's notice always said "This prompt and its
negative variants are hidden", also for a saved-pair prompt with none.
It now says the prompt is archived because it is in a saved pair, and
counts its negative variants only when there are any. From the live
test of #470.
* feat(library): delete a prompt in a saved pair permanently, with its own pairs (#482, server)
The Delete review's preview now marks which archive ids may be deleted
permanently (in a saved pair, and nobody else's negative), with a review
digest of the prompt and its pairs and the counts the review shows. The
write accepts an optional delete_paired_ids with their digests. Each is
deleted with its own saved pairs (image-pair links, then pairs, then the
prompt as #443 deletes it) only while its digest is unchanged. Any drift
archives it and counts it changed. Paired deletes are backed up and
disk-checked like deletes.
* feat(library): ask Archive or Delete permanently for prompts in a saved pair (#482, review)
The Delete… review asks, for prompts in a saved pair, Archive (checked
on every open, D152's review) or Delete permanently, with their saved
pairs, in a D90 radio row. The choice rewrites the grid, sentences, title
and primary in place (openReviewDialog setRows/setPrimary), and the body
carries the chosen ids with their review digests. On the archived shelf,
Show archived prompts now reads the concept's way back, "← Prompt
library", as a plain button. TESTING.md records D154.
* docs(testing): number #482's deviation D160 (D154-D159 are taken)
* Revert "docs(testing): number #482's deviation D160 (D154-D159 are taken)"
This reverts commit
|
||
|
|
abd26bb275 |
test(collections): wait for the restore job, not 5 s; no hard-coded saved-view id (#520) (#521)
The restore test waited 5 s for a whole restore. Under load (webkit, merge queue of #514) the restore outlasted it and kept running into the next tests on the worker, replacing their database. It now waits for the restore job's event stream to end, as the backups spec does. The route-change test used saved view 5, which a worker can have made. |
||
|
|
b12a9a14ba |
feat(library): delete a prompt in a saved pair permanently, with its own saved pairs (#482) (#505)
* feat(library): set the category on many selected prompts (#437)
The Prompt library shelf gets the concept's Images selection checkbox on each
card and its .bulkbar (N selected, Set category, Clear), recorded as D148.
Set category opens the shared one-value dialog and posts
{prompt_ids, category, expected_epoch} to POST /prompt_manager/bulk/category:
the server checks the storage epoch the shelf was read under, then sets the
category on the Library parents in one BEGIN IMMEDIATE transaction, writing
only rows that change. A restored storage answers 409 storage-changed and
writes nothing. The legacy admin and images_filter bodies are unchanged.
This replaces the legacy admin page's bulk Set Category, a cutover
prerequisite (#334).
Closes #437
* feat(library): add tags to many selected prompts and select every loaded prompt (#442)
The Library selection bar (D148) gets Add tags and select-all of the loaded
shelf, the legacy admin page's bulk Add Tags and select-all checkbox.
Add tags posts {prompt_ids, tags, expected_epoch} to
POST /prompt_manager/bulk/tags: in one BEGIN IMMEDIATE transaction the server
checks the storage epoch, normalizes the names as image tags are, gives each
Library parent only the names it lacks (casefold), finds or inserts tag rows
by exact name only when some prompt gets them, and skips prompts that would
pass 20 tags. Prompt tags only. Both bulk/tags forms refuse a non-object body
with 400.
Ctrl/Cmd+A in the grid and "Select all N loaded" in the bar select every
loaded prompt; the offer follows each loaded batch.
Closes #442
* feat(vocabulary): tag search, tag sort and an Untagged prompts entry (#444)
Tags & categories gains a server-side tag search and sort (keyset pages,
cursor scoped to the query) and an "Untagged prompts" row whose Browse opens
the Library with tag_mode=untagged. Refine this view gets Tag match, saved
Library views keep it, and the routes echo the applied query so a stale
server is an error, never an unfiltered list. Recorded as D149.
Refs #444
* feat(library): filter prompts by output folder and browse tags with Any (#444)
Refine this view on the Prompt library gains the concept's Folder (folders
below an output root, read when the dialog opens) and a Tag match select
(All / Any). The listing takes folder and tag_mode, matches folders on the
normalized file_path under every spelling of each output root, echoes both
so a stale server is an error, and saved Library views keep them. Recorded
as D150.
Refs #444
* docs(library): record Add tags and select-all as D151 (#442)
* feat(library): archive paired and delete unpaired selected prompts on the server (#443)
A prompt_archive table marks Library parents hidden from the shelf; older
builds ignore it. New routes /prompt_manager/library/delete/preview,
/library/delete and /library/restore classify a selection (delete,
archive, protected, already archived, not found), execute exactly what was
reviewed under a records hold, take a verified safety backup (disk checked)
before any delete, and restore archived prompts. The shelf, search and the
shelf-facing counts honor the archive (?shelf=active|archived); archiving
repairs a stale prompt hash so scans find the archived row.
* feat(library): Delete… on the selection bar, Show archived prompts and Restore (#443)
The Library bar's Delete… previews the selection and opens the shared
review: what is deleted (in no saved pair), archived (paired: hidden,
restorable) or kept (tagged __protected__), the safety backup and what
cannot be undone. Show archived prompts (N) lists archived prompts with
Restore on their bar; an archived prompt's page shows an Archived notice
with Restore. TESTING.md records D152; the route snapshot gains the three
workspace routes.
* test(library): cover the Delete… review failure and close-only paths; record coverage floors (#443)
* fix(library): let saved-view defaults fill the missing #444 filters
libraryViewState passed `?? 'all'` and `?? ''` fallbacks that
normalizeViewState already applies to an undefined value. The redundant
branches were never taken in the page, which dropped saved-views.mjs below
its 95% branch floor. A unit now covers a shelf without the new filters.
Refs #444
* fix(library): lay out a loaded page of prompts without waiting for an animation frame (#443)
A starved renderer (headless WebKit under CPU load, a background tab) can
hold requestAnimationFrame for seconds. The shelf laid out each loaded page
only in its next frame, so the grid stayed empty after the first load or a
delete's reload while the caption already showed the new count. A loaded
batch is now laid out at once; measurements still flush per frame.
The regression test holds every animation frame and fails before the fix.
* test(saved-views): give the D153 Library view the tag_mode key (#444)
The Library saved-view state now holds tag_mode, and the API requires
every key, so main's new "Highest rated" spec posts it too.
Refs #444
* test(saved-views): give the D153 Library view the folder key (#444)
Refs #444
* fix(library): name the saved pair, not negative variants, as the archive reason (#443)
A prompt in a saved pair with no negative variants was described in the
Delete… review as "1 prompt with negative variants: hidden, restorable".
Paired prompts are archived because of the pair, so the Archive row now
reads "N prompts in a saved pair: hidden, restorable" and the first
sentence explains the pair. Found in the live test of #470.
* fix(library): nest Refine folders by path segment; backslash separates only on Windows (#444)
Live test B1: the Folder list indented a server list sorted as full-path
text, so "-" and "_" sorted before "/" and children landed under the wrong
parent (kiko/char under kiko-v6), with case variants interleaved. The
options are now a tree built by segment: each folder directly under its own
parent, case variants distinct siblings.
On POSIX a folder named "Metadata\Klein..." was listed but filtered to no
prompts, because normalize_folder read "\" as a separator. It now does so
only on Windows (ntpath), as the listing already did.
Refs #444
* fix(library): say why an archived prompt is archived, and name variants only when it has any (#443)
The archived prompt page's notice always said "This prompt and its
negative variants are hidden", also for a saved-pair prompt with none.
It now says the prompt is archived because it is in a saved pair, and
counts its negative variants only when there are any. From the live
test of #470.
* feat(library): delete a prompt in a saved pair permanently, with its own pairs (#482, server)
The Delete review's preview now marks which archive ids may be deleted
permanently (in a saved pair, and nobody else's negative), with a review
digest of the prompt and its pairs and the counts the review shows. The
write accepts an optional delete_paired_ids with their digests. Each is
deleted with its own saved pairs (image-pair links, then pairs, then the
prompt as #443 deletes it) only while its digest is unchanged. Any drift
archives it and counts it changed. Paired deletes are backed up and
disk-checked like deletes.
* feat(library): ask Archive or Delete permanently for prompts in a saved pair (#482, review)
The Delete… review asks, for prompts in a saved pair, Archive (checked
on every open, D152's review) or Delete permanently, with their saved
pairs, in a D90 radio row. The choice rewrites the grid, sentences, title
and primary in place (openReviewDialog setRows/setPrimary), and the body
carries the chosen ids with their review digests. On the archived shelf,
Show archived prompts now reads the concept's way back, "← Prompt
library", as a plain button. TESTING.md records D154.
* docs(testing): number #482's deviation D160 (D154-D159 are taken)
* Revert "docs(testing): number #482's deviation D160 (D154-D159 are taken)"
This reverts commit
|
||
|
|
bb744550f4 |
fix(tests): pin glibc's mmap threshold in the browser test server (#516) (#519)
Each tests/browser/server.py grew from about 13 MB to 0.6-1 GB RSS over a page-workflow shard while its Python heap stayed near 12 MB. glibc raises its mmap threshold (up to 32 MiB) after a large free, so the seeder's image-sized buffers came from per-thread malloc arenas, which fragmented and kept their pages. The server now fixes the threshold at glibc's 128 KiB start value (mallopt M_MMAP_THRESHOLD) before it starts, so large blocks go back to the OS on free. No memory limit is set; off glibc it does nothing. Closes #516 |
||
|
|
2650621a1b |
test(thumbnails): store the job id before the cancel test's first write cancels (#504) (#506)
The patched writer cancelled job_ids[0] after the first preview, but the id was stored only after the POST response was read. A fast worker wrote first, hit IndexError, cancelled nothing and the job finished. The first write now waits for the id on an Event the test sets after storing it. |
||
|
|
d6371b7fad |
feat(images): move deleted image files to the macOS Trash (#499) (#503)
* feat(images): move deleted image files to the macOS Trash (#499) Trash mode on macOS now calls -[NSFileManager trashItemAtURL:resultingItemURL:error:] through ctypes (no dependency), in utils/trash_macos.py. Before any move it refuses what it cannot show to be recoverable, already in the review dialog: network volumes, read-only volumes, iCloud Drive and File Provider folders (cloud-sync), a trash folder inside an output root (by file identity), and files macOS would refuse for permission (no-permission). A refused move keeps the file and logs the NSError; a move macOS reports done but whose place cannot be shown goes under errors as trash-unconfirmed, keeps its records and stops the job. The native proof runs in portability.yml's new native-trash job on macos-latest and macos-15-intel (PM_NATIVE_TRASH=macos). Windows still reports no-trash (#500). * test(trash): resolve the temp folder and wait for a busy image to be released (#499) The permission tests keyed their lstat fakes by the unresolved temp path, but the backend resolves paths (/var -> /private/var on macOS, 8.3 short names on Windows), so on those runners the fakes never matched (portability run 40954618922; reproduced on Linux with a symlinked TMPDIR). The native test's detach first proves this process holds nothing under the mount, then retries the plain detach until macOS releases a volume it has just written a trash folder to (macos-15-intel), and still fails after a forced detach. * test(trash): count a volume that unmounted after a busy detach as detached (#499) On macos-15-intel a detach that reported "Resource busy" finished afterwards, so the retry found no such volume (portability run 40957282805). The loop now checks os.path.ismount before each attempt. |
||
|
|
75cd9c6e13 |
test(browser): record each worker's browser log and attach it when a target closes (#513) (#518)
A browser that died between two tests left the next one only 'Target page, context or browser has been closed': Playwright logs a browser's stderr and exit only on the pw:browser debug channel, which CI leaves off. Each worker now keeps that channel as a bounded tail, prints one pm-browser-exit| line on an abnormal exit and attaches the tail as browser-stderr.txt to a test that fails on a closed or crashed target. Nothing is retried or hidden. Refs #513 |
||
|
|
77f08782f4 |
fix(tagging): keep tags typed before the image observation answers (#515) (#517)
The Tag image field is editable while the dialog observes the file, but the
observation's answer overwrote it with the saved set, so typing then Save
wrote the old tags ("0 tags saved"). The controller now reads the draft when
the answer arrives: text typed during a first observation (or before Retry
of a failed one) follows the saved tags, and a kept draft takes the text
typed while the reopen observes.
|
||
|
|
62815c9e3e |
fix(tests): close the twin database before the bulk-delete temp dir goes (#510) (#511)
tearDown removed the temp folder before the test's addCleanup closed twin.db, so Windows refused the delete (WinError 32). Register the folder and main database cleanups in setUp so they run after every cleanup a test adds. |
||
|
|
a1ec65442d |
fix(images): say plainly why Windows offers no Recycle Bin (#500) (#508)
Maintainer ruling (a): Windows never uses the Recycle Bin, because it deletes files larger than the bin's capacity permanently and IFileOperation's PreDeleteItem guard cannot detect it (native probe runs 40951037575, 40953347904). Each file in the Delete review now reads "Moving files to the Recycle Bin isn't available on Windows: files larger than the bin's capacity would be deleted permanently." (code windows-recycle) instead of "This drive has no trash.", and the summary line drops its "yet": "Moving to the trash is not available for these files; each one lists why." |
||
|
|
e52cc39cd5 |
fix(images): background jobs skip files hidden by the removal mark; walkers skip trash folders inside output roots (#501) (#502)
* fix(images): background jobs skip files hidden by the removal mark; walkers skip trash folders inside output roots (#501) - Thumbnail jobs and the legacy scan_output_dir listing leave library-removed files out (one mark check, mark_hides). - Missing-file links also forgets removal marks that hide nothing any more (file gone from a folder that exists, or changed), only under a readable, non-empty source, re-checked at apply. - The sidebar Images, Favorites and Needs review counts and the collection counts are read again when an image_delete job ends. - utils/media_folders.py: one skip for thumbnails and trash folders (.Trash, .Trash-<uid>, .Trashes, $RECYCLE.BIN) used by every walker, the image monitor and the delete review. * test(browser): wait for the answers two specs raced in the merge queue (#501) - System diagnostics "level filter": wait for each level's own log answer, and poll the shown levels; the CRITICAL line is in the previous level's answer too, so it proved nothing (firefox). - Library editor "restored storage after a confirmed save": wait for the PATCH answer, which waits for a whole restore in the test's route, instead of the 5 s expect default (webkit). |
||
|
|
eff6ece6a4 |
test(autotag): hide installed ML backends from the autotag tests (#485)
Five autotag tests assumed onnxruntime and llama_cpp were absent and failed in a venv that has them. The import check now runs in a fresh interpreter, and the missing-package tests hide onnxruntime with a None sys.modules entry, so they pass with or without the packages. Closes #472 |
||
|
|
d02a911748 |
feat(library): delete or archive selected prompts, Show archived prompts and Restore (#443) (#470)
* feat(library): set the category on many selected prompts (#437) The Prompt library shelf gets the concept's Images selection checkbox on each card and its .bulkbar (N selected, Set category, Clear), recorded as D148. Set category opens the shared one-value dialog and posts {prompt_ids, category, expected_epoch} to POST /prompt_manager/bulk/category: the server checks the storage epoch the shelf was read under, then sets the category on the Library parents in one BEGIN IMMEDIATE transaction, writing only rows that change. A restored storage answers 409 storage-changed and writes nothing. The legacy admin and images_filter bodies are unchanged. This replaces the legacy admin page's bulk Set Category, a cutover prerequisite (#334). Closes #437 * feat(library): add tags to many selected prompts and select every loaded prompt (#442) The Library selection bar (D148) gets Add tags and select-all of the loaded shelf, the legacy admin page's bulk Add Tags and select-all checkbox. Add tags posts {prompt_ids, tags, expected_epoch} to POST /prompt_manager/bulk/tags: in one BEGIN IMMEDIATE transaction the server checks the storage epoch, normalizes the names as image tags are, gives each Library parent only the names it lacks (casefold), finds or inserts tag rows by exact name only when some prompt gets them, and skips prompts that would pass 20 tags. Prompt tags only. Both bulk/tags forms refuse a non-object body with 400. Ctrl/Cmd+A in the grid and "Select all N loaded" in the bar select every loaded prompt; the offer follows each loaded batch. Closes #442 * feat(vocabulary): tag search, tag sort and an Untagged prompts entry (#444) Tags & categories gains a server-side tag search and sort (keyset pages, cursor scoped to the query) and an "Untagged prompts" row whose Browse opens the Library with tag_mode=untagged. Refine this view gets Tag match, saved Library views keep it, and the routes echo the applied query so a stale server is an error, never an unfiltered list. Recorded as D149. Refs #444 * feat(library): filter prompts by output folder and browse tags with Any (#444) Refine this view on the Prompt library gains the concept's Folder (folders below an output root, read when the dialog opens) and a Tag match select (All / Any). The listing takes folder and tag_mode, matches folders on the normalized file_path under every spelling of each output root, echoes both so a stale server is an error, and saved Library views keep them. Recorded as D150. Refs #444 * docs(library): record Add tags and select-all as D151 (#442) * feat(library): archive paired and delete unpaired selected prompts on the server (#443) A prompt_archive table marks Library parents hidden from the shelf; older builds ignore it. New routes /prompt_manager/library/delete/preview, /library/delete and /library/restore classify a selection (delete, archive, protected, already archived, not found), execute exactly what was reviewed under a records hold, take a verified safety backup (disk checked) before any delete, and restore archived prompts. The shelf, search and the shelf-facing counts honor the archive (?shelf=active|archived); archiving repairs a stale prompt hash so scans find the archived row. * feat(library): Delete… on the selection bar, Show archived prompts and Restore (#443) The Library bar's Delete… previews the selection and opens the shared review: what is deleted (in no saved pair), archived (paired: hidden, restorable) or kept (tagged __protected__), the safety backup and what cannot be undone. Show archived prompts (N) lists archived prompts with Restore on their bar; an archived prompt's page shows an Archived notice with Restore. TESTING.md records D152; the route snapshot gains the three workspace routes. * test(library): cover the Delete… review failure and close-only paths; record coverage floors (#443) * fix(library): let saved-view defaults fill the missing #444 filters libraryViewState passed `?? 'all'` and `?? ''` fallbacks that normalizeViewState already applies to an undefined value. The redundant branches were never taken in the page, which dropped saved-views.mjs below its 95% branch floor. A unit now covers a shelf without the new filters. Refs #444 * fix(library): lay out a loaded page of prompts without waiting for an animation frame (#443) A starved renderer (headless WebKit under CPU load, a background tab) can hold requestAnimationFrame for seconds. The shelf laid out each loaded page only in its next frame, so the grid stayed empty after the first load or a delete's reload while the caption already showed the new count. A loaded batch is now laid out at once; measurements still flush per frame. The regression test holds every animation frame and fails before the fix. * test(saved-views): give the D153 Library view the tag_mode key (#444) The Library saved-view state now holds tag_mode, and the API requires every key, so main's new "Highest rated" spec posts it too. Refs #444 * test(saved-views): give the D153 Library view the folder key (#444) Refs #444 * fix(library): name the saved pair, not negative variants, as the archive reason (#443) A prompt in a saved pair with no negative variants was described in the Delete… review as "1 prompt with negative variants: hidden, restorable". Paired prompts are archived because of the pair, so the Archive row now reads "N prompts in a saved pair: hidden, restorable" and the first sentence explains the pair. Found in the live test of #470. * fix(library): nest Refine folders by path segment; backslash separates only on Windows (#444) Live test B1: the Folder list indented a server list sorted as full-path text, so "-" and "_" sorted before "/" and children landed under the wrong parent (kiko/char under kiko-v6), with case variants interleaved. The options are now a tree built by segment: each folder directly under its own parent, case variants distinct siblings. On POSIX a folder named "Metadata\Klein..." was listed but filtered to no prompts, because normalize_folder read "\" as a separator. It now does so only on Windows (ntpath), as the listing already did. Refs #444 * fix(library): say why an archived prompt is archived, and name variants only when it has any (#443) The archived prompt page's notice always said "This prompt and its negative variants are hidden", also for a saved-pair prompt with none. It now says the prompt is archived because it is in a saved pair, and counts its negative variants only when there are any. From the live test of #470. |
||
|
|
bc3f09d844 |
ci(portability): set the job limit from three green runs after #487 (#493) (#497)
Main runs 40935337099, 40938407252 and 40941240624 (
|
||
|
|
e0b86712a8 |
fix(workspace): start no sidebar count read while the page is leaving (#494) (#496)
The Favorites count is re-read 300 ms after a star changes. A reload
inside that window started the read on the leaving page, which WebKit
fails ("access control checks") and reports as a page error. After
beforeunload, the workspace clears that timer, aborts count reads in
flight and defers new ones, as well as a finished restore's reload. A
page that stays reads them on its next input or when it becomes visible,
as the legacy admin page does.
|