[eric] onboarding: v3 connect-first flow, live theme pad, personalized reveal

This commit is contained in:
ciregenz
2026-07-15 00:29:11 -07:00
parent 164f5c6c06
commit 910f87f270
17 changed files with 998 additions and 11 deletions
@@ -1369,7 +1369,7 @@ const dashboardLayoutSlice = createSlice({
addNote(
state,
action: PayloadAction<{ x?: number; y?: number; expandedSessionIds?: string[]; color?: NoteColor }>,
action: PayloadAction<{ x?: number; y?: number; expandedSessionIds?: string[]; color?: NoteColor; content?: string }>,
) {
const id = `note-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
let posX: number, posY: number;
@@ -1388,7 +1388,7 @@ const dashboardLayoutSlice = createSlice({
y: posY,
width: DEFAULT_NOTE_W,
height: DEFAULT_NOTE_H,
content: '',
content: action.payload.content ?? '',
color: action.payload.color || 'yellow',
zOrder: state.nextZOrder++,
};
@@ -0,0 +1,45 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import type { PersonalizedStarter } from '@/shared/state/settingsSlice';
// Transient bridge between the v3 flow overlay and the dashboard: the overlay finishes, stashes the prep payload here, and the dashboard's reveal hook consumes it exactly once to seed the canvas.
export interface OnboardingV3State {
/** True while the full-screen flow owns the window; suppresses onboarding v2. */
flowActive: boolean;
/** One-shot: set on finish, cleared by the reveal seeder after cards land. */
revealPending: boolean;
greeting: string | null;
starters: PersonalizedStarter[];
scanSummary: string | null;
}
const initialState: OnboardingV3State = {
flowActive: false,
revealPending: false,
greeting: null,
starters: [],
scanSummary: null,
};
const onboardingV3Slice = createSlice({
name: 'onboardingV3',
initialState,
reducers: {
setFlowActive(state, action: PayloadAction<boolean>) {
state.flowActive = action.payload;
},
stageReveal(state, action: PayloadAction<{ greeting: string | null; starters: PersonalizedStarter[]; scanSummary: string | null }>) {
state.revealPending = true;
state.greeting = action.payload.greeting;
state.starters = action.payload.starters;
state.scanSummary = action.payload.scanSummary;
},
clearReveal(state) {
state.revealPending = false;
state.scanSummary = null;
},
},
});
export const { setFlowActive, stageReveal, clearReveal } = onboardingV3Slice.actions;
export default onboardingV3Slice.reducer;
@@ -75,6 +75,17 @@ export interface AppSettings {
signin_method?: 'google' | 'email' | 'stripe' | null;
/** Anonymous device id (first-run generated); stitches anon to authed PostHog Persons. */
installation_id?: string | null;
/** Onboarding v3 lifecycle: absent = never seen, 'done'/'skipped' once resolved. */
onboarding_v3?: string | null;
/** User-picked accent hex from the onboarding theme pad; null = stock accent. */
accent_color?: string | null;
personalized_greeting?: string | null;
personalized_starters?: PersonalizedStarter[];
}
export interface PersonalizedStarter {
title: string;
prompt: string;
}
export interface ActivateSubscriptionPayload {
+2
View File
@@ -18,6 +18,7 @@ import subscriptionsReducer from './subscriptionsSlice';
import workflowsReducer from './workflowsSlice';
import missedRunsReducer from './missedRunsSlice';
import onboardingProgressReducer from '@/shared/state/onboardingProgressSlice';
import onboardingV3Reducer from '@/shared/state/onboardingV3Slice';
export const store = configureStore({
reducer: {
@@ -40,6 +41,7 @@ export const store = configureStore({
workflows: workflowsReducer,
missedRuns: missedRunsReducer,
onboardingProgress: onboardingProgressReducer,
onboardingV3: onboardingV3Reducer,
},
// Disable Redux Toolkit's dev-mode invariant middleware (serializable + immutable checks). These deep-walk the entire state on every dispatch, and our state is large enough to trigger 30-50ms pauses on hot paths (agent streaming, websocket heartbeats, settings sync). Console warns "SerializableStateInvariantMiddleware took 41ms" repeatedly under load. Production builds skip these middlewares anyway, so disabling them in dev makes dev behavior match prod, no surprises at packaging time. Trade-off: serializability bugs (e.g. accidentally putting a Map or Date directly into state) won't be caught at dev time. We've shipped many versions with stable slice shapes; that risk is now low.
middleware: (getDefault) =>