53cfd8b0358087f748c301d61ba67d9e5610c19c
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c7375573ed |
feat(images): set an image's own category, show its origin and count image categories (#332, PR 3) (#428)
* 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. * feat(vocabulary): count and rename image categories (#332) Tags & categories lists every category value with image_count, the stored image_categories rows holding it. Values only images hold follow every prompt-backed value (first_pid at SQLite's INTEGER maximum), so keyset cursors cross the boundary. /library/options keeps its own query and never lists image-only values. Manage category counts image rows (impact.images), treats an image-held target as existing, renames image rows in the same transaction, allows renaming an image-only value, and hashes the sorted image rowids into the impact token so a change between preview and apply is refused with vocabulary-conflict. Every image-category statement reads idx_image_categories_category. * feat(images): the category filter matches each image's effective category (#332) An image's own category wins; without one it inherits its link's: the recipe's category when set, else the positive prompt's (MC4, one rule with the inspector chip). Ambiguous and unlinked images inherit nothing, so an unlinked image with its own category is now listed. The link read no longer filters by category in SQL; the page filter, select all N matching and saved views compare the effective value. A failed own-category read answers the existing listing-failed 500. * 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. * feat(inspector): show an image's own category in the inspector and History (#332) The inspector's library facts gain category_scope "image": an image's own category overrides the inherited one for any link status (linked, ambiguous, none). Images reads it for output sources only; History reads it once per inspection inside its snapshot, keyed by the same file key from the symlink-resolved output folder. A failed read hides the chip rather than show a possibly wrong value. The chip's hidden prefix names the origin ("Image category: "); the visible origin text comes in PR 3. * perf(images): keep the category filter's link read narrow; record D141, D142, D145 (#332) Under a category the link read keeps only links whose raw recipe or positive category is the value (Python still decides the inherited one), the links of images whose own category is the value are read by path, and plain reads skip the inherited-category work. The own-category map is keyed by normalized path. TESTING.md records D141, D142 and D145 and amends D13, D36 and D39; the filter budget seeds 2,000 own categories. * 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. * fix(images): Windows path keys for own categories; plan check for the category read (#332) Review fixes: on Windows the own-category map keys each file with normalize_image_path (abspath drops trailing dots and spaces), and the plan test also covers the category's narrowing clause. * feat(images): Set category sets each image's own category; the chip names its origin (#332) The selection bar's Set category writes the selected images' own category (linked or not, up to 500, or all N matching up to 500), with the toolbar categories as suggestions; Use prompt category removes it after a confirmation that states the count. It never writes a prompt (#259's prompt action is retired from Images; the Library editor and the legacy admin Set Category still change prompt categories). The toolbar lists the listed images' own categories beside the prompt and recipe ones. The inspector chip shows its origin: "· this image", "· prompt" or "· recipe". Tags & categories counts images and opens Images for a category only images use. * test(images): compare the category chip's visible origin against the concept (#332) D146: the inspector comparison measures each chip's span.chip-source and lays the concept's own chip out with the recorded rule and origin added at runtime (withChipSource), in the side panel and the viewer's details. The D39 recipe case asserts the visible " · prompt"; the mutation check removes the origin. * test(images): compare Set category and the category options against the concept (#332) With one image's own category set through the real route, the toolbar's #category-filter options are compared with D141 mapped (the image category sorted in with the Library's, D36), and the Set category dialog against the concept's simpleInput laid out as recorded at runtime: D140 (empty field, datalist#workspace-input-suggestions of the toolbar's categories, Use prompt category between Cancel and Apply) and D8 (Apply). Unmapped, the dialog differs only in those parts. * test(history): compare the History media inspector's category chip against the concept (#332) D145: with an own category set on the baseline's newest output, the Recorded run viewer's chip is compared with the concept viewer's category chip laid out with the D146 origin and the own category at runtime; D21 maps its hidden "Image category: " prefix. Unmapped, the two differ only in the text, the prefix, the width and span.chip-source. * test(images): Set category sets each image's own category in page workflows (#332) Replace the #259 prompt-category tests with CUJ 1, 2, 3, 8 and 10, the suggestions datalist, the 500-image limit, the source-changed path, D142 and coded refusals; Images never posts /prompt_manager/bulk/category. * test(inspector): the category chip names its origin in the panel, viewer and History (#332) CUJ 4 and 5: "· this image", "· prompt" and "· recipe" chips, an unlinked file's own category, and History's current-file inspector. * test(vocabulary): category rename covers image categories; image-only rows browse Images (#332) CUJ 6 and 7 with own categories set through the API on two output files. * test(vocabulary): compare image-category rows and the Manage category preview against the concept (#332) The vocabulary fixture seeds own image categories on request (image_categories: Landscapes on two images, Portraits on one image only). mapVocabularyConcept maps D143 ("N positive prompts · M images", image-only rows before Manage category), a model checked against the concept page with those rows added at runtime, and mapVocabularyDialog takes the preview sentence, so the Manage category preview is compared with D9 and D144's images. The default fixture and mapping are unchanged. * test(vocabulary): park the pointer before measuring the category rows (#332) Firefox kept the pointer where the concept's Review was pressed, over the production Portraits row's Browse, so that button was measured hovered. * docs(testing): record D140, D143, D144 and D146 and the image category UI (#332) D140 (Set category writes images' own categories), D146 (the chip's visible origin) beside D141, D142 and D145; D143 and D144 in the vocabulary section. D8, D21 (the D20/D21 side panel) and D39 amended; the D8, the "all N matching" and the Native vocabulary UI paragraphs name the Prompt library editor and the legacy admin "Set Category" for prompt categories; the #332 section lists the interface and its comparisons. * 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). * fix(images): focus after a changed source; Browse is a fresh Images visit (#332) Review fixes: a 409 source-changed reload moves focus to the heading as a success does; a selection the listing no longer holds says none could be found instead of sending nothing; a Tags & categories Browse opens a plain Images visit filtered by the category (remembered and with the Appearance defaults) rather than a saved-view visit. * 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. * refactor(images): take the selection bar's categories from its one caller (#332) The getCategories default was never used (images.mjs always passes the toolbar's categories) and left images-selection.mjs function coverage at 97.05 (floor 98). |
||
|
|
f95f66d76a |
ci(browser): run page workflows with parallel workers and CI shards (#274)
* test(browser): one test server per Playwright worker The page workflows started one global webServer and ran with one worker. Each worker now owns its own tests/browser/server.py on an OS-chosen port, with its own temp root and database, through a shared `test` in tests/browser/fixtures.mjs. baseURL, and so page, request and browser.newContext, point at that worker's server. A server that dies fails the running test with its stderr tail and the next test gets a fresh one. The worker count comes from PM_TEST_WORKERS, else 1 under CI, else a quarter of the CPUs up to 8. Chromium workflows in the pinned image: 510 tests in 606 s with one worker on main, 511 (one new isolation spec) in 119 s with eight. * test(browser): test servers bind an OS-chosen port themselves tests/browser/server.py accepts --port 0: it binds 127.0.0.1:0 before building the app, holds that socket for its whole life and prints PM_TEST_SERVER_PORT=<n> to stdout. startBrowserServer defaults to port 0 and reads the announced port, so no port is reserved and released before the server binds and two launchers can no longer race for one. The "address already in use" retry and free-port.mjs are gone; --port N still works and is checked against the announcement. * ci(coverage): shard and merge modes for the frontend coverage run frontend-coverage.mjs now plans its steps from the command line. No arguments still runs everything, then reports and checks. --shard I/N runs one slice of the chromium+touch workflows into coverage/js/raw without wiping it or reporting; shard 1 also runs the node units and the viewer suite. --merge-only reports and checks over raw coverage the shards wrote, with --update-baseline as before, and refuses an empty or missing raw dir. npm scripts: test:coverage:shard and coverage:merge. * ci(viewerjs): run browser suites with workers and shards Frontend coverage runs as three shards (PM_TEST_WORKERS=3 each) that upload their raw V8 files as coverage-raw-N; a merge job downloads them, fails unless all three arrived with raw files and no clashing names, then runs coverage:merge. Firefox and WebKit page workflows run as two shards each with three workers. The required viewerjs context keeps its name and now needs the shards, the merge and the browser shards, failing unless every one succeeded. * ci(viewerjs): cache npm downloads and size timeouts from the first sharded run The first sharded run lost a webkit shard to its 12-minute limit after all 267 tests passed, and npm ci stalled for 5 minutes in six of eight jobs. Each npm ci now restores npm's download cache keyed on the vendor lockfile, and browser job timeouts are the measured times plus at least 50%, never under 20 minutes. The workflow comment records the measurements. * test(browser): wait for rendering frames, not timers or luck, under load Two Library workflows failed in CI's webkit shard 2/2 (run 39714840809) under parallel load: - "library infinite loading ..." counted cards right after the last page hid "Load more". That render queues the window update for the next frame, so a slow frame left the 120 cards of the all-rows layout in place (received 120, expected < 80). It now waits for the next frame, which runs after every callback the page queued. - "row measurement preserves anchor ..." settled with a 60 ms timer. A scroll event is delivered in a rendering frame, so on a starved renderer the timer ended before the scroll's frame and no card was in view (null anchor). settleLibrary now waits for two consecutive native frames with no Library frame queued, checked inside the frame callback. Before/after: a scratch copy that delays every frame by 120 ms (as a starved runner) failed the old helpers 19 of 40 runs, at the CI lines, and passed the new ones 40 of 40 on chromium and webkit. Throttled to 1 CPU with 4 workers, the original spec failed 1 of 42 for each test. The Tags viewer workflow closed the viewer as soon as its image was visible, before ViewerJS rendered the title, so CI's coverage never ran the page's title callback (tags-page.js functions 26/46 = 56.52%, one function above the floor of 56; locally 27/46). The test now asserts the title names the shown file, which always runs the callback. * test(browser): stable under parallel workers Document how to run the browser suites in parallel and sharded, with the measured times, and what keeps specs stable beside each other: wait for a rendering frame, a response or a visible state, never for time. With |
||
|
|
762669758b |
feat(history): concept gallery cards, history comparison and CSS retirement (#266)
* feat(history): concept shared gallery The history page's gallery is the concept's section.history-gallery with its panel heading (Shared gallery, <name> · same positive prompt) and the Images media cards, followed by the recorded executions. A saved variant with no images shows the concept's empty copy. Recorded as D47. * feat(history): drop the shared gallery's Linked images caption (D47 ruling) The concept's "Shared gallery" heading is the gallery's only heading: the embedded collection's "Linked images" heading and "N loaded · N linked images" count repeated it. mountImages gains caption: false and a focusTarget; only the history page's shared gallery passes them, so the Images page and the All images and Repeat run dialogs keep their caption. The layout row and the selected-image side panel stay (TESTING.md D47). * test(history): concept comparison for prompt history history-concept.spec.mjs compares the Prompt history with the concept's #prompt/alpine on the concept fixture at 1600x1100 and 390x844, both themes and motion settings, on Chromium and touch, with its behaviors in both documents and a mutation check. inventoryOf gains a history section; mapHistoryConcept applies D24, D35, D45, D46, D47 and the approved Show archived button, laying the concept out at runtime. Corrections it demonstrated: each compared negative mounts in a display: contents child of the concept's .compare-item (D25), and the concept fixture now links images oldest first and counts one pair use per image, so newest-first reads and lane run counts match the concept. history-canonical-comparison.mjs uses the concept's selectors for production and drops its closed polish entries. * feat(history): drop the Linked images caption from the history dialogs (D47 ruling) The maintainer extended the D47 trim to the history page's two media dialogs. All images for this positive and Repeat run are named by their own dialog title, as in the concept (app.js a1282-1304), so their embedded collection no longer shows the "Linked images" heading or the "N loaded · N linked images" count. A stranded focus returns to the dialog's close button. The Images page keeps its caption. The history concept comparison now records each dialog's caption count in both documents, and TESTING.md D47 covers the dialogs. * refactor(workspace): retire library.css and the interim page guards Every page is rebuilt, so the rules that waited for PRs D, E and F go. library.css is deleted with its link; the overlay is now the only stylesheet after concept.css. Deleted because the concept already draws them: the .library-grid tracks and breakpoints, the history .row-actions flex row and button size, summary cursors, h2 margins, and the sender and Recorded run dialogs' colors and border. Deleted because they had no effect: min-inline-size on block children, a .recorded-run-dialog pre limit, the general .prompt-inspector margin (now scoped to the Recorded run dialog, the only mount point that is not display: contents) and a 700px padding a later rule overrode. The duplicated sender and Recorded run dialog guards are merged into one rule each. Kept with a marker: windowed Library rows (D3), the shelf states (D40), the history panel's wrap, archived-lane and coarse-target rules (D45), and under the new D48 the production surfaces the concept never shows: recorded executions and the Recorded run dialog (#209, D26), the Archive negative confirmation (#235) and the prompt sender. They now use the concept's --green and --red tokens instead of private copies with the same values. The workspace browser specs on Chromium and the concept specs on touch pass unchanged. TESTING.md records D48 and closes the role-block gap. * refactor(workspace): drop the dead embedded caption and tighten the review guards - images.mjs: an embedded collection never has a caption (D47), so the caption option, its "Linked images" heading, the embedded count string and the heading arm of the focus fallback are removed; callers no longer pass caption:false. Behavior is unchanged. - workspace.css: delete the never-applied .recorded-run-dialog::backdrop rule (the concept's .ca-tools dialog::backdrop always won) and note that the D3 row's min-inline-size: 0 is load-bearing for the concept's <=700px 1fr track. - concept-css test: the waiting-rule guard is case-insensitive and is proven against the exact retired comments and the kept provenance labels. - history-concept spec: function replacement for the singular run label, and the DOM mutation removes Compare negatives by role. - D47 markers sit where the caption trim happens. |