diff --git a/src/renderer/src/state.ts b/src/renderer/src/state.ts index 0a04937..42d3bab 100644 --- a/src/renderer/src/state.ts +++ b/src/renderer/src/state.ts @@ -207,9 +207,10 @@ export function sessionSnapshot(state: AppState, genResults: string[]): unknown } return { version: SESSION_VERSION, - category: state.category, - operation: state.operation, - lastOperation: state.lastOperation, + // The category and its operation are deliberately NOT persisted as such: + // the app always launches into the first category, and each category's + // sub-page rides in lastOperation (kept current by setOperation). + lastOperation: { ...state.lastOperation, [state.category]: state.operation }, options: state.options, queues, genResults: genResults.slice(0, MAX_GEN) @@ -231,8 +232,6 @@ function isValidItem(i: unknown): i is QueueItem { } interface PersistedSession { - category: CategoryId - operation: string lastOperation: Partial> options: Record queues: AppState['queues'] @@ -254,12 +253,14 @@ export function parseSession(raw: unknown): { state: AppState; genResults: strin return false } } - // Two independent checks: a renamed OPERATION must not also discard the - // persisted category. - const category = validCat(p.category) ? p.category : FIRST_CATEGORY - const operation = findOperation(category, p.operation) - ? p.operation - : defaultOperation(category) + // Always launch into the FIRST category (owner decision: coming back to + // the remembered one felt wrong). What IS remembered is each category's + // last sub-page: Images still opens on Generate if that is where it was, + // and switching to Video lands on its own remembered operation. + const category = FIRST_CATEGORY + const remembered = (p.lastOperation ?? {})[category] + const operation = + remembered && findOperation(category, remembered) ? remembered : defaultOperation(category) // Drop queues for categories no longer in the catalog (a removed/renamed tool). const queues: AppState['queues'] = {} for (const [cat, q] of Object.entries(p.queues ?? {})) { diff --git a/test/session-persist.test.ts b/test/session-persist.test.ts index 7c0e9f7..a933588 100644 --- a/test/session-persist.test.ts +++ b/test/session-persist.test.ts @@ -105,10 +105,33 @@ describe('session persistence round-trip', () => { expect(pruned.state.queues.images!.selected).toEqual([]) }) - it('falls back to a valid category/operation when the persisted one is invalid', () => { - const snap = sessionSnapshot(initialState, []) as Record - const parsed = parseSession({ ...snap, category: 'nonsense', operation: 'nope' }) + it('always launches into the first category, whatever was open at close', () => { + // The category itself is not restored - only each category's sub-page is. + const state: AppState = { ...initialState, category: 'video', operation: 'compress' } + const parsed = parseSession(sessionSnapshot(state, [])) expect(parsed).not.toBeNull() expect(parsed!.state.category).toBe('images') }) + + it("remembers each category's last sub-page, including the one open at close", () => { + const state: AppState = { + ...initialState, + category: 'video', + operation: 'compress', + lastOperation: { images: 'generate', video: 'compress' } + } + const parsed = parseSession(sessionSnapshot(state, [])) + // Launches into Images ON its remembered sub-page... + expect(parsed!.state.category).toBe('images') + expect(parsed!.state.operation).toBe('generate') + // ...and Video's sub-page survives for when the user switches to it. + expect(parsed!.state.lastOperation.video).toBe('compress') + }) + + it('falls back to the default operation when the remembered one is gone', () => { + const state: AppState = { ...initialState, lastOperation: { images: 'renamed-away' } } + const parsed = parseSession(sessionSnapshot(state, [])) + expect(parsed!.state.category).toBe('images') + expect(parsed!.state.operation).toBe('convert') + }) })