Skip to content

Commit aa31bab

Browse files
committed
fix: CreateTopControls crash — missing store fields
Reported on Discord by @diimmortalis (Ubuntu 24.04 + `npm run dev`) with a clean console-log dump. Two separate crashes, both from the same root cause: CreateTopControls.tsx destructured four fields from useCreateStore that were never added to the store. Stack traces: TypeError: can't access property "length", activeList is undefined CreateTopControls CreateTopControls.tsx:188 (open-dropdown render) TypeError: setComfyRunning is not a function current CreateTopControls.tsx:114 (comfy-start polling tick) Fix: - Add `imageModelList: ClassifiedModel[]`, `videoModelList: ClassifiedModel[]`, `comfyRunning: boolean` to createStore as runtime-only (not persisted) state, plus setters. - useCreate.fetchModels mirrors the fetched model lists into the store after `setImageModels` / `setVideoModelsList`. - useCreate.checkConnection mirrors the ComfyUI ping result into `comfyRunning` so the header Lichtschalter stays in sync with the deeper hook state. Folds into v2.3.9 under the "reported on Discord" banner.
1 parent fb58338 commit aa31bab

4 files changed

Lines changed: 29 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ All notable changes to Locally Uncensored are documented here.
1010
- (2) `src/hooks/useCreate.ts:fetchModels` only cleared the persisted image/video model names when ComfyUI returned a non-empty list. With 0 models, the stale strings stayed alive forever. Now fetchModels explicitly `state.setImageModel('', 'unknown')` + `state.setVideoModel('')` when the corresponding list is empty after the startup-race retries expire.
1111
- (3) `src/components/create/CreateView.tsx` renders a dedicated empty-state card when `connected === true && modelsLoaded && currentModeModels.length === 0`. The card shows `PackageOpen` icon + "No {image|video} models installed" + a **Go to Model Manager** primary button (calls `useUIStore.getState().setView('models')`) + a **Refresh list** secondary action (calls `fetchModels()`). `OutputDisplay` / `PromptInput` / `ParamPanel` / I2V + I2I uploads / preflight banners all suppress during the empty state so no downstream code can hit the old crash path. The Mode switcher (Image / Video) stays so the user can switch sides and get a matching empty-state.
1212

