53cfd8b0358087f748c301d61ba67d9e5610c19c
7
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
195a1d33ac |
feat(annotations): Tag image dialog, card Tag button and inspector tag chips (#281)
* feat(annotations): image tagging controller with staged suggestions Add the UI-agnostic Tag image dialog controller (#249 C1): observe the file into a capture, prefill the saved tags and the server's tagging defaults, stage the top suggestions as removable chips, and save manual tags plus the remaining chips with case-insensitive dedupe (manual spelling first). Every read is serial-guarded; saves are sent once and a lost answer is resent identically only by Retry. Conflict, storage, file and capture outcomes keep the draft and gate Save on a reopen. ApiError now carries a validated copy of a tag-conflict body's current tag set so the dialog can show it. * feat(annotations): Tag image dialog beside Info on every image card Every Images and Library-embedded gallery card shows the concept's Tag after Info. Tag opens the concept's Tag image dialog (image-tagging.mjs over the C1 controller) in the shared modal shell with the sticky tagging-modal footer: capture preview, manual tags, the four legacy engines with real defaults, explicit Generate, removable suggestion chips (D83), legacy model labels (D84), a Suggestions count (D91) and Retry where the controller allows it. The capture is released on close, route change and pagehide. The Tags page gains the concept's Auto-tag images for the image selected in Images (aria-disabled without one, D115) and real Images counts that follow every save. Retires D9, #245 D2 and #245 D3. The toast now uses the mock's copy verbatim ("tags" even for one), C1's D60/D61/D68 markers are renumbered to D83/D84/D91, and at extreme zoom a card's info row stacks (and wraps Info and Tag) when its row is narrower than the narrowest bounded card, list rows included (D31). * feat(annotations): show image tags in the inspector and viewer The inspector parses the B3 envelope's image_tags facet (strictly: exact keys, current/none/unavailable, at most 100 vocabulary names) and draws one chip row after the prompt blocks: the file's current image tags first, then D21's linked tags not already shown (case-insensitive). In the Images panel image tags are Tags filter buttons and linked tags keep D21's rule; in the viewer and wherever no Tags filter exists every chip is plain. none and unavailable add no chips (D88, plan D65; D21 amended). The tagging fixture reset now drops leftover captures, so a long run of tagging tests in one worker stays below the capture store's limit. * fix(annotations): re-read a tagged file in place after a save After a tag save the selected file's inspection is re-read quietly: the panel and the viewer keep the current details until the new answer lands, with no "Loading file details…" in between. While a Tags filter or a search is active, Images re-reads its loaded range in place (same query, from the first page up to the loaded count, published once), keeping the cards, the scroll position and the selection while the selected file still matches; when it no longer matches, the panel follows the collection. * fix(annotations): give suggestion chips the concept chip corner The D83 chips took the later `.tag-suggestion` row rule's 5px corner; the concept `.chip` has 4px. The concept comparison of the Tag dialog found it. * test(annotations): compare the Tag dialog with the approved concept The Tag image dialog's inventory (order, text, field attributes, tokens, preview box, chips, status, sticky actions) must equal the concept's at 1600x1100 and 390x844, dark and light, opened, generated with WD14 and with a language model, for an empty result and in the review scope, and the card's Info and Tag group must equal the concept's. Production runs on the concept fixture with the concept image's tags saved and the fake model answering the concept's own sample; only D29, D83, D84 and D91 are mapped, and a mutation check shows the comparison notices a change. The D3 footer check again asserts a strictly shorter footer, with short seedless subtitles that stay on one line beside Info and Tag. * fix(annotations): final review round for the Tag image dialog - Option fields show the server defaults that Generate sends: the controller bumps an options revision on open and on a model change, and the panel re-renders only then (I1). - A tag conflict shows the current set at once ("Saved now: ...") and puts it ahead of the draft in the manual field, so the next Save sends what is shown (I2). - The opening sentence is the concept's on-screen copy; "Remove anything you do not want to add." leads the chips only while chips exist; the comparison's opened mapping is gone (I3). - ApiClient gains an additive keepalive option; the capture release and the Save PUT use it, and pagehide closes the dialog (I4). - Closing during a save keeps the capture until the PUT settles, then releases it and still reports the outcome (toast, page refresh) (I5). - A refresh asked during a page load is pending and runs once it settles; re-reads use pages up to 200 files; a failed re-read toasts; Load more waits for a refresh (I6, M2, M3). - The selection bar drops checked files a refreshed result no longer lists (I7). - D116 records Retry; after a failed Generate it takes Generate's own path, and after a changed storage, file or expired capture it observes again keeping the draft (I9, M4, M5). - A save made from Tags marks a filtered Images visit stale (M6). - images.mjs drops under 800 lines: the tagging wiring moves to images-tagging.mjs and the favorites star to images-favorites.mjs (unchanged), and reset() reuses dropSelection() (M1). - Restore the activity-scan.mjs coverage floors; drop the duplicate paired captures test (I8, M9). * fix(annotations): coverage floors for the Tag dialog round Floors for the new images-favorites.mjs and images-tagging.mjs, and the measured raises of image-tagging-state.mjs and image-tagging.mjs. activity-scan.mjs keeps main's floors. |
||
|
|
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. |
||
|
|
10a4847a6b |
feat(history): concept heading, negative variants and compare (#265)
* feat(history): concept heading and summary The Prompt history page takes the approved mock's heading and summary surface (docs/concepts/workspace/app.js renderHistory a591-598, #249 F1): - Heading description is "Prompt history · one positive, one shared gallery"; the actions stay "← Prompt library", "Edit metadata" and button.send "Send to ComfyUI" (aria-label "Send prompt pair to ComfyUI: <title>"), on one row at 1600. Send is disabled only while the selected pair's roles are loading (D24). - The summary is article.surface > span.chip category, the positive and selected negative prompt blocks, then .card-meta "N tracked runs · N images · N negative variants". Tracked runs come from the recorded history window's recorded_run_count (hidden until a read completes, kept while a refresh is in flight); images and variants from the saved positive's image_count and variant_count. - The summary's copy/send actions are named after the prompt, as in the concept ("Copy positive prompt: <title>"); mountPromptBlocks gains setTitle so an in-place metadata edit renames them without a remount. - mountRecordedHistory gains an optional onRecordedCount callback. Selector mapping: - .library-surface (history summary) -> .library-history article.surface - .library-surface .library-chip -> article.surface > span.chip - "Copy|Send <role> prompt[ to ComfyUI]" in "Saved prompt texts" -> "...: <title>" - heading p.muted "One positive prompt. Reusable negative variants. One shared gallery." -> "Prompt history · one positive, one shared gallery" - Unchanged: region "Saved prompt texts", .library-variants, .library-shared-gallery, .recorded-history (still .library-surface). Tests: workspace-history "history heading actions stay on one row at 1600 and are enabled after load" and "summary meta uses recorded runs" (both failed before), a prompt-blocks setTitle unit, and the mapped selectors in workspace-visual, workspace-library-editor and the canonical comparison. * feat(history): concept negative variants panel The history's Negative variants panel is the concept's (app.js a598, style.css 1059-1154): section.surface.timeline-panel with the panel heading (h2, p.small "Choose a red dot to change the negative and filter this gallery."), row actions Compare negatives (aria-pressed; its section is Task F3), + Add negative and the approved Show archived; a .timeline of .positive-lane and .variant-lane rows (--lane-offset 38 + 47i) with the "−" .variant-dot, "<name> · N runs" and a .run-dot when the pair's run_count > 1; and .timeline-lower with the legend and All images for this positive. D26: All images for this positive opens the concept dialog with the embedded, bounded (Load more) media gallery instead of filtering the shared gallery. The run dot opens Recorded run for the pair's newest recorded execution, else a Repeat run dialog with the pair's newest linked image. Backing data: - library variants carry prompt_pairs.run_count; - GET /prompt_manager/library/{positive_id}/history/pairs/{pair_id}/newest reads the newest run in the positive's exact raw-text scope whose current-epoch saved-pair hint still resolves to that pair (the get_history_run hint rule), inside the fenced history snapshot. - workspace-dialogs gains openContentDialog (onClose releases the body). - mountRecordedHistory returns openRun. D45 (new deviations): - a dot is named "Select negative <title>" when no other loaded lane has that title, else "Select negative variant N", so names stay unique; - the bounded "N of M saved negative variants" count stays a visually hidden status; "Saved combinations, not an execution timeline." is gone; - archived lanes (#235) are concept lanes marked .library-variant-archived at .6 emphasis with their Restore button; coarse dots keep a 47px target; the heading, legend row and All images button wrap at 390px and 200% zoom. Retired CSS: .library-branches, .library-positive-lane, .library-variant-lane, .library-variant-dot, .library-variants, .library-panel-heading, .library-variant-footer, .library-variant-summary. Selector mapping: - .library-variants -> .library-history .timeline-panel (region "Saved negative variants" unchanged) - .library-branches -> .timeline; .library-positive-lane -> .positive-lane - .library-variant-lane -> .variant-lane; .library-variant-dot -> .variant-dot - .library-variant-archived and .library-variant-restore unchanged - .library-variant-footer / button:last-child -> .timeline-lower / button - .library-variants > p.muted -> .timeline-panel .panel-heading .small - "Select negative variant N" -> "Select negative <title>" for titled pairs (history, native library, inspector, visual and real-host fixtures); untitled pairs unchanged - lane text "· N linked image(s)" -> "· N run(s)" - All images for this positive: aria-pressed filter -> dialog "All images for this positive"; tests that used it for the whole gallery select the lane linking the image, or read the dialog - variant panel row actions gain Compare negatives first Tests: new workspace-history "variants panel matches the concept", "run dot appears only for pairs with more than one run and opens its run", "all images dialog lists every image for the positive"; new workspace-library-editor "show archived and restore still work" (all four failed before the change). Python: variants carry run_count, newest pair run (DB and route). Unit: content dialog. The history fixture counts two canonical pairs and links one image to Clean geometry. * feat(history): compare negative variants Compare negatives now opens the concept's comparison (app.js a599-616, style.css 1155-1179) as a section.surface right after the timeline panel: h2 "Compare negative variants", p "Baseline compared with <name>. Choose another red dot to change the comparison.", a .compare-grid of two .compare-item negative blocks (baseline, selected), p.small "Added terms", then span.diff-added "+ term" per added term or p.small "No added terms.". Choosing another red dot updates the same section in place; pressing Compare negatives again removes it. D25: the baseline is the oldest active variant; with no selection the selected side falls back to it, as the concept's v does. The pure addedTerms(base, selected) in history-state.mjs is the concept's rule: comma split, trim, drop empty terms, exact case-sensitive difference that keeps the selected order and repeats. D46 (new deviations): - lanes are newest first, so the baseline is the lowest-id active variant, not the first lane; while the variant page has more, the oldest is not loaded and the section says "Load more negative variants to reach the baseline." instead of guessing; - the two blocks are named "Baseline" and "Selected" (regions and copy/send actions), so no name repeats on the page; - Compare negatives is disabled when there is no active variant. Selector mapping: none changed; the section is new (.library-history .timeline-panel + section.surface). Tests: unit "added terms split, trim and exact-diff like the concept"; workspace-history "compare toggles, follows the selected dot and stacks at 390" and "compare waits for the page holding the oldest active negative" (all failed before the change). * test(history): guard the newest-pair route and record D45/D46 TESTING.md lists the Prompt history page's spec decisions (D24-D26) and its new deviations D45 and D46 beside D23 and D40-D44, notes under D46 that production opens on the newest lane while the concept opens on its baseline, adds GET /prompt_manager/library/{positive_id}/history/pairs/ {pair_id}/newest to the history route list with its guards, and documents the detail variants' run_count. Tests: the newest-pair read skips a stale reference_epoch row, stops at 16 stale-hint candidates (17 newer stale rows hide an older valid run and report {run: null}), and rejects a negative-only or deleted positive; the route answers 404 for an ineligible positive, 503 storage-unavailable for storage failures without leaking details, and 409 history-changed for a restore during the snapshot. * fix(history): run-dot touch target, unique dot names and dialog cleanup - D45: under pointer: coarse the 12px run dot gets a 44px ::after target, and while a run dot shows the lane text wraps within 139px before it. - D45: dot names compare trimmed titles and never use a title that reads like a fallback "variant N", so names stay unique. - All images and Repeat run share one dialog-scope slot: each open replaces the previous scope instead of adding a cleanup per open. - A run-dot lookup that settles after the view went stale opens nothing. - Retire the unused .library-main .library-chip rule and the stale "Saved combinations" comparison note. Tests (workspace-history, Chromium and touch): the coarse run-dot target and lane width; a failed newest lookup shows the toast and Repeat run; Recorded run that cannot open (recorded history failed) falls back to Repeat run; an archived pair's Repeat run reads archive=archived; Compare negatives is disabled with no active variant; dot names stay unique. * fix(workspace): one inert handle for aborted history and prompt-block mounts An aborted parent no longer gets a separate never-called stub per method: mountRecordedHistory returns its hoisted openRun (it answers false before reading later state), and mountPromptBlocks returns one shared no-op for update, setState, setTitle and dispose. This keeps the history.mjs and prompt-blocks.mjs function-coverage floors (98) without lowering them. Tests: test:coverage (Chromium and touch, 922 workflows) passes for all 61 files; it failed before on history.mjs 94.44 and prompt-blocks.mjs 93.75 function coverage. |
||
|
|
00bda8f442 |
feat(inspector): concept prompt blocks, side panel and viewer details (#260)
* feat(workspace): concept prompt blocks Shared prompt blocks (Images panel, viewer details, Library cards and saved texts, recorded runs) now render the approved mock's markup (app.js promptBlock a298-302, concept.css s550-588), #249 D22: - section.prompt-section.<role>[aria-label="<Role> prompt"] > .prompt-heading (span.role-label "+ Positive prompt" / "- Negative prompt"; .prompt-actions: copy button.icon-btn "Copy <role> prompt", then send button.send.icon-btn "Send <role> prompt to ComfyUI") + p.prompt-text. - No per-block source line; a file's evidence moves to the panel badge (D2) and "File & provenance" (D3). Recorded runs keep every field's source in "Frozen field provenance". - Empty text reads "No negative prompt (explicitly empty)" and still sends an explicit clear. Absent, ambiguous and unresolved roles render p.prompt-text.muted with the existing status strings and no actions; unknown role names render nothing and cannot be sent or copied. - Copy shows the shared toast "Prompt copied."; without a clipboard it opens the concept's "Copy prompt" select-to-copy dialog. - The data-send-role / data-copy-role contract is unchanged. The D22 interim overrides (26px actions, prompt-text margins, copy status, source line) are retired so the concept's rules apply; the action buttons measure the mock's 31x31. pre.prompt-text styling stays only for the Library editor's Archive dialog and frozen field provenance, which keep their own markup until their page rebuilds. Test selector mapping: .prompt-block / .prompt-positive -> .prompt-section(.positive) .prompt-block-heading h3 -> .prompt-heading span.role-label region pre / pre.prompt-text -> p.prompt-text "Empty negative prompt" -> "No negative prompt (explicitly empty)" "Send <role> prompt" -> "Send <role> prompt to ComfyUI" .prompt-source / copy status line -> removed (toast or Copy prompt dialog) copy button 30x26 -> 31x31 (measured in the mock) * feat(inspector): concept side panel The Images side panel is the concept's aside#image-inspector.detail-panel in the order of app.js imageDetails: plain cover, heading with the format chip, filename, the D21 evidence badge and category chip, prompt blocks, tag chips, the six facts and the footer (Send to ComfyUI pair, Prompt history). Provenance, raw records and downloads stay in the viewer's full details. Positive-scope tag chips set the Images Tags filter to that tag. * fix(inspector): mark the side panel's overlay rules with their D2 decision The overlay lint (tests/vendor/concept-css.test.mjs) requires a /* D<n> */ marker on every workspace.css rule that overrides a concept selector; the three .detail-panel rules from D2 lacked one. * feat(inspector): full details in the viewer The viewer's #viewer-details renders the concept's imageDetails(i, true): the side panel content without the cover in a class-less wrapper, then "File & provenance" and "Workflow & raw metadata" (pre[tabindex=0] > code and the two downloads with their unavailable reasons), then the pair send ("Send to ComfyUI", labelled "Send prompt pair to ComfyUI: <title>") and Prompt history buttons. One renderer now serves both variants. Provenance wording (maintainer, 2026-10-20): the six per-fact "<Fact>: Configuration evidence · <source>" lines and FACT_SOURCES are gone; the concept's "Prompt evidence: <badge>" line carries the D21 badge in lower case and is left out without a badge. The honest facts stay (current-file note, Source, Path, Size, Dimensions, Format, Media type, Modified, occurrence, association, Download original). An unverified format reads "PNG extension", no stray leading dot. inspector.css and images.css are deleted; only rules with a live consumer moved to the overlay under #249 D3 (inspector wrapping, the inert aria-disabled download, the narrow Images container, the Library and recorded-run prompt blocks, the Archive negative's prompt-block, the prompt sender) plus a D20 wrap rule for the viewer's buttons at 200% zoom. D1 review follow-ups: library.css no longer restyles recorded-run p.prompt-text.muted (m2); the copy fallback has a seam and unit tests for a missing clipboard and a refused write (m3); a stale test title is renamed (m4); the fallback trigger falls back to the same-role button when the clicked one was detached by a re-render (m7); TESTING.md records the D20 and D22 deviations (m1). Selector mapping in tests: .file-inspector-inner -> .panel-heading, .generation-facts -> .meta-grid (first), .file-facts -> details .meta-grid, .file-name -> #viewer-details > div > p.small, .file-inspector-footer -> #viewer-details, viewer "Send prompt pair" -> /^Send prompt pair to ComfyUI: /. * test(comfyui): real-host specs read the concept prompt blocks library-sender: an explicitly empty saved negative now reads the concept's "No negative prompt (explicitly empty)" (D22). The prompt-sender spec's panel and viewer selectors (complementary "Selected image details", the card image opening the viewer, .meta-grid facts, the labelled pair send) moved with the D3 commit. * test(comfyui): the frozen run's prompt regions read the concept prompt text * fix(inspector): coarse viewer-detail targets and Prompt history on the shown route - Prompt history closes the viewer and, when its target is the route already shown, re-renders it as the concept's go() does (navigateTo plus a router refresh event), instead of doing nothing. - The viewer details' disclosure summaries and "Download original" keep 40px targets on coarse pointers (#249 D34), as TESTING.md promises. - Drop the dead `.prompt-positive` rules and merge the two `pre.prompt-text` rules. - TESTING.md records the side panel's D20/D21 deviations. |
||
|
|
86d82186ee |
feat(images): concept Images page — toolbar, cards, star, viewer, compared against the mock (#258)
* feat(workspace): shared concept toolbar and dialogs
Add the concept's shared page toolbar (search.toolbar with the search
field, category filter, sort and Filters) and its dialogs in one modal
shell: "Refine this view" (Source / Folder / Media / Tags, Apply filters),
"Import & inspect" with only the real operations (Open inspector, Rescan),
and the "Copy prompt" select-to-copy fallback. The sort lists the
concept's three options first (D14); there is no Save view (D16). Every
dialog returns focus to its trigger on close or Escape.
Refs #249
* fix(workspace): bind shared dialogs to the page and its route
- Drop the invented Refine note; every field is real (D14).
- Ignore a late close event once the shell was reopened or replaced, so
an action that opens the next dialog keeps it open.
- Dialog openers require the page scope; disposing it closes and removes
the shell unless a newer page's dialog is open. Every route change
closes the shared dialog before the next page mounts.
- Clear filters also reports category '' so the page clears the toolbar
category, as the concept does.
- The toolbar requires its scope and falls back to the first sort for an
unknown value; dialog callbacks are validated up front.
Refs #249
* feat(images): concept toolbar, filter row and caption
The Images page now renders the approved mock's heading, shared toolbar,
filter row, section caption and infinite continuation (#249, Task C2).
- Heading: "Your generations, their prompts, and the next idea." with
Import (upload icon) before New prompt (primary).
- Toolbar (shared mountToolbar): search over filenames, linked prompt
text, titles and tags; category (the Library's categories); sort
Recently added / Name A–Z / Highest rated, then Oldest first and
Name Z–A (D14).
- Filter row: Grid/List/Masonry segmented control, Thumbnail size,
the scope chip ("All output folders" or "<source> · <folder>") and
"Group by positive prompt →".
- Caption: "N of M images · K positive prompts" from positive_count; a
plain cursor page keeps the first page's count and an unknown count
hides the "· K positive prompts" part. The date span shows
"<Today|Yesterday|weekday> · <Month D>" of the newest loaded result
under a date sort and is hidden otherwise (D17).
- Continuation: "N of M images · more load as you scroll" with Load
more, the concept's end copy, Retry for a failed page, and the
concept's "K more images loaded. N of M." toast.
- The listing sends category, tags and sort=rating (PR B params).
Old control -> new control:
- "Search filenames" field -> toolbar search "Search workspace"
(placeholder "Search prompts, tags, filenames…"), also matching
prompt text and tags
- Source select -> Filters -> "Refine this view" Source
- Folder select -> Refine Folder (all sources' folders and ancestors)
- Media select -> Refine Media
- (new) Tags -> Refine Tags; Category -> toolbar category select
- Sort select (Newest/Oldest/Filename A–Z/Z–A) -> toolbar sort
(Recently added/Oldest first/Name A–Z/Name Z–A, plus Highest rated)
- Layout buttons -> .segmented Grid/List/Masonry in the filter row
- Thumbnail size field -> label.small + select#image-size
- "Group by positive prompt" link -> button.chip "Group by positive
prompt →" (#/library)
- Refresh -> Import -> "Scan an output folder" -> Rescan (D15)
- "Current gallery" link -> removed; Overview's tool cards link the
legacy gallery (D29)
- "All images" h2 + count -> .section-caption #result-count
- .image-continuation -> .infinite-continuation (Retry kept)
Legacy behavior kept: video items, keyboard grid navigation, scroll and
filter restoration, bounded DOM, continuation retry, source-change
recovery (now disabling the toolbar, Filters and Import). The Filters
and Import dialogs close on route change and browser Back.
The shared toolbar (C1) called .some() on select.options, which is an
HTMLOptionsCollection in a browser (only the unit tests' fake DOM gave
an array); setValue and setCategories now spread it first. The layout
segmented control may wrap at extreme zoom (D31) instead of scrolling
the 390px page sideways.
The retired .image-filters, .image-view-controls, .image-toolbar,
.image-layouts, .image-collection, .image-continuation and duplicated
.image-workspace grid rules are gone from images.css.
* test(workspace): model select.options as a browser collection
The toolbar units' fake DOM returned select.options as an Array, so the
toolbar's .some() call passed there while it threw in every browser
(fixed in
|
||
|
|
ecd21165a7 |
feat(history): add chronological runs and exact output inspection (#227)
* feat(history): add bounded chronological read projections (#209) * fix(history): validate internal continuation positions (#209) * feat(history): expose fenced history windows and run details (#209) * fix(history): release retired projection connections (#209) * feat(history): resolve reported outputs through current authorization (#209) * fix(history): reject invalid inspection targets (#209) * feat(history): add scoped history state and current-file adapter (#209) * fix(history): preserve slideshow and clear stale reentry actions (#209) * feat(history): render recorded executions and frozen run details (#209) * fix(history): recover receipts and refresh capture evidence (#209) * test(history): verify chronological history across restore and host modes (#209) * fix(history): enforce chronological marker visual parity (#209) * fix(history): resolve final chronological review findings Address #209 recovery, bounded visit restoration, exact receipt availability, and portable regression coverage. * test(history): repair Windows fixtures and review feedback Address #209 and PR #227 native path, cleanup, media and review-comment expectations. Document Windows portability preflight without changing production behavior. * ci: raise the viewerjs job budget for the larger browser suite The cross-browser workflows suite grew from 396 to 496 tests on this branch, which pushed the whole viewerjs job to about 25.5 minutes against the 25 minute cap set in #181. Two exact-head runs on this branch timed out without any test failing: run 39173019283 was killed on test 474 of 496, and run 39175125474 printed "496 passed (12.8m)" and was then cancelled during Playwright shutdown. Raise only the job budget. Browser projects, workers and per-test timeouts are unchanged. Splitting the browsers into parallel jobs is tracked separately. |