feat(m1): playable loop — story graph, story panel, queue UI #14

Merged
ginnoir merged 20 commits from feat/m1-playable-loop into main 2026-06-11 19:26:54 -05:00
Showing only changes of commit a69585e89c - Show all commits
+31 -1
View File
@@ -1,4 +1,5 @@
import { create } from 'zustand';
import { getPrefs, setPrefs as persistPrefs, type GamePrefs } from './prefs';
import type { GameView } from './viewModel';
/**
@@ -9,10 +10,26 @@ import type { GameView } from './viewModel';
const MAX_LOG_LINES = 50;
export interface StoryLogEntry {
nodeId: string;
prose: string;
choiceLabel?: string;
}
export interface GameStoreState extends GameView {
log: string[];
storyPanelOpen: boolean;
storyHasUnread: boolean;
storyLog: StoryLogEntry[];
prefs: GamePrefs;
settingsOpen: boolean;
setView: (view: GameView) => void;
appendLog: (line: string) => void;
appendStoryLog: (entry: StoryLogEntry) => void;
setStoryPanelOpen: (open: boolean) => void;
setStoryHasUnread: (unread: boolean) => void;
setPrefs: (partial: Partial<GamePrefs>) => void;
setSettingsOpen: (open: boolean) => void;
}
export const useGameStore = create<GameStoreState>((set) => ({
@@ -25,6 +42,19 @@ export const useGameStore = create<GameStoreState>((set) => ({
actions: [],
story: { currentProse: null, choices: [] },
log: [],
setView: (view) => set(view),
storyPanelOpen: false,
storyHasUnread: false,
storyLog: [],
prefs: getPrefs(),
settingsOpen: false,
setView: (view) => set((state) => ({ ...state, ...view })),
appendLog: (line) => set((state) => ({ log: [...state.log, line].slice(-MAX_LOG_LINES) })),
appendStoryLog: (entry) => set((state) => ({ storyLog: [...state.storyLog, entry] })),
setStoryPanelOpen: (open) => set({ storyPanelOpen: open }),
setStoryHasUnread: (unread) => set({ storyHasUnread: unread }),
setPrefs: (partial) => {
const prefs = persistPrefs(partial);
set({ prefs });
},
setSettingsOpen: (open) => set({ settingsOpen: open }),
}));