13+
- **CreateTopControls no longer crashes the Create header toggle + dropdown** — the header-level model picker + ComfyUI Lichtschalter lives at `src/components/create/CreateTopControls.tsx` and used to destructure four fields (`imageModelList`, `videoModelList`, `comfyRunning`, `setComfyRunning`) from `useCreateStore()` that were never added to the store. Clicking the dropdown threw `TypeError: can't access property "length", activeList is undefined`; clicking the Lichtschalter during starting/stopping threw `setComfyRunning is not a function`. Reported on Discord by @diimmortalis (with a precise console-log dump, bless them — Ubuntu 24.04 + `npm run dev`). Fix: the four missing fields are now part of `createStore` as runtime-only (not persisted) state; `useCreate.fetchModels` + `useCreate.checkConnection` mirror their values into the store so the header control always has a live list + the right toggle state.
1314
- **Backend Selector modal no longer spams on repeat** — users with multiple local backends (Ollama + LM Studio, Ollama + vLLM, etc) reported on Discord that the "N local backends detected" modal kept re-appearing every 5-10 seconds, regardless of whether they clicked Skip or Use selected. The pre-existing `sessionStorage` guard wasn't enough in the face of WebView2 reloads (which the backup-restore triad can trigger) or cache evictions. Fix: (1) added a persistent `hideBackendSelector` flag in `providerStore` so the user's opt-out survives reloads; (2) the modal now has a pre-checked "Don't show this again" tickbox; (3) a permanent explanatory line "You can add, remove, or switch backends anytime in **Settings → Providers**" (with the link clickable — it navigates you there); (4) dismissing the modal with the tickbox checked persists the opt-out. Users who want the modal back at some point can uncheck the box before dismissing.
1415
- **LU always starts in the Chat sidebar tab, not Code** — on a fresh install or an NSIS update, the left-sidebar tab (Chat / Code / Remote) could land on Code because `codexStore`'s persist middleware saved `chatMode` between sessions. Newcomers clicking around Code without any conversations got an empty screen. `codexStore` now excludes `chatMode` from `partialize` so the default (`'lu'`) is used on every fresh boot. If a user wants to stay in Codex or Claude Code mid-session, they pick it from the sidebar each time; `workingDirectory` still persists so Codex remembers the last project path.
1516
- Tiny grammar fix in the Create empty-state copy: "Install **an image** model" / "Install **a video** model" (was "Install a image model" in both branches).

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ Drop-in patch on top of v2.3.8.
4444
### What's fixed
4545
- **Create view: clean empty-state instead of a crash when no models are installed** — if you opened Create before downloading any ComfyUI model, the Create tab could go from unresponsive to a hard shutdown ("program disappears and a duplicate reopens"). Surfaced on Discord by @figuringitallout on a fresh Windows install. Root causes, both fixed: (1) `classifyModel()` was called with stale persisted model names that no longer existed on disk, (2) the main content area kept rendering `OutputDisplay` / `PromptInput` / `ParamPanel` against an empty model list. Create now detects this state and renders a calm empty-state card with a primary **Go to Model Manager** CTA and a **Refresh list** secondary action. Mode switcher stays available. Stale persisted image/video model names are proactively cleared by `useCreate.fetchModels` when ComfyUI reports 0 models in that category.
4646
- **App always starts in the Chat tab on boot, not Code**`codexStore` used to persist `chatMode` between sessions, so users who happened to end their last session in Codex / Claude Code would re-open the app inside an empty coding panel instead of the Chat homepage. Newcomers in particular landed in Code without any visible conversations and thought the app was broken. Fixed by excluding `chatMode` from the persisted slice — default `'lu'` (Chat) is used on every fresh boot; pick Code or Remote from the sidebar each session when you want them.
47+
- **Header-level Create toggle + dropdown no longer crash on click** — the Lichtschalter and model picker in the app header (when you're on Create) used to crash with `TypeError: activeList is undefined` / `setComfyRunning is not a function`. Reported on Discord by @diimmortalis with a clean console-log dump. Four store fields (`imageModelList`, `videoModelList`, `comfyRunning`, `setComfyRunning`) that the component expected were never actually in the store — now they are, and they're populated by the same `useCreate.fetchModels` / `checkConnection` path the main Create view uses.
4748
- **Backend Selector modal no longer reopens every 5-10 seconds** — users with multiple local backends (Ollama + LM Studio, Ollama + vLLM, etc) reported on Discord that the "N local backends detected" modal kept re-appearing no matter how often they dismissed it. Fix: a persistent `hideBackendSelector` flag in `providerStore`, a pre-checked "Don't show this again" tickbox in the modal, and a permanent pointer to **Settings → Providers** (clickable — it navigates you there) so users know where to manage providers going forward.
4849
- **`classifyModel(name)` is null-safe** — defensive guard returns `unknown` if called with `null`, `undefined`, or non-string, so any path that reaches the classifier with a stale or missing model name degrades gracefully.
4950
- **CONTRIBUTING.md dev workflow documented**`npm run tauri:dev` / `npm run tauri:build` / `npm run dev` are now spelled out with the trade-off between them. Reported in Discord by @k-wilkinson (sourceodin) — thanks.

src/hooks/useCreate.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ export function useCreate() {
5252
const checkConnection = useCallback(async () => {
5353
const ok = await checkComfyConnection()
5454
setConnected(ok)
55+
// Mirror into createStore so the header-level Lichtschalter in
56+
// CreateTopControls stays in sync with the deeper useCreate state.
57+
useCreateStore.getState().setComfyRunning(ok)
5558
return ok
5659
}, [])
5760

@@ -105,6 +108,14 @@ export function useCreate() {
105108
setSchedulerList(schedulers)
106109
setVideoBackend(vBackend)
107110

111+
// Mirror the fetched lists into createStore so the header-level
112+
// CreateTopControls dropdown (which does NOT host its own useCreate)
113+
// can render without crashing on undefined. Discord-reported by
114+
// @diimmortalis (console: `activeList is undefined`).
115+
const st = useCreateStore.getState()
116+
st.setImageModelList(imgModels)
117+
st.setVideoModelList(vidModels)
118+
108119
// If ComfyUI is connected but returns 0 models, do NOT set modelsLoaded — keep retrying.
109120
// ComfyUI may still be scanning directories (race condition on startup).
110121
if (imgModels.length === 0 && vidModels.length === 0) {

src/stores/createStore.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { create } from 'zustand'
22
import { persist } from 'zustand/middleware'
3-
import type { ModelType } from '../api/comfyui'
3+
import type { ModelType, ClassifiedModel } from '../api/comfyui'
44
import { classifyModel } from '../api/comfyui'
55
import type { PreflightError } from '../api/preflight'
66
// ModelType includes: flux, flux2, zimage, sdxl, sd15, wan, hunyuan, unknown
@@ -80,6 +80,12 @@ interface CreateState {
8080
preflightWarnings: string[]
8181
gallery: GalleryItem[]
8282
promptHistory: string[]
83+
/** Runtime-only (not persisted): populated by useCreate.fetchModels so the
84+
* header-level CreateTopControls can render its model dropdown + Lichtschalter
85+
* without hosting its own ComfyUI fetching. */
86+
imageModelList: ClassifiedModel[]
87+
videoModelList: ClassifiedModel[]
88+
comfyRunning: boolean
8389

8490
setPreflightStatus: (ready: boolean | null, errors: PreflightError[], warnings: string[]) => void
8591
setMode: (mode: 'image' | 'video') => void
@@ -110,6 +116,9 @@ interface CreateState {
110116
removeFromGallery: (id: string) => void
111117
clearGallery: () => void
112118
addToPromptHistory: (prompt: string) => void
119+
setImageModelList: (list: ClassifiedModel[]) => void
120+
setVideoModelList: (list: ClassifiedModel[]) => void
121+
setComfyRunning: (running: boolean) => void
113122
}
114123

115124
export const useCreateStore = create<CreateState>()(
@@ -147,6 +156,9 @@ export const useCreateStore = create<CreateState>()(
147156
preflightWarnings: [],
148157
gallery: [],
149158
promptHistory: [],
159+
imageModelList: [],
160+
videoModelList: [],
161+
comfyRunning: false,
150162

151163
setPreflightStatus: (ready, errors, warnings) => set({ preflightReady: ready, preflightErrors: errors, preflightWarnings: warnings }),
152164
setMode: (mode) => set((state) => {
@@ -227,6 +239,9 @@ export const useCreateStore = create<CreateState>()(
227239
const filtered = s.promptHistory.filter(p => p !== prompt)
228240
return { promptHistory: [prompt, ...filtered].slice(0, 50) }
229241
}),
242+
setImageModelList: (list) => set({ imageModelList: list }),
243+
setVideoModelList: (list) => set({ videoModelList: list }),
244+
setComfyRunning: (running) => set({ comfyRunning: running }),
230245
}),
231246
{
232247
name: 'create-store',

0 commit comments

Comments
 (0)