Repository navigation
Feat(GUI3): master-detail model view, remove preset rails (#2355 Slice 1) - #2424
Conversation
…e 1) - Add ModelListPanel: searchable, filterable listbox with funnel filter, compact model rows, keyboard nav (Arrow/Home/End), visible selected state. The only new icon is 'funnel' added to Icon.tsx. - Add ModelDetailPanel: title, key metadata, primary actions (Load/Unload/ Download/Delete/CancelPull), role=tablist with README (markdown-it + DOMPurify), Presets (loadApplied/saveApplied via presetStore.ts), and Files (stub, deferred). - Remove renderPresetRail() and all preset-rail state from ModelManager.tsx (presetRailCollapsed, selectedRailPresetId, etc.). - Wire custom-model / omni-collection form back via ModelListPanel footer buttons and conditional overlay panel in the detail slot. - Fix WCAG scrollable-region-focusable: listbox gets tabIndex=0 when no item is selected; data-model-id attribute enables arrow-key selection. - Update tests/features.spec.ts test 20 for new layout (HF zone removed). - Update tests/a11y.spec.ts A46-A50 for master-detail interaction model. - Add A91-A105 covering: layout, search, funnel filter, listbox/option ARIA, keyboard nav, tablist structure, Presets tab, custom-model buttons, axe scan. - Update ACCESSIBILITY.md: Group E, bump test count to 121/7-skipped/0-failed. Deferred to follow-ups: Files tab, full preset-card polish, sorting, #2356. Part of #2355 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
A: README checkpoint derivation — deriveHFRepo() tries model.checkpoint, model.checkpoints.main, then first checkpoints value; strips :variant suffix; validates with /^[\w.-]+\/[\w.-]+\$/ before fetching. HF link also uses deriveHFRepo. ModelReadmeTab now takes model prop. B: Sort controls — labeled <select id='model-list-sort'> in ModelListPanel with Name (A-Z, default), Size, Last used, Download count. Graceful fallback to name when last_used/downloads data absent. C: Responsive list-first — at max-width:700px, manager--detail-mobile-open class toggles list/detail panels exclusively (not stacked). Back button (model-detail-panel__back-btn) with aria-label injected via onBack prop. Focus: detail heading on open, selected list item on Back. D: Presets tab Change — Change button (aria-haspopup=dialog, aria-expanded) on linked preset card opens inline role=dialog chooser listing compatible presets excluding current. Attach buttons in compatible list relabelled to Switch when a non-default preset is already linked. Tests: A106-A115 (10 new); full suite 131 passed / 7 skipped / 0 failed. ACCESSIBILITY.md: Group F added, test count updated. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reconciliation complete — fl0rianr 2026-06-25 clarificationsThis commit closes all 4 gaps from @fl0rianr's 6-point clarification on #2355. What changedA — README checkpoint derivation (tightened)
B — Sort controls (added)
|
…trip) (#2355) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
README tab now renders raw HTML correctly (root cause + fix) Root cause: The GUI3 model README tab built its markdown-it instance with Fix:
Tests: Added A116 (raw HTML renders as real DOM elements @fl0rianr thank you for the screenshot — that made the repro instant. Could you pull |
fl0rianr follow-up: the master-detail ModelListPanel dropped the original rail's pin/favorite affordance. Re-wire the existing client-local pin store (scoped `pinned_models` localStorage, no lemond) into ModelListPanel so pinned models float to the top. A11y: the per-row pin is a non-button span (avoids axe nested-interactive inside role="option"); keyboard/AT users toggle via aria-keyshortcuts="P" on the focusable selected row; pinned state is exposed in the row aria-label. Tests A118-A123 added; full suite 139 passed / 7 skipped / 0 failed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Round update — left-rail parity (pin/favorite) + clarification on #2355 feedback@fl0rianr Following your latest #2355 feedback ("real left rail still missing its features" + "presets still in cards layout"), here's what shipped this round and what I need from you. Shipped (this commit):
The new rail already carries search (Ctrl+K), type filter (funnel), sort (name/size/last-used/downloads), per-row status (running pulse / downloaded dot / live %), size badge, backend chip, capability icon, live count, and full keyboard nav. Open clarifications (posted in detail on #2355, link):
Holding the larger rail items until you confirm — didn't want to over-build on a guess. |
Builds the missing LEFT navigation rail (ModelNavRail) to match fl0rianr's canonical 3-pane target: left nav rail + existing ModelListPanel (middle) + ModelDetailPanel (right).
Left rail (client-side derived, no lemond):
- Primary nav: All Models / Downloaded / My Models / Favorites with counts + aria-current; filters the middle list.
- Collapsible Categories (All/LLM/Omni/Image/Audio/TTS/Embed) reusing the existing filterTab state.
- Backends select (distinct recipes + counts) filters by recipe.
- Collapsible Tags (model-family + size chips) with aria-pressed; filters the list.
- Storage meter as role=progressbar; used derived from downloaded sizes, total is MOCK (no client disk-usage source, lemond off-limits).
Also per fl0rianr:
- Status stays a simple dot (no separate Downloaded badge).
- Moved + Custom model / + Omni collection buttons from the bottom footer to a grounded button group with background at the TOP of the list area.
- Presets remain in the model-detail Presets tab ('2) a'); global Presets page untouched.
Shared filter predicates exported from ModelListPanel are the single source of truth for both the middle-list filtering and the rail counts. Responsive: rail collapses behind a keyboard-reachable Filters toggle at <=700px.
Tests: A124-A136 (13 new) in tests/a11y.spec.ts. Full suite 152 passed / 7 skipped / 0 failed. ACCESSIBILITY.md Group G added.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
GUI3 left navigation rail — three-pane model view shipped (PR #2424)@fl0rianr I built the missing left navigation rail to match your canonical 3-pane target screenshot. The middle ( Left rail (top → bottom), all derived CLIENT-SIDE from the model list already loaded —
Your specific notes applied:
MOCK data note: there's no client-available disk-usage source and Tests: added A124–A136 (13 new a11y tests). Full suite 152 passed / 7 skipped / 0 failed. WCAG 2.1 AA: keyboard nav, One question for you: the rail's top "Search presets…" + "+ New" — in your screenshot it sits above the model nav. Since presets now live in the model-detail Presets tab, I shipped these as accessible POC placeholders (labelled input + Please verify against your target screenshot 🙏 |
#2424) fl0rianr feedback: the model-detail Presets tab rendered linked + compatible presets as full-width stacked rows. Restyle so the linked preset is a single highlighted card and recommended presets render as a neat responsive grid of compact cards (icon + name, short description, compact temp/ctx/tools meta, Attach/Switch action) consistent with the global preset (.recipe-card) style but smaller and focused. a11y: grid is a ul role=list of li cards (not role=listbox/option) to avoid nested-interactive with per-card Attach buttons; linked/active state via aria-current + text badges (not color only); each Attach/Switch button names its preset + model; inline Change dialog preserved; Browse/Manage Presets deep-link via client-local lemonade:navigate event. Tests A137-A141; 157 passed / 7 skipped / 0 failed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Model-detail Presets tab restyled — rows → neat compact card grid@fl0rianr — addressed your 18:12Z note: "The presets are not neat cards — a slightly smaller, focused version of the cards in the global preset view." Before: Linked + Compatible presets rendered as full-width stacked rows (Quality / Preview / Turbo with a Switch button each). After:
Behavior preserved: inline a11y: grid is a Tests: added A137–A141 and updated the prior Presets-tab assertions. Full Playwright suite: 157 passed / 7 skipped / 0 failed. Could you verify against your target screenshot (the "Recommended Presets" grid in the 3-pane mock)? One check for you: I matched the global |
…ilter & scroll fixes 1. Add a favorite star (aria-pressed) toggle to ModelDetailPanel that reuses the existing client-local pin store; Favorites nav count reflects it. 2. Remove the 'Search presets' box and '+ New' button from ModelNavRail (model search lives in the middle pane). 3. Hide 'Back to models' at desktop widths; keep it for narrow/mobile viewports. 4. Scope the funnel filter to capabilities only and give the popover a solid opaque background (was undefined --surface-overlay -> transparent). 5. Fix the nav-rail layout clipping: .manager--detail inherited grid-template-rows auto 1fr from .manager, so panes grew past the viewport; bound the row with minmax(0,1fr) so the rail scrolls independently and the Storage meter stays reachable. Tests A142-A148 added; A136 updated. 164 passed / 7 skipped / 0 failed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
@fl0rianr — pushed fixes for all five review items on PR #2424 (commit 9ac168b):
Tests: added A142–A148, updated A136. Full suite 164 passed / 7 skipped / 0 failed; Could you confirm these match what you had in mind? Happy to adjust the star's placement or the rail behaviour. |
|
Good but still some fixes ahead:
If those points work we can merge. |
1. Favorites is now a DISTINCT client-local store (favorite_models) from Pinned (pinned_models). Detail star toggles favorite; left-rail Favorites uses a star icon and counts favorites. Pinned still floats rows to the top. 2. Move the 'no model selected' empty state out of the middle list into the right ModelDetailPanel (noModelsAvailable prop); the middle list only shows a search-no-match message. 3. Funnel filters by functional capability tags (multi-select) derived from the model data; middle-panel rows render multiple capability icons each with an accessible label. Popover stays opaque (--surface-3). 4. Storage meter consumes real disk stats via api.getStorageInfo() when lemond provides them, else a graceful derived estimate (no 32/512 literal). POC has no real disk source yet — surfaced to fl0rianr on the PR. 5. A 'Hugging Face' nav entry appears in the left rail below My Models/Favorites when a model search yields HF matches, shows the live count, and filters the middle list to the HF results; it clears when the search is cleared. Tests: A149-A153 added; A143/A145/A146 updated. 168 passed / 7 skipped / 0 failed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
@fl0rianr — pushed the 5 merge items (commit 8f008f1). All 168 Playwright tests pass (7 skipped, 0 failed). 1. Favorites ≠ Pinned (separated). New client-local store 2. "No model selected" moved to the right pane. Removed the empty-state 3. Funnel = capability tags + multi-icon rows. The funnel is now a MULTI-SELECT of functional capabilities (Popular, Chat, Omni, Vision, Tool use, Reasoning, Code, Audio, Image, Speech, Embeddings, Reranking) derived from the model labels — a model can have several. Each middle-row renders one small icon per capability (each with an accessible label). Popover background stays opaque ( 4. Storage = real used/total, no 32/512 mock — NEEDS YOUR INPUT. I removed the hardcoded literals and wired 5. Hugging Face nav entry. When the model search triggers an HF search with matches, a "Hugging Face" entry appears in the left rail BELOW My Models and Favorites, showing the live count of HF matches. Clicking it filters the middle list to the HF results; it disappears (and resets to All Models) when the search is cleared. Tests: added A149–A153, updated A143/A145/A146. Could you confirm 1/2/3/5 look right and let me know your preference on the storage sourcing for #4? |
|
Sorry for the typo, i wanted to ask for #2356, we need a PR to solve that one. |
…e 1) (#2424) * Feat(GUI3): master-detail model view, remove preset rails (#2355 Slice 1) - Add ModelListPanel: searchable, filterable listbox with funnel filter, compact model rows, keyboard nav (Arrow/Home/End), visible selected state. The only new icon is 'funnel' added to Icon.tsx. - Add ModelDetailPanel: title, key metadata, primary actions (Load/Unload/ Download/Delete/CancelPull), role=tablist with README (markdown-it + DOMPurify), Presets (loadApplied/saveApplied via presetStore.ts), and Files (stub, deferred). - Remove renderPresetRail() and all preset-rail state from ModelManager.tsx (presetRailCollapsed, selectedRailPresetId, etc.). - Wire custom-model / omni-collection form back via ModelListPanel footer buttons and conditional overlay panel in the detail slot. - Fix WCAG scrollable-region-focusable: listbox gets tabIndex=0 when no item is selected; data-model-id attribute enables arrow-key selection. - Update tests/features.spec.ts test 20 for new layout (HF zone removed). - Update tests/a11y.spec.ts A46-A50 for master-detail interaction model. - Add A91-A105 covering: layout, search, funnel filter, listbox/option ARIA, keyboard nav, tablist structure, Presets tab, custom-model buttons, axe scan. - Update ACCESSIBILITY.md: Group E, bump test count to 121/7-skipped/0-failed. Deferred to follow-ups: Files tab, full preset-card polish, sorting, #2356. Part of #2355 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(GUI3): reconcile Slice 1 with fl0rianr clarifications (#2355) A: README checkpoint derivation — deriveHFRepo() tries model.checkpoint, model.checkpoints.main, then first checkpoints value; strips :variant suffix; validates with /^[\w.-]+\/[\w.-]+\$/ before fetching. HF link also uses deriveHFRepo. ModelReadmeTab now takes model prop. B: Sort controls — labeled <select id='model-list-sort'> in ModelListPanel with Name (A-Z, default), Size, Last used, Download count. Graceful fallback to name when last_used/downloads data absent. C: Responsive list-first — at max-width:700px, manager--detail-mobile-open class toggles list/detail panels exclusively (not stacked). Back button (model-detail-panel__back-btn) with aria-label injected via onBack prop. Focus: detail heading on open, selected list item on Back. D: Presets tab Change — Change button (aria-haspopup=dialog, aria-expanded) on linked preset card opens inline role=dialog chooser listing compatible presets excluding current. Attach buttons in compatible list relabelled to Switch when a non-default preset is already linked. Tests: A106-A115 (10 new); full suite 131 passed / 7 skipped / 0 failed. ACCESSIBILITY.md: Group F added, test count updated. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(GUI3): render raw HTML in model README (html:true + frontmatter strip) (#2355) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(GUI3): restore client-local pin/favorite to model rail (#2355) fl0rianr follow-up: the master-detail ModelListPanel dropped the original rail's pin/favorite affordance. Re-wire the existing client-local pin store (scoped `pinned_models` localStorage, no lemond) into ModelListPanel so pinned models float to the top. A11y: the per-row pin is a non-button span (avoids axe nested-interactive inside role="option"); keyboard/AT users toggle via aria-keyshortcuts="P" on the focusable selected row; pinned state is exposed in the row aria-label. Tests A118-A123 added; full suite 139 passed / 7 skipped / 0 failed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(gui3): add left navigation rail for three-pane model view (#2355) Builds the missing LEFT navigation rail (ModelNavRail) to match fl0rianr's canonical 3-pane target: left nav rail + existing ModelListPanel (middle) + ModelDetailPanel (right). Left rail (client-side derived, no lemond): - Primary nav: All Models / Downloaded / My Models / Favorites with counts + aria-current; filters the middle list. - Collapsible Categories (All/LLM/Omni/Image/Audio/TTS/Embed) reusing the existing filterTab state. - Backends select (distinct recipes + counts) filters by recipe. - Collapsible Tags (model-family + size chips) with aria-pressed; filters the list. - Storage meter as role=progressbar; used derived from downloaded sizes, total is MOCK (no client disk-usage source, lemond off-limits). Also per fl0rianr: - Status stays a simple dot (no separate Downloaded badge). - Moved + Custom model / + Omni collection buttons from the bottom footer to a grounded button group with background at the TOP of the list area. - Presets remain in the model-detail Presets tab ('2) a'); global Presets page untouched. Shared filter predicates exported from ModelListPanel are the single source of truth for both the middle-list filtering and the rail counts. Responsive: rail collapses behind a keyboard-reachable Filters toggle at <=700px. Tests: A124-A136 (13 new) in tests/a11y.spec.ts. Full suite 152 passed / 7 skipped / 0 failed. ACCESSIBILITY.md Group G added. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(gui3): restyle model-detail Presets tab as neat compact card grid (#2424) fl0rianr feedback: the model-detail Presets tab rendered linked + compatible presets as full-width stacked rows. Restyle so the linked preset is a single highlighted card and recommended presets render as a neat responsive grid of compact cards (icon + name, short description, compact temp/ctx/tools meta, Attach/Switch action) consistent with the global preset (.recipe-card) style but smaller and focused. a11y: grid is a ul role=list of li cards (not role=listbox/option) to avoid nested-interactive with per-card Attach buttons; linked/active state via aria-current + text badges (not color only); each Attach/Switch button names its preset + model; inline Change dialog preserved; Browse/Manage Presets deep-link via client-local lemonade:navigate event. Tests A137-A141; 157 passed / 7 skipped / 0 failed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * GUI3 #2424: address fl0rianr review — favorites star, rail cleanup, filter & scroll fixes 1. Add a favorite star (aria-pressed) toggle to ModelDetailPanel that reuses the existing client-local pin store; Favorites nav count reflects it. 2. Remove the 'Search presets' box and '+ New' button from ModelNavRail (model search lives in the middle pane). 3. Hide 'Back to models' at desktop widths; keep it for narrow/mobile viewports. 4. Scope the funnel filter to capabilities only and give the popover a solid opaque background (was undefined --surface-overlay -> transparent). 5. Fix the nav-rail layout clipping: .manager--detail inherited grid-template-rows auto 1fr from .manager, so panes grew past the viewport; bound the row with minmax(0,1fr) so the rail scrolls independently and the Storage meter stays reachable. Tests A142-A148 added; A136 updated. 164 passed / 7 skipped / 0 failed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address fl0rianr's 5 merge-blocking items on PR #2424 (GUI3 model view) 1. Favorites is now a DISTINCT client-local store (favorite_models) from Pinned (pinned_models). Detail star toggles favorite; left-rail Favorites uses a star icon and counts favorites. Pinned still floats rows to the top. 2. Move the 'no model selected' empty state out of the middle list into the right ModelDetailPanel (noModelsAvailable prop); the middle list only shows a search-no-match message. 3. Funnel filters by functional capability tags (multi-select) derived from the model data; middle-panel rows render multiple capability icons each with an accessible label. Popover stays opaque (--surface-3). 4. Storage meter consumes real disk stats via api.getStorageInfo() when lemond provides them, else a graceful derived estimate (no 32/512 literal). POC has no real disk source yet — surfaced to fl0rianr on the PR. 5. A 'Hugging Face' nav entry appears in the left rail below My Models/Favorites when a model search yields HF matches, shows the live count, and filters the middle list to the HF results; it clears when the search is cleared. Tests: A149-A153 added; A143/A145/A146 updated. 168 passed / 7 skipped / 0 failed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>


Summary
Implements Slice 1 of the master-detail model view (#2355). Replaces the old preset-rail model/preset flow with an email-client-style split layout in
prototype/ui-redesign/.Part of #2355
What shipped in Slice 1
Left panel —
ModelListPanelrole=listboxwith compact model rows (name, size, backend badge, downloaded dot, capability badge)'funnel'added toIcon.tsx) withrole=dialogpopoveraria-selected="true"+.model-list-item--selected)+ Custom model/+ Omni collectionbuttons in footer — wired to existingopenCustomForm()in ModelManagerRight panel —
ModelDetailPanelaria-labelsrole=tablistwith tabs: README (fetch + markdown-it + DOMPurify), Presets (loadApplied/saveAppliedviapresetStore.ts), Files (stub)Preset linking (Presets tab)
aria-current="true") and compatible presetspresetStore.ts(localStorage — never in lemond)role=status aria-live=politeattach confirmationPreset rail removed
renderPresetRail()(~2344-2395) removed per planpresetRailCollapsed,selectedRailPresetId, etc.) removedAccessibility (WCAG 2.1 AA — non-negotiable)
All keyboard and screen-reader patterns are implemented: searchbox label, listbox/option ARIA, funnel popover, tablist/tab/tabpanel, attach action, aria-live regions.
Tests: 121 passed / 7 skipped / 0 failed
Files changed
src/components/ModelDetailPanel.tsxsrc/components/ModelListPanel.tsxsrc/components/ModelManager.tsxsrc/components/Icon.tsx'funnel'iconsrc/styles/styles.csstests/a11y.spec.tstests/features.spec.tsACCESSIBILITY.mdDeferred to follow-up slices