feat(web): polish workbench UX and add two-sided status widgets - #207
Open
joshistoast wants to merge 61 commits into
Open
feat(web): polish workbench UX and add two-sided status widgets#207joshistoast wants to merge 61 commits into
joshistoast wants to merge 61 commits into
Conversation
Pro-app convention: pointer stays reserved for links. Overrides Chakra's button/switch cursor tokens and sweeps the per-component pointer props, keeping functional cursors (grab, resize, crosshair, text, not-allowed).
Zag slides the indicator via an inline var(--transition-duration, 150ms) fallback the duration-token collapse never reaches; pointing the var at the motion-aware fast token collapses it to 1ms with reduce motion on.
Makes Chakra's shine variant the default, resting on the same bg.emphasized surface with a small fg lift as the band. Reduce motion falls back to the flat fill: a gradient frozen mid-sweep reads as a smudge.
One platform primitive bakes in the arrow and the popover slot-recipe chrome (tooltip-family surface, stroke, shadow), so consumers stay consistent by construction. Dropdown-shaped popovers opt out via showArrow.
The rounded corners clipped generated pixels and read as decoration on what is a verbatim view of the output; the border and shadow carry the framing.
The setting was rendered permanently disabled and nothing read it. Removing it from the project-settings contract also drops the now-unused comingSoon plumbing from the settings controls; normalization silently sheds the key from older persisted projects and .invk imports.
Promotes the experimental toggle to a default-on preference and drops the experimental labeling. Stored preferences keep their explicit value, so an account that turned it off stays off.
A new bottomPanel: 'popover' manifest mode makes the compact chip a real Popover.Trigger opening a large dismissable panel over the status bar, VSCode-style, instead of claiming the shared bottom panel. The popover renders the widget bare, so the view mounts its own header with the mark-read and clear actions.
Two adjacent unlabelled icon buttons read as noise you had to hover to tell apart; one options menu now carries the visibility toggles and both sort radio groups, staying open so several can be set in one visit. The trigger highlight compares against the defaults instead of hardcoding polarity.
The board panel opens scoped to the project at hand; boards owned by other projects stay one toggle away in the options menu, and the open project's own board is always shown regardless.
The starred-gap row grows to hold a hairline separator while the section is expanded; collapsed, the header already separates the sections and the gap stays at the plain grid gutter.
Two halves: the topbar switcher renames through the project library, whose open-project path flushes immediately so the server renames the board in the same transaction; and the gallery shows the open project's board under the live project name, since the fetched board list can lag the rename.
While shift is held over the hovered button the icon, tooltip and aria-label swap to the cancel-all action, so the click never does more than the button shows. The keyboard is watched only while hovered, and the gesture state resets if the button unmounts mid-hover.
openManager now takes the missing model's type and seeds the Add Models starter catalog with it through the same one-shot dynamic-import seam the search seed uses, so prompt expansion lands on text LLMs, image-to-prompt on vision models, and the empty model card on main models.
The Select primitive renders valueText verbatim in the trigger, so the static None string masked the active selection; the field now passes the selected prompt's name and keeps None for the empty catalog.
The expand-prompt and image-to-prompt tooltips no longer switch to a missing-model warning; the popovers already explain what is missing and offer the way out.
The rows sit on the popover's bg.muted surface, where the row recipe's bg.muted/60 hover is invisible; they hover one surface step up instead.
Hover and active now share bg.emphasized in the side rails and status bar; the open widget stays distinguished by the brand hue in its content.
The gallery item search and its semantic-query chip hand-built their frames (opaque bg, emphasized border, md radius) and drifted from the themed inputs; InputShell carries the input recipe's chrome keyed on focus-within, with start/end slots, so composite fields match <Input size="xs"> by construction.
Inline drop zones preview the drop treatment with an accent border on pointer hover (standing down while disabled), and the drag-over accent now wins over consumer hover fills. Board rows stop flashing their hover over the drop tint: the shell's hover stands down while a compatible drag is in flight, and the hovered destination gets a solid accent ring and fill.
The app version already shows in the app menu, the launchpad help menu, and the splash screen; a status-bar chip restating it earned no place. Stale placed instances in persisted layouts degrade silently: the region view model drops instances with no registered widget.
Non-button interactive rows (picker options, menu items) computed cursor auto after the pointer sweep, showing the I-beam over their labels; a base-layer rule pins interactive roles to the arrow. Typeable comboboxes are deliberately excluded — their inputs keep the I-beam.
Chakra's subtle-fill hover collides with muted and control surfaces; an fg-derived 10% fill reads on every surface and keeps the palette tint. Plain buttons are link-shaped actions and now underline on hover — stock Chakra gives them no hover state at all.
The subtle variant's bg.muted hover disappears against the topbar preset strip; one surface step up reads everywhere.
One platform component carries the house segments (xs group, 2xs centered labels, the axe-driven checked-item fill) and replaces hand-rolled SegmentGroup markup in the canvas tool rows, graph preview, gallery view tabs, and dynamic prompts.
36 icons across four captioned category blocks read as a form; one grid scans faster and the keyboard order already walked the flat list.
The preset library graduates from a bare menu to the wildcards treatment: a popover panel with save, search past five entries, apply-on-click rows, rename (wiring the port that never had a UI), and delete behind a confirm. Empty and no-match states say what the list would hold.
Pixel-equal hover colors and transition tuples churn with every theme tweak without guarding behavior. Structural, focus, and contrast assertions stay; the orphaned row-interaction style helpers go with them.
The UI batches added two platform primitives to the initial source graph and ~3KB of editor-owned chrome; captured through the pipeline's own update flow and verified green.
The wildcards tabs, graph preview modes, and gallery view tabs now wear the layer-pane look — borderless track, neutral emphasized fill, xs/600 pills — instead of the accent-bar track, which stays the canvas default.
A new palette hoverTint slot backs the ghost/outline/plain hovers and the SegmentTabs fills: the default gray resolves to fg pulled 40% toward the accent so translucent fills sit in the menus' cool-tinted family, while red/brand/accent buttons keep their own tint. The contrast guard now composites the translucent shown fill before measuring.
Buttons and the segment-tab strips read as different families: buttons sat one size notch larger with squarer corners than the tab pills. - Add a shared radii.control token (5px) and put buttons, tab pills, segmented controls, and input chrome on it. - Shrink button sizes one notch (xs 28px, sm 32px, md 36px) so xs lands on the tab-pill height; sm/md keep the xs text style. - Re-pair segmentGroup item heights with the new button scale. - Pair the iterations field to the 28px capsule and retune the routing control's diagonal icon insets for the shorter box.
…motion - Shift input/numberInput/select/combobox xs/sm/md heights one notch down so same-named sizes share a row height with buttons; fix the stale 32px pairings (InputShell, gallery search inner input, SAM prompt input, canvas number-field affix offsets). - Speed button hover fills up to the faster duration and give segment-tab pills a motion-aware background/color transition. - Round menu/select/combobox items at l2. - Bump the topbar controls one size so they keep their 32px box, and put the layout-preset tabs on the shared control corner with the hover-tint fills (nav sidebars keep the stock selected accent).
…r treatment The red delete tint lived in five copies of the same hover const and a handful of items that tinted the label but forgot the hover fill. The dropdown item recipe now styles `[data-danger]` (fg.error text, bg.error hover/highlight) for menu, select, and combobox items alike, and every delete/uninstall/clear item — direct or through the shared item components' danger tone — opts in with the one attribute.
The card filled itself with the raw cluster color and flipped its text dark/light to survive it. It now sits on the app's dropdown surface (muted bg, emphasized border, md corner) with the cluster's palette color confined to a swatch dot beside the cluster line.
Rows sized themselves from padding while their trailing actions were 24px squares; both now sit on the shared 28px control height and corner.
- Focus the gallery search input from anywhere on its chrome and balance the trailing help button's inset against the leading glyph. - Open the project switcher on right-click too. - Speed tooltip open/close animation up one step. - Mark the applied generate preset and prompt template with a check and label weight instead of filling the row with accent. - Trim sm button horizontal padding. - Give the prompt-trigger popover the same empty-state anatomy as the image-to-prompt popover, and let its trigger show the expanded tint instead of disabling itself while open.
…tarred rule centering - Pin the empty generate-presets trigger to an explicit 24px square. - Hairline separators between preset rows. - The active layout preset's icon takes the brand color. - Center the starred-gallery rule between thumbnail edges; the row above bakes its trailing grid gap into its height, which sat the rule 2px low.
…ing stripped Dialogs ran on Chakra's stock marketing-modal chrome — 24px gutters, lg titles, top placement — with each file patching its own corner of it: two competing surface colors, hand-copied borders, three cancel-button variants, close buttons sized at every call site, and local body-padding compensations. The dialog recipe now owns the look: one bg.subtle surface, xs/700 titles, an fg.subtle xs description slot, 16px gutters, footer gap 2, centered placement, and a tucked-in close trigger; the platform CloseButton defaults to small and muted. Dialog files now carry structure only — redundant placement/gap/size/color props are gone, dismiss buttons all sit on ghost, header subtitles ride the aria-wired Dialog.Description, and the crop dialog gets the close button it was missing.
…ons menu on the resume card - Project cards, list rows, intent tiles, and the resume card keep the arrow cursor over their overlay links, per the pro-app convention the theme's cursor tokens set for controls. - The resume card's cover sits inset from the card with its own radius, vertically centered against a taller text column. - The resume card is a library project like any other, so it now carries the same actions menu as the grid cards: right-click anchors it at the cursor and a 3-dot trigger rides next to the Resume button. - The view-all-projects button steps up to xs.
…g per-card menus Right-clicking one project while another's context menu was open flashed the new menu and immediately closed it: zag's dismissable stack treats any layer mounted above another as nested, and a controlled menu closes a beat after the pointerdown, so the second card's menu mounted above the first's still-registered layer and was dismissed along with it. One ProjectActionsMenuHost now serves every card. Opens route through its context and remount the single hosted menu (keyed per open), so the old layer's cleanup always precedes the new mount and the menu simply moves between cards. The rename/delete dialogs ride beside the menu — an item click closes the menu, which would have unmounted dialogs living inside it. Cards keep plain dots buttons wired through a trigger hook that restores toggle-to-close (the pointerdown already dismissed the menu; the click must not reopen it) and hands focus back to the originating control when the menu closes, since no zag trigger is registered to restore it to. A browser regression test pins the cross-card handoff, aria-expanded transfer, same-card re-right-click, and the dots toggle.
… weight, palette placement - Scrollable heals zag's phantom thumb: the initial measure races a popover's zero-size mount (observers can end up watching pre-remount nodes) and the "has overflow" default strands a small unscrollable thumb on non-overflowing content. Live-node checks detect the contradiction and route one synthetic scroll through zag's own re-measure path; a popover regression test pins it. Two listeners the heal exposed are hardened on their own merits: scrolling inside a popover's own list no longer dismisses the rect-anchored surface, and the workflow-library grid only paginates when it actually overflows. - The dynamic-prompts preview disables its row when there is exactly one expansion — pulling it in would paste back what the prompt box already says — while keeping the preview ink at full strength. - Segment-tab pills use the button font weight. - The gallery sort trigger gets a "Sort by" tooltip via the shared-ids pairing the routing control established. - The command palette pins placement="top" again; the dialog recipe's new centered default had silently pulled it to the middle.
…phantom heal Chakra's stock scroll-area recipe hides a scrollbar only when NEITHER axis overflows, so a vertical-only bar stayed pinned — thumb clamped to zag's minimum size — whenever content merely spilled sideways (nowrap dynamic-prompt rows, wide JSON). The theme's scrollArea override scopes the guard per orientation: a bar shows only when its own axis overflows. The stale-measure heal moves from Scrollable into a shared useScrollAreaPhantomHeal hook so the raw dual-bar scroll areas (JsonPreview, the widget failure boundary) are covered too — JsonPreview mounts inside freshly-opened dialogs, exactly the racy context. Tests poll for zag's measure instead of guessing delays, and now pin both directions: a bar hides when only the other axis spills, and stays shown when its own axis genuinely overflows.
… Ultra Dark - The default ramp trends ~2.5% L darker at the deep end with an even slate chroma curve instead of the old spike at 400; off-ramp seeds and contrast anchors re-derived. Contrast improves at every measured pair. - Forest becomes Osaka Jade: the neutrals move to blue-green stone (hue ~180) with a jade brand and aqua accent. Preferences stored under the old id migrate through resolveWorkbenchThemeId's legacy map, which re-resolves on every ingest so a server echo of 'forest' cannot fight the client. - Ultra Dark keeps its pure-black floor but drops the green-lime cast from its light steps for a faint cool slate, leaving brand lime and the sky accent as the only color. - The token-contract baseline is regenerated through the same resolver the gate reads — a deliberate retheme, not drift.
The legacy About modal's content as a settings tab: the server version, GitHub/Discord links (on a proper GitHub brand mark, which the help menu adopts too), and the copyable system-information blob — installed dependency versions plus the redacted runtime config for admins — backed by an account-scoped store over /app/version and /app/app_deps. The command palette lists the new section, and the Image Map section it had been missing.
The status bar packed every widget to the left. Each bottom chip's context menu now offers moving it to the strip's trailing cluster and back. Placement and drag-reorder stay on the region's single order model — alignment is a render split persisted as an optional id list on the region state, carried by preset snapshots, counted by layout drift, and tolerant of ids that are no longer placed (a re-enabled widget keeps its side). Old snapshots without the field still validate.
…froze dnd-kit's useSortable pins an inline style.transition for its transform animation, which replaces the recipe's CSS transition outright — preset tabs, bottom chips, and the preset manager rows all had frozen hover/selected fills. Each sortable now joins the motion-aware fill fade back into the inline value beside dnd's transform transition.
The context-menu toggle was the only way across; the strip's trailing spacer is now a real drop target (join the end cluster at the end), and landing beside a widget adopts that widget's cluster — same-region and cross-region alike, with stale end memberships normalized on entry. Collision detection prefers a direct pointer hit on the spacer over the region droppable that contains it, which otherwise shadowed the gesture into the nearest-chip fallback; a collision-level test pins the spacer geometry alongside the resolution tests.
Compose and Edit no longer place the Video widget; it stays addable everywhere and the Video preset keeps it. The pre-Video backfill migration is retired with it — its "untouched default" fingerprint now matched the current default again, so it re-added Video to every saved preset on load and made them drift instantly. Old snapshots that genuinely contain Video still get their instance created; tests that exercise Video place it explicitly first.
… drop chrome The strip-wide drop curtain hid the right-side target behind itself — finding it meant peering through a full-width overlay. During a widget drag the status bar now shows one rail-style drop zone per cluster, sized to its cluster, with the trailing zone carrying a to-line-end glyph and a minimum target even while empty. The chrome is deliberately not a new look: both zones render the same WidgetRegionDropOverlay the rails use (now accepting inset overrides so it cannot fork), gated on the shell's dropState so widgets the region rejects see the disallowed treatment instead of an inviting slot, and highlighting only over a zone's own background — over chips it previews a reorder, exactly like the rails. The shared DropZone's drag-over fill drops from the opaque accent.muted to a translucent accent tint so the content underneath stays legible everywhere it is used. Notifications ride the trailing cluster in every built-in preset. The end-to-end browser test drives the real pipeline with real browser input; its gesture deliberately runs outside act(), which starves dnd-kit's effect-driven callbacks into exactly the silent failure the test exists to catch.
joshistoast
requested review from
JPPhoto,
Pfannkuchensack,
blessedcoolant and
lstein
as code owners
September 3, 2026 23:26
joshistoast
enabled auto-merge
September 3, 2026 23:27
…pendium-02 # Conflicts: # invokeai/frontend/webv2/performance/architecture-baseline.json # invokeai/frontend/webv2/performance/browser-baseline.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This is a workbench polish compendium: a broad pass over layout, navigation, control feedback, and visual consistency.
Highlights
Workbench + status bar
Faster daily actions
Clearer, more consistent UI
Themes + settings
Suggested review path
Merge Plan
Frontend-only. Persisted layouts and the renamed Forest theme are normalized forward. No special merge handling is needed.