The CEO-runtime model dropdown (and the team-model pool) collapsed the
instant a user clicked to open it whenever the app received a background
snapshot refresh. Root cause:
useEffect(executionReadyConnectors, ...)
→ loadModels() loads catalog for every ready connector
→ setConnectorModelsById(Object.fromEntries(entries))
— always a brand-new object reference, even if the catalog
returned from the IPC is byte-identical to the previous load
→ connectorModels useMemo picks up a new reference
→ modelOptions useMemo picks up a new reference
→ <Select> re-renders its options children
→ Chromium's native <select> treats DOM mutation of <option>
children as "popup stale" and closes the dropdown.
Because snapshot refreshes fire on every domain-changed event
(credentials-changed, run-status, heartbeat-status, etc.), the user
effectively could not hold the dropdown open long enough to pick a
model.
Fix: structurally compare the freshly-loaded catalog against the
previous state and bail out of setState when the shape is identical
(same connector ids, same per-connector model lists in order, same
id/label/enabled flags). Same guard on the error-path reset so an
already-empty map does not trigger a re-render either.
typecheck 0 / lint 0-0 / test:unit 508/508 / build 0-0.
The CEO-runtime model dropdown on the Company onboarding step rendered
a {value:"", label:"默认 / Default"} sentinel as the first option, a
holdover from the adapter era when "" meant "let the connector pick".
Under pi-mono every connector carries a defaultModel and every run
must bind a concrete model id, so the sentinel is now misleading —
picking it left selectedModel empty and the wizard would advance
without a valid model.
- CompanyStep: drop the sentinel from the options array; the dropdown
now shows only real models (or a single "Loading..." entry while
the per-connector fetch is in flight).
- useConnectorModelSelection: when the connector's current model is
not enabled, auto-select the connector's defaultModel if enabled,
otherwise the first enabled model. An empty enabled list still
falls back to "" so the CompanyStep hint UI behaves correctly.
Result: Fire Pass now shows "Kimi K2.5 Turbo (Fire Pass)" as the
sole option (pre-selected). Every other connector picks its catalog
default up-front.
typecheck 0 / lint 0-0 / build 0-0 / test:unit 508/508.
Onboarding ProvidersStep now reads the 26-entry CONNECTOR_CATALOG and
renders featured connectors (claude-pro, openai-codex, anthropic, openai,
amazon-bedrock) up front with a collapsible advanced list for the
remaining 21. Each auth kind gets a dedicated card component: OAuth
button flow, inline API-key form, and a cloud deep-link to Settings.
Delete legacy Claude/Codex/Gemini hardcoded cards and the compatible-API
slot rotation. Swap ModelManagement Settings panel for the new
ProvidersPanel driven by credentialsList IPC.
Create src/renderer/components/ui/icons.ts as the single re-export point
for every Phosphor icon the renderer touches, then rewrite the 95 files
that previously imported directly from "@phosphor-icons/react" to pull
from the relative-path-to-barrel instead. The TitleBar commit earlier
in this series already used ui/icons; this commit extends the pattern
to the rest of the renderer.
Benefits now wired in:
- one location where bundle tree-shaking can be reasoned about,
- typos like Briefcase vs BriefCase show up against a finite list,
- re-theming or swapping icon sets is a one-file edit,
- a lint rule that flags raw @phosphor-icons/react imports can be
added later without mass-rewriting call sites.
The barrel also re-exports the `Icon` type so call sites that type
icon props against the Phosphor component type work unchanged.
Test count stays at 363 passing; typecheck stays at 156 errors.
OnboardingStepPanel previously forwarded ~50 props to seven step
components, so adding a single wizard field meant touching four layers.
Introduce a renderer-local React Context (OnboardingProvider /
useOnboarding) that carries all wizard state and actions (connectors,
node status, company inputs, model selection, execution mode, mission,
launch summary, locale, t, error/loading). Step panels now receive only
their navigation callbacks (onContinue / onBack / onLaunch / onFinish)
and read everything else via useOnboarding(). CompanyOnboarding wraps
the step panel tree in OnboardingProvider with a memoised value.
Business logic is unchanged: each onContinueFrom* transition, the
bootstrap call, and the welcome-step branch remain exactly as before;
this refactor only moves the data-passing mechanism.
All six step panels previously hard-coded "X / 5" or "X / 6" in their
header captions, which drifted out of sync with the 7-step STEP_ORDER in
OnboardingHero. Export STEP_ORDER / TOTAL_STEPS / getStepNumber from
OnboardingHero and consume them in every step so the counter is always
the single source of truth.