* feat(saved-views): keep a collection in a saved view Images saved views gain a collection key (#331 D140): empty or a collection id of 1 to 9 digits. Writes refuse favorites together with a collection; older rows read an empty collection, and a stored row with both opens the collection, as the listing does for a URL with both. Part of #331 * feat(collections): store collections in the library database * feat(collections): add, remove and rename collections and keep members through duplicate cleanup * feat(collections): add collection routes and the Images collection filter * feat(images): store images' own categories in the library database (#332) One new table, image_categories, and its category index, created empty by a forward-only migration that reads and writes only its own objects. Startup checks the structure; restore also checks the values. The store sets, clears and reads own categories by file key (the deepest output source holding the file and the path below it), and copies a removed duplicate's category to a kept copy that has none. * feat(images): duplicate cleanup keeps images' own categories (#332) The kept copy's own category wins; a kept copy without one receives the removed copy's, keyed by the deepest output source holding each file. The removed copy's row is never deleted, so a give-back or a failed delete loses nothing. The job log reports how many were kept. * feat(images): set, remove and list images' own categories (#332) PUT /prompt_manager/images/category sets the own category of up to 500 listed files, DELETE removes it (listed or not), and GET /prompt_manager/images/categories lists the values whose file is listed. Targets are the Images listing identity of an output source, checked by media_target (shared with Collections). The routes never touch prompts, recipes or links; POST /prompt_manager/bulk/category is unchanged. * fix(images): key symlinked output folders and keep key work off the event loop (#332) Review fixes: a source matches by its configured spelling too, so a detected output folder that is a symlink keys its files (routes and duplicate cleanup). File keys are computed in the executor. A path no source holds is a plain 403, a lone surrogate a 400. The install repairs a table that lacks its category index instead of refusing to open. * fix(collections): address review findings * feat(images): set or remove a category on all N matching images (#332) The category routes also accept the Images listing filter of "Select all N matching" (#392) instead of explicit items, resolved now and capped at 500 images like an explicit selection. * feat(collections): browse a collection from the sidebar The sidebar gains the concept's COLLECTIONS group (D136): one row per collection with its folder icon, name and listed-member count, without the PROPOSED badge; the label shows once a collection exists. A row opens #/collection/<id>, the Images page filtered to that collection (D137): the collection's name as h1, Collection as breadcrumb and title, Edit to rename or delete it through the saved views' Edit dialog with collection nouns, and a note counting members the sources do not list now. An unknown or deleted collection shows a page that says so with a link to Images. A saved view on a deleted collection lists nothing and says so (MC6, D140). The list is read again after a restore and the counts 300 ms after a change. Part of #331 * test(collections): compare the COLLECTIONS group with the concept The concept hides COLLECTIONS at 950 px and below, which would leave a phone no way to open a collection; production shows the rows there in the wrapped layout of every other sidebar nav (D136). The shell comparison now compares the group instead of filtering it out: the concept fixture seeds Landscapes (12) and Experiments (8), the rows compare by name order, and at phone width the nav compares against the concept's own sidebar-nav layout. The collection rows are checked at 1600x1100 and 390x844 in both themes. Part of #331 * test(vendor): count the #332 host case in the execution matrix image-attribution.spec.mjs gains the image category route case, so the execution config lists 92 tests (3 attribution cases per project). * test(browser): install the slideshow clock before wrapping setTimeout The slideshow fixture wrapped window.setTimeout before page.clock.install(), so the viewer's timer ran on real time and clock.runFor() did not drive it; a late WebKit timer failed 'slideshow exposes exact state labels' on CI. * fix(collections): address review findings - A list read superseded by a newer one (the 300 ms count read, a restore) no longer shows "could not be loaded": the collection and saved-view pages wait for the list to settle. - A listing that finds the open collection gone shows the deleted page once the list no longer holds it; when the list read fails or still holds it, the page keeps the listing's error and Retry instead of staying blank, and each failed read is reported again. - The missing-members note follows every list read and goes away at zero. - A rename that settles after its Edit dialog closed leaves any dialog opened since alone. - The deleted-collection, list-failed and saved-view failure pages focus their title after a route change, as every page does. Part of #331 * test(coverage): keep main's floors for files this PR does not touch coverage:update raised nine floors of untouched files from one run; metadata-autocomplete.js branches then measured 84.84 against 85. * test(legacy): record the image category routes in the legacy contract snapshot (#332) #332 adds PUT/DELETE /prompt_manager/images/category and GET /prompt_manager/images/categories; the legacy pages' routes are unchanged. * test(duplicates): a removed copy with a collection and a category moves both (#331, #332)
657 lines
12 KiB
JSON
657 lines
12 KiB
JSON
{
|
|
"admin.js": {
|
|
"lines": 37,
|
|
"branches": 61,
|
|
"functions": 32,
|
|
"statements": 37
|
|
},
|
|
"api-client.js": {
|
|
"lines": 98,
|
|
"branches": 96,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"cache-ownership.js": {
|
|
"lines": 94,
|
|
"branches": 89,
|
|
"functions": 98,
|
|
"statements": 94
|
|
},
|
|
"classic-node.js": {
|
|
"lines": 98,
|
|
"branches": 79,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"comfy-metadata.js": {
|
|
"lines": 97,
|
|
"branches": 92,
|
|
"functions": 98,
|
|
"statements": 97
|
|
},
|
|
"data-actions.js": {
|
|
"lines": 98,
|
|
"branches": 98,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"focused-node.js": {
|
|
"lines": 97,
|
|
"branches": 87,
|
|
"functions": 90,
|
|
"statements": 97
|
|
},
|
|
"gallery-settings.js": {
|
|
"lines": 98,
|
|
"branches": 94,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"gallery.js": {
|
|
"lines": 49,
|
|
"branches": 68,
|
|
"functions": 50,
|
|
"statements": 49
|
|
},
|
|
"html-escape.js": {
|
|
"lines": 98,
|
|
"branches": 98,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"image-helpers.js": {
|
|
"lines": 96,
|
|
"branches": 91,
|
|
"functions": 98,
|
|
"statements": 96
|
|
},
|
|
"list-state.js": {
|
|
"lines": 98,
|
|
"branches": 92,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"metadata-autocomplete.js": {
|
|
"lines": 98,
|
|
"branches": 82,
|
|
"functions": 85,
|
|
"statements": 98
|
|
},
|
|
"png-metadata.js": {
|
|
"lines": 98,
|
|
"branches": 96,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"prompt-bridge-adapter.js": {
|
|
"lines": 98,
|
|
"branches": 93,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"prompt-bridge-host.js": {
|
|
"lines": 98,
|
|
"branches": 98,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"prompt-library.js": {
|
|
"lines": 98,
|
|
"branches": 94,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"prompt-list-sort.js": {
|
|
"lines": 98,
|
|
"branches": 98,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"save-to-library.js": {
|
|
"lines": 98,
|
|
"branches": 98,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"scan-progress.js": {
|
|
"lines": 98,
|
|
"branches": 98,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"shared/toolkit/core.js": {
|
|
"lines": 98,
|
|
"branches": 96,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"shared/toolkit/prompt-bridge.js": {
|
|
"lines": 98,
|
|
"branches": 94,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"shared/toolkit/suggestions.js": {
|
|
"lines": 98,
|
|
"branches": 98,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"sse-stream.js": {
|
|
"lines": 98,
|
|
"branches": 92,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"tag-autocomplete.js": {
|
|
"lines": 98,
|
|
"branches": 94,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"tags-page.js": {
|
|
"lines": 57,
|
|
"branches": 48,
|
|
"functions": 56,
|
|
"statements": 57
|
|
},
|
|
"text-links.js": {
|
|
"lines": 98,
|
|
"branches": 98,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"theme-bridge.js": {
|
|
"lines": 90,
|
|
"branches": 53,
|
|
"functions": 98,
|
|
"statements": 90
|
|
},
|
|
"view-router.js": {
|
|
"lines": 98,
|
|
"branches": 98,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/activity-job-view.mjs": {
|
|
"lines": 97,
|
|
"branches": 87,
|
|
"functions": 98,
|
|
"statements": 97
|
|
},
|
|
"workspace/activity-kinds.mjs": {
|
|
"lines": 98,
|
|
"branches": 95,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/activity-scan.mjs": {
|
|
"lines": 96,
|
|
"branches": 90,
|
|
"functions": 98,
|
|
"statements": 96
|
|
},
|
|
"workspace/activity-store.mjs": {
|
|
"lines": 98,
|
|
"branches": 96,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/activity.mjs": {
|
|
"lines": 96,
|
|
"branches": 91,
|
|
"functions": 98,
|
|
"statements": 96
|
|
},
|
|
"workspace/collections.mjs": {
|
|
"lines": 98,
|
|
"branches": 95,
|
|
"functions": 95,
|
|
"statements": 98
|
|
},
|
|
"workspace/file-inspector.mjs": {
|
|
"lines": 96,
|
|
"branches": 93,
|
|
"functions": 93,
|
|
"statements": 96
|
|
},
|
|
"workspace/heading.mjs": {
|
|
"lines": 98,
|
|
"branches": 98,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/history-media.mjs": {
|
|
"lines": 98,
|
|
"branches": 86,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/history-state.mjs": {
|
|
"lines": 97,
|
|
"branches": 91,
|
|
"functions": 98,
|
|
"statements": 97
|
|
},
|
|
"workspace/history.mjs": {
|
|
"lines": 97,
|
|
"branches": 87,
|
|
"functions": 98,
|
|
"statements": 97
|
|
},
|
|
"workspace/icons.mjs": {
|
|
"lines": 98,
|
|
"branches": 98,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/image-card.mjs": {
|
|
"lines": 98,
|
|
"branches": 98,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/image-layout.mjs": {
|
|
"lines": 98,
|
|
"branches": 98,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/image-tagging-state.mjs": {
|
|
"lines": 98,
|
|
"branches": 94,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/image-tagging.mjs": {
|
|
"lines": 97,
|
|
"branches": 92,
|
|
"functions": 88,
|
|
"statements": 97
|
|
},
|
|
"workspace/images-category.mjs": {
|
|
"lines": 98,
|
|
"branches": 98,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/images-export.mjs": {
|
|
"lines": 98,
|
|
"branches": 98,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/images-favorites.mjs": {
|
|
"lines": 98,
|
|
"branches": 94,
|
|
"functions": 85,
|
|
"statements": 98
|
|
},
|
|
"workspace/images-multiselect.mjs": {
|
|
"lines": 98,
|
|
"branches": 98,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/images-selection.mjs": {
|
|
"lines": 98,
|
|
"branches": 93,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/images-state.mjs": {
|
|
"lines": 98,
|
|
"branches": 98,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/images-tagging-bulk.mjs": {
|
|
"lines": 98,
|
|
"branches": 93,
|
|
"functions": 86,
|
|
"statements": 98
|
|
},
|
|
"workspace/images-tagging-review.mjs": {
|
|
"lines": 98,
|
|
"branches": 93,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/images-tagging.mjs": {
|
|
"lines": 98,
|
|
"branches": 98,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/images.mjs": {
|
|
"lines": 97,
|
|
"branches": 89,
|
|
"functions": 98,
|
|
"statements": 97
|
|
},
|
|
"workspace/index.mjs": {
|
|
"lines": 98,
|
|
"branches": 97,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/inspect.mjs": {
|
|
"lines": 98,
|
|
"branches": 92,
|
|
"functions": 93,
|
|
"statements": 98
|
|
},
|
|
"workspace/inspection-state.mjs": {
|
|
"lines": 98,
|
|
"branches": 94,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/library-editor-state.mjs": {
|
|
"lines": 96,
|
|
"branches": 92,
|
|
"functions": 98,
|
|
"statements": 96
|
|
},
|
|
"workspace/library-editor.mjs": {
|
|
"lines": 98,
|
|
"branches": 93,
|
|
"functions": 92,
|
|
"statements": 98
|
|
},
|
|
"workspace/library-layout.mjs": {
|
|
"lines": 98,
|
|
"branches": 98,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/library-location.mjs": {
|
|
"lines": 94,
|
|
"branches": 91,
|
|
"functions": 93,
|
|
"statements": 94
|
|
},
|
|
"workspace/library-state.mjs": {
|
|
"lines": 98,
|
|
"branches": 95,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/library.mjs": {
|
|
"lines": 96,
|
|
"branches": 85,
|
|
"functions": 96,
|
|
"statements": 96
|
|
},
|
|
"workspace/local-inspection.mjs": {
|
|
"lines": 98,
|
|
"branches": 91,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/local-parameters.mjs": {
|
|
"lines": 98,
|
|
"branches": 94,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/local-projection.mjs": {
|
|
"lines": 97,
|
|
"branches": 96,
|
|
"functions": 98,
|
|
"statements": 97
|
|
},
|
|
"workspace/local-resolver-stages.mjs": {
|
|
"lines": 95,
|
|
"branches": 93,
|
|
"functions": 98,
|
|
"statements": 95
|
|
},
|
|
"workspace/local-resolver-support.mjs": {
|
|
"lines": 96,
|
|
"branches": 81,
|
|
"functions": 98,
|
|
"statements": 96
|
|
},
|
|
"workspace/local-resolver.mjs": {
|
|
"lines": 96,
|
|
"branches": 86,
|
|
"functions": 98,
|
|
"statements": 96
|
|
},
|
|
"workspace/local-semantics.mjs": {
|
|
"lines": 98,
|
|
"branches": 98,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/local-session.mjs": {
|
|
"lines": 98,
|
|
"branches": 92,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/local-video.mjs": {
|
|
"lines": 98,
|
|
"branches": 94,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/maintenance-review.mjs": {
|
|
"lines": 95,
|
|
"branches": 87,
|
|
"functions": 91,
|
|
"statements": 95
|
|
},
|
|
"workspace/media-viewer.mjs": {
|
|
"lines": 98,
|
|
"branches": 93,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/plural.mjs": {
|
|
"lines": 98,
|
|
"branches": 98,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/preferences.mjs": {
|
|
"lines": 98,
|
|
"branches": 96,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/prompt-blocks.mjs": {
|
|
"lines": 98,
|
|
"branches": 95,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/prompt-inspector.mjs": {
|
|
"lines": 98,
|
|
"branches": 98,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/prompt-sender.mjs": {
|
|
"lines": 98,
|
|
"branches": 91,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/py-json.mjs": {
|
|
"lines": 97,
|
|
"branches": 92,
|
|
"functions": 98,
|
|
"statements": 97
|
|
},
|
|
"workspace/recorded-run.mjs": {
|
|
"lines": 95,
|
|
"branches": 83,
|
|
"functions": 80,
|
|
"statements": 95
|
|
},
|
|
"workspace/review-state.mjs": {
|
|
"lines": 96,
|
|
"branches": 84,
|
|
"functions": 98,
|
|
"statements": 96
|
|
},
|
|
"workspace/review.mjs": {
|
|
"lines": 98,
|
|
"branches": 89,
|
|
"functions": 93,
|
|
"statements": 98
|
|
},
|
|
"workspace/saved-views.mjs": {
|
|
"lines": 98,
|
|
"branches": 95,
|
|
"functions": 95,
|
|
"statements": 98
|
|
},
|
|
"workspace/state.mjs": {
|
|
"lines": 98,
|
|
"branches": 98,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/system-appearance.mjs": {
|
|
"lines": 98,
|
|
"branches": 93,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/system-backups.mjs": {
|
|
"lines": 95,
|
|
"branches": 88,
|
|
"functions": 94,
|
|
"statements": 95
|
|
},
|
|
"workspace/system-controls.mjs": {
|
|
"lines": 98,
|
|
"branches": 89,
|
|
"functions": 89,
|
|
"statements": 98
|
|
},
|
|
"workspace/system-diagnostics.mjs": {
|
|
"lines": 95,
|
|
"branches": 86,
|
|
"functions": 94,
|
|
"statements": 95
|
|
},
|
|
"workspace/system-integrations.mjs": {
|
|
"lines": 97,
|
|
"branches": 90,
|
|
"functions": 92,
|
|
"statements": 97
|
|
},
|
|
"workspace/system-maintenance.mjs": {
|
|
"lines": 93,
|
|
"branches": 81,
|
|
"functions": 92,
|
|
"statements": 93
|
|
},
|
|
"workspace/system-overview.mjs": {
|
|
"lines": 96,
|
|
"branches": 92,
|
|
"functions": 98,
|
|
"statements": 96
|
|
},
|
|
"workspace/system-sources.mjs": {
|
|
"lines": 98,
|
|
"branches": 88,
|
|
"functions": 95,
|
|
"statements": 98
|
|
},
|
|
"workspace/system-state.mjs": {
|
|
"lines": 98,
|
|
"branches": 94,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/system-tagging.mjs": {
|
|
"lines": 98,
|
|
"branches": 94,
|
|
"functions": 95,
|
|
"statements": 98
|
|
},
|
|
"workspace/system-thumbnails.mjs": {
|
|
"lines": 98,
|
|
"branches": 95,
|
|
"functions": 83,
|
|
"statements": 98
|
|
},
|
|
"workspace/system.mjs": {
|
|
"lines": 98,
|
|
"branches": 92,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/theme.mjs": {
|
|
"lines": 98,
|
|
"branches": 93,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/thumbnail-review.mjs": {
|
|
"lines": 98,
|
|
"branches": 93,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/thumbnail-support.mjs": {
|
|
"lines": 98,
|
|
"branches": 98,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/toast.mjs": {
|
|
"lines": 98,
|
|
"branches": 98,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/toolbar.mjs": {
|
|
"lines": 96,
|
|
"branches": 96,
|
|
"functions": 98,
|
|
"statements": 96
|
|
},
|
|
"workspace/topbar.mjs": {
|
|
"lines": 98,
|
|
"branches": 98,
|
|
"functions": 83,
|
|
"statements": 98
|
|
},
|
|
"workspace/viewer-state.mjs": {
|
|
"lines": 98,
|
|
"branches": 95,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/vocabulary-dialog.mjs": {
|
|
"lines": 98,
|
|
"branches": 88,
|
|
"functions": 95,
|
|
"statements": 98
|
|
},
|
|
"workspace/vocabulary-state.mjs": {
|
|
"lines": 98,
|
|
"branches": 96,
|
|
"functions": 98,
|
|
"statements": 98
|
|
},
|
|
"workspace/vocabulary.mjs": {
|
|
"lines": 98,
|
|
"branches": 91,
|
|
"functions": 93,
|
|
"statements": 98
|
|
},
|
|
"workspace/workspace-dialogs.mjs": {
|
|
"lines": 97,
|
|
"branches": 95,
|
|
"functions": 98,
|
|
"statements": 97
|
|
}
|
|
}
|