From d8b1893739a2eee3eb6ec2d6b1d31dee3762c020 Mon Sep 17 00:00:00 2001 From: Pabloader Date: Fri, 27 Mar 2026 16:08:55 +0000 Subject: [PATCH] Move menu to the main tabs area --- ...enu-sidebar.module.css => menu.module.css} | 0 src/games/storywriter/components/app.tsx | 2 - src/games/storywriter/components/editor.tsx | 46 +++++++--------- .../components/{menu-sidebar.tsx => menu.tsx} | 55 +++++++++---------- src/games/storywriter/contexts/state.tsx | 23 +++----- 5 files changed, 54 insertions(+), 72 deletions(-) rename src/games/storywriter/assets/{menu-sidebar.module.css => menu.module.css} (100%) rename src/games/storywriter/components/{menu-sidebar.tsx => menu.tsx} (74%) diff --git a/src/games/storywriter/assets/menu-sidebar.module.css b/src/games/storywriter/assets/menu.module.css similarity index 100% rename from src/games/storywriter/assets/menu-sidebar.module.css rename to src/games/storywriter/assets/menu.module.css diff --git a/src/games/storywriter/components/app.tsx b/src/games/storywriter/components/app.tsx index 03cae6c..8829f34 100644 --- a/src/games/storywriter/components/app.tsx +++ b/src/games/storywriter/components/app.tsx @@ -1,4 +1,3 @@ -import { MenuSidebar } from "./menu-sidebar"; import { Editor } from "./editor"; import { ChatSidebar } from "./chat-sidebar"; import { Title } from "@common/components/Title"; @@ -13,7 +12,6 @@ export const App = () => { {currentStory ? {currentStory.title} - Storywriter : Storywriter} - diff --git a/src/games/storywriter/components/editor.tsx b/src/games/storywriter/components/editor.tsx index dd0a846..c19b5ef 100644 --- a/src/games/storywriter/components/editor.tsx +++ b/src/games/storywriter/components/editor.tsx @@ -8,10 +8,12 @@ import { CharacterEditor } from "./character-editor"; import { LocationEditor } from "./location-editor"; import { ChaptersEditor } from "./chapters-editor"; import { LoreEditor } from "./lore-editor"; +import { Menu } from "./menu"; import { useInputCallback } from "@common/hooks/useInputCallback"; import Prompt from "../utils/prompt"; const TABS: { id: Tab; label: string; right?: boolean }[] = [ + { id: "menu", label: "Menu" }, { id: "story", label: "Story" }, { id: "chapters", label: "Chapters" }, { id: "lore", label: "Lore" }, @@ -23,7 +25,7 @@ const TABS: { id: Tab; label: string; right?: boolean }[] = [ export const Editor = () => { const appState = useAppState(); - const { currentStory, dispatch } = appState; + const { currentStory, currentTab, dispatch } = appState; const handleInput = useInputCallback((text: string) => { if (!currentStory) return; @@ -36,12 +38,7 @@ export const Editor = () => { }, [currentStory?.id]); const handleTabChange = (tab: Tab) => { - if (!currentStory) return; - dispatch({ - type: 'SET_CURRENT_TAB', - id: currentStory.id, - tab, - }); + dispatch({ type: 'SET_CURRENT_TAB', tab }); }; const contentRef = useRef(null); @@ -65,10 +62,10 @@ export const Editor = () => { }, [currentStory?.scratchpad]); const promptPreview = useMemo(() => { - if (currentStory?.currentTab !== 'prompt') return ''; + if (currentTab !== 'prompt') return ''; const text = Prompt.formatSystemPrompt(appState); return highlight(text, false); - }, [currentStory?.currentTab, appState]); + }, [currentTab, appState]); useEffect(() => { if (currentStory?.lastEditedText) { @@ -91,19 +88,16 @@ export const Editor = () => { } }); return () => cancelAnimationFrame(raf); - }, [currentStory?.id, currentStory?.currentTab]); - - if (!currentStory) { - return
; - } + }, [currentStory?.id, currentTab]); return (
-
- {currentStory.title} -
+ {currentStory &&
{currentStory.title}
}
- {currentStory.currentTab === "story" && ( + {currentTab === "menu" && ( + + )} + {currentTab === "story" && currentStory && ( { placeholder="Start writing your story..." /> )} - {currentStory.currentTab === "lore" && ( + {currentTab === "lore" && currentStory && ( )} - {currentStory.currentTab === "characters" && ( + {currentTab === "characters" && currentStory && ( )} - {currentStory.currentTab === "locations" && ( + {currentTab === "locations" && currentStory && ( )} - {currentStory.currentTab === "chapters" && ( + {currentTab === "chapters" && currentStory && ( )} - {currentStory.currentTab === "scratchpad" && ( + {currentTab === "scratchpad" && currentStory && ( { placeholder="Notes, ideas, outlines — anything you don't want in the story..." /> )} - {currentStory.currentTab === "prompt" && ( + {currentTab === "prompt" && currentStory && (
)}
- {TABS.map((tab) => ( + {TABS.filter(tab => currentStory || tab.id === 'menu').map((tab) => ( +
+ {stories.map(story => ( + handleSelect(story.id)} + onRename={(newTitle) => handleRename(story.id, newTitle)} + onDelete={() => handleDelete(story.id)} + onDuplicate={() => handleDuplicate(story.id)} + /> + ))} +
+
+ + -
- {stories.map(story => ( - handleSelect(story.id)} - onRename={(newTitle) => handleRename(story.id, newTitle)} - onDelete={() => handleDelete(story.id)} - onDuplicate={() => handleDuplicate(story.id)} - /> - ))} -
-
- - -
{isSettingsOpen.value && ( @@ -148,6 +145,6 @@ export const MenuSidebar = () => { {isConnectionSettingsOpen.value && ( )} - +
); }; diff --git a/src/games/storywriter/contexts/state.tsx b/src/games/storywriter/contexts/state.tsx index 5f682de..2988494 100644 --- a/src/games/storywriter/contexts/state.tsx +++ b/src/games/storywriter/contexts/state.tsx @@ -11,7 +11,7 @@ export type ChatMessage = LLM.ChatMessage & { id: string; } -export type Tab = "story" | "lore" | "characters" | "locations" | "chapters" | "scratchpad" | "prompt"; +export type Tab = "story" | "lore" | "characters" | "locations" | "chapters" | "scratchpad" | "prompt" | "menu"; export enum CharacterRole { Protagonist = 'protagonist', @@ -72,7 +72,6 @@ export interface Story { lore: LoreEntry[]; characters: Character[]; locations: Location[]; - currentTab: Tab; chatMessages: ChatMessage[]; chapters: Chapters.Chapter[]; lastEditedText?: string; @@ -83,6 +82,7 @@ export interface Story { interface IState { stories: Story[]; currentStoryId: string | null; + currentTab: Tab; connection: LLM.Connection | null; model: LLM.ModelInfo | null; enableThinking: boolean; @@ -102,7 +102,7 @@ type Action = | { type: 'DELETE_LORE_ENTRY'; storyId: string; entryId: string } | { type: 'REORDER_LORE_ENTRIES'; storyId: string; entryIds: string[] } | { type: 'SET_SYSTEM_INSTRUCTION'; systemInstruction: string } - | { type: 'SET_CURRENT_TAB'; id: string; tab: Tab } + | { type: 'SET_CURRENT_TAB'; tab: Tab } | { type: 'DELETE_STORY'; id: string } | { type: 'SELECT_STORY'; id: string } | { type: 'DUPLICATE_STORY'; id: string } @@ -129,6 +129,7 @@ type Action = const DEFAULT_STATE: IState = { stories: [], currentStoryId: null, + currentTab: 'menu', connection: null, model: null, enableThinking: false, @@ -170,7 +171,6 @@ function reducer(state: IState, action: Action): IState { lore: [], characters: [], locations: [], - currentTab: 'story', chatMessages: [], chapters: [], }; @@ -261,12 +261,7 @@ function reducer(state: IState, action: Action): IState { }; } case 'SET_CURRENT_TAB': { - return { - ...state, - stories: state.stories.map(s => - s.id === action.id ? { ...s, currentTab: action.tab } : s - ), - }; + return { ...state, currentTab: action.tab }; } case 'DELETE_STORY': { const remaining = state.stories.filter(s => s.id !== action.id); @@ -288,7 +283,6 @@ function reducer(state: IState, action: Action): IState { lore: [...original.lore], characters: original.characters, locations: original.locations, - currentTab: 'story', chatMessages: [], chapters: [], }; @@ -299,10 +293,7 @@ function reducer(state: IState, action: Action): IState { }; } case 'SELECT_STORY': { - return { - ...state, - currentStoryId: action.id, - }; + return { ...state, currentStoryId: action.id, currentTab: 'story' }; } case 'ADD_CHAT_MESSAGE': { return { @@ -547,6 +538,7 @@ function reducer(state: IState, action: Action): IState { export interface AppState { stories: Story[]; currentStory: Story | null; + currentTab: Tab; connection: LLM.Connection | null; model: LLM.ModelInfo | null; enableThinking: boolean; @@ -567,6 +559,7 @@ export const StateContextProvider = ({ children }: { children?: any }) => { const value = useMemo(() => ({ stories: state.stories, currentStory: state.stories.find(s => s.id === state.currentStoryId) ?? null, + currentTab: state.currentTab, connection: state.connection, model: state.model, enableThinking: state.enableThinking,