* 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)
56 lines
3.5 KiB
HTML
56 lines
3.5 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<meta name="color-scheme" content="light dark">
|
|
<title>Prompt Manager · Workspace</title>
|
|
<link rel="stylesheet" href="js/shared/toolkit/controls.css">
|
|
<link rel="stylesheet" href="lib/viewerjs/viewer.min.css">
|
|
<link rel="stylesheet" href="js/workspace/concept.css">
|
|
<link rel="stylesheet" href="js/workspace/workspace.css">
|
|
<script src="lib/viewerjs/viewer.min.js" defer></script>
|
|
<script src="js/comfy-metadata.js" defer></script>
|
|
<script src="js/gallery-settings.js" defer></script>
|
|
<script src="js/png-metadata.js" defer></script>
|
|
<script type="module" src="js/workspace/index.mjs"></script>
|
|
</head>
|
|
<body class="ca-tools">
|
|
<a class="skip" href="#workspace-main">Skip to workspace</a>
|
|
<div class="app-shell">
|
|
<aside class="sidebar">
|
|
<a class="brand" href="#/images"><span class="brand-mark">P<span>±</span></span><span>Prompt Manager<small>COMFYASSETS</small></span></a>
|
|
<div class="sidebar-label">WORKSPACE</div>
|
|
<nav id="primary-nav" aria-label="Workspace"></nav>
|
|
<!-- D49: the concept's SAVED VIEWS group without its "PROPOSED" badge (saved views are persisted, #249 D16). -->
|
|
<div class="sidebar-label">SAVED VIEWS</div>
|
|
<nav id="saved-nav" aria-label="Saved views"></nav>
|
|
<!-- D136: the concept's COLLECTIONS group without its "PROPOSED" badge; the label shows once a collection is listed. -->
|
|
<div class="sidebar-label" hidden>COLLECTIONS</div>
|
|
<nav id="collections-nav" aria-label="Collections"></nav>
|
|
<div class="sidebar-bottom">
|
|
<nav id="system-nav" aria-label="Tools and system"></nav>
|
|
<div class="connection"><i></i><div>ComfyUI workspace<small>Your library, on your server</small></div></div>
|
|
</div>
|
|
</aside>
|
|
<div class="workspace">
|
|
<header class="topbar">
|
|
<div class="breadcrumbs">ComfyAssets <span>/</span> <span id="workspace-breadcrumb">Images</span></div>
|
|
<div class="top-actions"><!-- D53: the progress pill, shown while a job runs. D52: the count shows running jobs, hidden at 0. --><a id="activity-pill" class="activity-pill" hidden></a><button id="activity-open" type="button">Activity <span class="count" id="activity-count" hidden>0</span></button><label class="theme-label" for="theme">Theme
|
|
<select id="theme" aria-label="Theme">
|
|
<option value="dark">Dark</option>
|
|
<option value="light">Light</option>
|
|
<option value="system" selected>System</option>
|
|
</select></label><a class="canvas-link" href="../">ComfyUI <span aria-hidden="true">↗</span></a></div>
|
|
</header>
|
|
<!-- D129: the library-location notice, shown above every page while a node-folder library can move. -->
|
|
<div id="library-banner" class="library-banner" hidden></div>
|
|
<main id="workspace-main" tabindex="-1"></main>
|
|
<footer class="app-footer"><span>ComfyAssets · Local tools. Your library.</span></footer>
|
|
</div>
|
|
</div>
|
|
<div id="workspace-toast" class="toast" role="status" aria-live="polite" aria-atomic="true"></div>
|
|
<noscript><p>This workspace needs JavaScript. <a href="admin">Open the existing dashboard</a>.</p></noscript>
|
|
</body>
|
|
</html>
|