Uh oh!
There was an error while loading. Please reload this page.
feat(ui): Item row primitive + selection-led first-run & 模型 polish - #162
Merged
Conversation
The first-run provider tiles dropped a 32px provider logo into an 18px grid track, clipping the logo and overlapping the title. Rebuild the list on a new shared `Item` primitive (shadcn Item rewritten onto Base UI `useRender`) whose media slot is `shrink-0` with no fixed width, so it sizes to the icon it wraps and that class of mismatch cannot recur. - packages/ui: new primitives/item.tsx (Item / ItemMedia / ItemContent / ItemTitle / ItemDescription / ItemActions); exported from index. The clickable row hover stays neutral (a faint foreground wash), reserving the brand `accent` for active/selected rows. - OnboardingHero: provider list now renders through Item; the dead .maka-onboarding-card grid CSS that caused the clip is removed. - tests: re-target the hover-neutral and shared-primitive contracts to the new primitive instead of the deleted CSS class.
Redesign the needs_connection first-run hero around picking a provider: title leads, the three setup steps compress into one quiet numbered stepper, and a scrollable provider list (Item rows) is the subject. - Vendor official brand SVGs from @lobehub/icons-static-svg (MIT) in provider-brand-marks.tsx; ProviderLogoMark now delegates to them, so Settings 模型 rows get the same real icons (Claude/OpenAI/Gemini/ DeepSeek/Moonshot/Zhipu/Ollama) instead of hand-drawn SVGs. - Neutral, theme-aware provider plate (drops hardcoded light tints). - De-version provider copy: highlight the provider, not model versions (no "GPT/Responses", no "V3"); update the OAuth-section contract test expectations to match. - Remove now-dead .maka-onboarding-grid block and the orphaned maka-card-enter / maka-agents-fade-in keyframes. Brand marks ship under their owners' trademarks; SVGs are MIT-licensed.
The enabled-models band was a horizontal scroller; once a few models were enabled the chips ran off the right edge and got cut mid-word. Put the header on its own row and let the chips wrap below, with a max-height + vertical scroll for long lists, so every enabled model stays fully visible.
- Enabled-models strip: a flex-wrap left every chip a different width (sized to its own text) so rows never lined up. Lay the chips on an even auto-fill grid and truncate long names, so columns align and the band reads as one tidy group. - Catalog pill tabs: hide Base UI's sliding rounded-md indicator. Our pills supply their own fill, and the indicator's square corners poked out from behind the 999px pill, making the selected state look warped.
Wrap Base UI's Accordion (Root / Item / Header / Trigger / Panel) the same way the Tabs primitive is wrapped (cn + data-slot), and export the Primitive-prefixed aliases from @maka/ui. Gives the design system a single reusable, governed disclosure component instead of hand-rolled expand/collapse.
The enabled-models list flattened providers and connections into one
peer level, which read wrong (a connection and its provider looked like
siblings) and felt cramped. Rebuild it as a Base UI Accordion: each
provider is a collapsible row that owns its connections, with the worst
connection status rolled up to the header so problems show while
collapsed. The provider holding the default connection and any failing /
needs-relogin provider start expanded; healthy ones stay compact.
Connection rows render on the shared Item primitive; the old bespoke
chip grid and its CSS are removed. Contract tests updated: the @maka/ui
import check tolerates multi-line formatting and now also pins the
Accordion + Item primitives, and the no-raw-button rule allows Base UI's
`render={<button/>}` polymorphic target.The zai-coding-plan provider reused the legacy 智谱 (Zhipu) molecule mark,
but the product surfaced here ('Z.AI Coding Plan') carries the Z.ai
wordmark — a stylized 'Z' of three slanted strokes. Vendor the official
Z.ai mark (monochrome, currentColor) from @lobehub/icons-static-svg and
point the zai-coding-plan case at it.The enabled-models accordion sat in a bordered, filled card — the heaviest element on the 模型 page, clashing with the catalog grid below and reading as 'two visual languages'. Drop the card box for a seamless hairline-bounded list (whitespace + logo plates carry the structure), and align the columns: connection rows now sit directly under the provider name (pl 49 = pad-x 6 + logo 32 + gap 11) with their status right edge aligned to the provider rollup (pr 32 = pad-x 6 + chevron 15 + meta gap 11). Healthy statuses stay quiet; only problems take color.
The 模型 catalog (国内/海外/本地 cards) and the OAuth login cards were hand-written: a Button (or, when gated, a raw <div>) wrapping <span> copy styled by bespoke .providerCatalogCard CSS. They were the last part of the page not on the shared shadcn Item primitive, so the page still mixed two component languages. Render every catalog + OAuth entry as a governed Item (ItemMedia · ItemContent[ItemTitle/ItemDescription] · ItemActions) — the same primitive the enabled connections use — in a seamless single-column hairline list. Gated providers become an inert div Item (no render prop) with a real trailing state badge instead of an absolute ::after, so the provider-surface gate still keeps them unclickable. Removes the dead .providerCatalogCard CSS skin. The two contract tests that pinned the old card geometry (8px radius, fixed 172px height, auto-fill 198px grid, .providerCatalogCard/Copy class names, ::after badges) are repointed to the new intent: governed .providerCatalogRow chrome shared across all tabs, single-column seamless list, squared (non-pill) badges. Typecheck + all 1467 desktop tests pass.
The category / preview / login badge sat inside ItemTitle (the row's first line), so it pinned to the title baseline instead of the row center on two-line rows. Move it into ItemActions next to the chevron — a direct child of the row's items-center flex — so badge and chevron share the row's vertical center.
Two papercuts found while testing the running app:
- The model page opened with a '.settingsPageIntro' row that paired a
non-actionable hint ('如果配置遇到问题,可以查看配置指南。' — plain
text, no link) with a 'N 个模型' badge that just restated the count
already shown by the 已启用模型 header right below it. Removed the
whole intro row (and its now-orphan CSS); the page starts at the
enabled-models list.
- The unified row lists carried BOTH a container top+bottom border AND
inner row separators, so the catalog stacked a line under the tabs and
doubled a line against the 自定义供应商 border. Drop the container
borders on all three lists (enabled / market / oauth); inner hairlines
between rows now carry the separation on their own.Convert ConnectionDetail's 连接标识 / 服务地址 / 模型密钥 rows from the hand-written <label><span/> markup to the governed Base UI Field primitive (FieldRoot + Label + FieldDescription from @maka/ui), which already existed but had no consumers. The label tone is pinned to the established Settings caption (text-xs / foreground-60) so the rows stay visually identical; the parenthetical state hints (OAuth 固定 / 已设置…) now render on their own FieldDescription line under the label. Scoped to this one sheet as a pilot: AddProviderForm and every other Settings form stay on the legacy markup. The model-oauth copy contract is repointed to the new Field structure while keeping the copy Chinese-first.
The provider config sheet's close button is absolutely positioned in the top-right corner (.providerConfigSheetClose, 30px at right:14px), but the ConnectionDetail header's right-aligned status badges (默认 / 国内) were laid out flush to the content edge and slid underneath the X. Reserve the corner with a 36px right padding on .providerEditor header so the badges (and any long provider title) clear the close affordance.
…buttons Every governed @maka/ui <Button> in ProvidersPanel also carried a className="maka-button". That legacy class is defined in maka-tokens.css under @layer components, but the Button primitive's own styling lives in @layer utilities, which wins under Tailwind v4 layer ordering — so the maka-button class was inert dead weight on these buttons (colors, size, border, radius, hover, focus and disabled all came from the Button variant, not maka-button). Remove all 18 occurrences. Repoint the three model-oauth contract assertions that pinned the class, and add an a11y-contract guard so a governed Button can't silently regrow the legacy class. The legacy maka-button system stays in place for the raw <button> holdouts in other surfaces (composer, memory, network gateway) that still rely on it.
…tton Three layout fixes on the provider config sheet, verified live via CDP: - Refresh button stranded ~36px from the card's right edge. Root cause: the close-button clearance rule was written as the descendant selector `.providerEditor header`, which also matched the nested `.modelTableHeader` and leaked its `padding-right: 36px` onto the model card, capping the right-aligned button short of the edge. Scope it to the direct child `.providerEditor > header` so only the sheet's own title/badges header reserves the corner. - Model table header switched to `align-items: flex-start` so the refresh button sits on the title line instead of floating mid-block, and the grid gets an explicit `minmax(0, 1fr)` column so the header spans the full card width. - Close button raised to `top: 10px` so it centers on the header's first line, lining up with the title and the right-aligned status badges.
.providerConfigSheetClose hand-rolled rest/hover/focus styles on top of the governed Button but leaned on --settings-surface-* / --text* tokens that were never defined, so hovering the X did nothing. Strip the class down to position + circular shape and let the Button's variant="quiet" size="icon-sm" own the rest/hover/focus states. Update the contract test to assert the close button reuses the quiet icon Button, replacing the assertion on the now-removed hand-written focus outline.
…m-primitive # Conflicts: # apps/desktop/src/renderer/OnboardingHero.tsx
…safe
Item is routinely rendered as a <button> (provider rows, OAuth cards,
connection rows), but ItemMedia/ItemContent/ItemTitle/ItemActions defaulted to
<div> and ItemDescription to <p> — flow content that is invalid inside a button
and trips React DOM-nesting checks. Default the sub-slots to <span>; the
flex/line-clamp classes lay them out identically, and a span is valid inside
both a button and a div, so call sites never need render={<span/>}.
Add a renderToStaticMarkup regression test asserting a clickable Item emits a
<button> with no <div>/<p> inside.…logins The Settings → 模型 band was titled "已启用模型" but grouped and counted every connection, and labeled OAuth subscription logins that had lapsed (stored as enabled:false + needs_reauth) as "已禁用". A connection that only needed re-login read as user-disabled, and the enabled-only rollup left a group holding only a lapsed login showing idle at the header. - Rename the band to「模型连接」(it is the configured-connection list); count and empty state stay on the full list, now honestly named. - chipStatusText: needs_reauth wins over the disabled check (需要重新登录); a bare enabled:false (legacy migration only) reads 暂不可用, never 已禁用. - rollupForGroup spans the full group, so a lapsed-only group raises a warn header and auto-expands instead of reading idle. - Extract chipStatusText/rollupForGroup into a pure provider-connection-status module with direct behavioural tests; relax the contract test to verify the dedicated helper import. The deeper enabled/auth state-model decoupling and the chat-readiness / health copy stay as the documented follow-up.
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 freeto 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.
What
Started as a first-run overflow fix and grew into a focused polish of the model-connection surfaces (first-run hero + Settings → 模型). The root overflow bug was a 32px provider logo dropped into an 18px grid track. Rather than patch the bespoke grid, the rows were rebuilt on two new governed
@maka/uiprimitives (Item,Accordion), and the surrounding first-run page and the Settings model surfaces were tidied up in the same vein.Changes
Shared primitives (
@maka/ui)item.tsx(new):Item / ItemMedia / ItemContent / ItemTitle / ItemDescription / ItemActionson Base UIuseRender+ ourcn/cva. The media slot isshrink-0with no fixed width, so it sizes to whatever icon it wraps and the icon-track mismatch cannot recur. Clickable-row hover stays neutral (faintforegroundwash), reserving brandaccentfor active rows. Sub-slots default tospan(notdiv/p) so a row renderedas={button}stays valid HTML.accordion.tsx(new): governed Base UI Accordion, used by the model-connections list.First-run hero (needs_connection), selection-led redesign
Itemrows) is the subject of the page..maka-onboarding-card/.maka-onboarding-gridgrid CSS (and its orphaned keyframes) is removed.Settings → 模型, model-connections section
Itemrows (was a horizontal scroller that clipped chips mid-word).enabled:falseis an OAuth subscription whose login lapsed (stored asenabled:false+lastTestStatus:'needs_reauth'; there is no user enable/disable toggle). Such a connection now reads「需要重新登录」instead of「已禁用」, and the group rollup spans the full group so a group holding only a lapsed login raises a warning header and auto-expands (it previously read idle/green). Status + rollup logic moved to a pureprovider-connection-status.tshelper with direct behavioural tests.Itemprimitive; piloted Base UIFieldon the model config sheet; restored the config-sheet close button to the governed quiet iconButtonand cleaned up the header alignment.Real brand icons + de-versioned copy (first-run + Settings 模型)
@lobehub/icons-static-svg(MIT) inprovider-brand-marks.tsx;ProviderLogoMarkdelegates to them, so both surfaces show real marks (Claude / OpenAI / Gemini / DeepSeek / Moonshot / Zhipu / Ollama) on a neutral, theme-aware plate.Verification
rive-workflow-toolchild-process reap) is a timing race under the parallel isolated runner; it passes 10/10 in isolation. Suite covers Item button-safety, the 模型连接 status/rollup behaviour, copy/contract, hover-neutral, and shared-primitive assertions.@maka/uibuild + renderer typecheck: pass.first-runselection-led layout + real icons + no overflow;provider-workspacestrip wraps with all fixtures visible, light/dark × 1280/990. The lapsed-login status/rollup states are covered by the behavioural unit tests rather than screenshots.Follow-up (not in this PR)
enabledfield in core. Todayenabled:falseoverloads "user-disabled", "not usable for chat", and "OAuth login lapsed". The section-level honesty is fixed here, but the same "请到设置启用" framing is still wrong inchat-readiness/onboarding/health, andconnection-status.tsstill maps a lapsed login to a "已禁用" presentation. Splittingenabledfrom auth/readiness status (and fixing the matching copy across the chain) is a separate, larger change. Reviewed from first principles via codex + an independent agent.Itemrows is a different UX (browse vs pick-one) and deserves its own mock/review.