From ae0d23233166de39f21481bd4f2f1439c1f0526e Mon Sep 17 00:00:00 2001 From: Pabloader Date: Wed, 8 Apr 2026 09:47:59 +0000 Subject: [PATCH] Show usernames in chat --- .../storywriter/components/chat-sidebar.tsx | 45 +++++++++++++++++-- src/games/storywriter/components/menu.tsx | 2 +- src/games/storywriter/contexts/state.tsx | 19 ++++---- 3 files changed, 51 insertions(+), 15 deletions(-) diff --git a/src/games/storywriter/components/chat-sidebar.tsx b/src/games/storywriter/components/chat-sidebar.tsx index 2fc848c..3066d35 100644 --- a/src/games/storywriter/components/chat-sidebar.tsx +++ b/src/games/storywriter/components/chat-sidebar.tsx @@ -2,7 +2,7 @@ import { ContentEditable } from "@common/components/ContentEditable"; import { highlight } from "@common/highlight"; import { useInputState } from "@common/hooks/useInputState"; import clsx from "clsx"; -import { Check, ChevronsRight, Edit2, RefreshCw, Sparkles, Trash2, X } from "lucide-preact"; +import { Check, ChevronsRight, Edit2, GitFork, RefreshCw, Sparkles, Trash2, X } from "lucide-preact"; import { useCallback, useEffect, useMemo, useRef, useState } from "preact/hooks"; import styles from '../assets/chat-sidebar.module.css'; import sidebarStyles from '../assets/sidebar.module.css'; @@ -20,6 +20,8 @@ interface RoleHeaderProps { } const RoleHeader = ({ message, chatMessages }: RoleHeaderProps) => { + const { currentWorld, userName } = useAppState(); + const toolName = useMemo(() => { if (message.role !== 'tool') return; for (const m of chatMessages.toReversed()) { @@ -29,12 +31,28 @@ const RoleHeader = ({ message, chatMessages }: RoleHeaderProps) => { } }, [message, chatMessages]); + let displayName: string = message.role; + let roleLabel = null; + + if (message.role === 'tool' && toolName) { + displayName = toolName; + roleLabel = message.role; + } else if (currentWorld?.chatOnly) { + if (message.role === 'user' && userName) { + displayName = userName; + roleLabel = message.role; + } else if (message.role === 'assistant' && currentWorld.title) { + displayName = currentWorld.title; + roleLabel = message.role; + } + } + return (
- {message.role} - {toolName && ( + {displayName} + {roleLabel && ( - {toolName} + {message.role} )}
@@ -362,6 +380,16 @@ export const ChatPanel = () => { }); }, [currentStory, currentWorld, dispatch]); + const handleForkChat = useCallback((messageId: string) => { + if (!currentStory || !currentWorld) return; + dispatch({ + type: 'DUPLICATE_STORY', + worldId: currentWorld.id, + id: currentStory.id, + upToMessageId: messageId, + }); + }, [currentStory, currentWorld, dispatch]); + const handleStartEdit = useCallback((message: ChatMessage) => { setEditingMessageId(message.id); setEditingContent(message.content); @@ -416,6 +444,15 @@ export const ChatPanel = () => { {!isLoading && canEdit && (
+ {currentWorld?.chatOnly && ( + + )}
) : ( -
+
diff --git a/src/games/storywriter/contexts/state.tsx b/src/games/storywriter/contexts/state.tsx index 4c9c75e..7582651 100644 --- a/src/games/storywriter/contexts/state.tsx +++ b/src/games/storywriter/contexts/state.tsx @@ -137,7 +137,7 @@ type Action = | { type: 'EDIT_SCRATCHPAD'; worldId: string; id: string; text: string } | { type: 'DELETE_STORY'; worldId: string; id: string } | { type: 'SELECT_STORY'; worldId: string; id: string } - | { type: 'DUPLICATE_STORY'; worldId: string; id: string } + | { type: 'DUPLICATE_STORY'; worldId: string; id: string; upToMessageId?: string } // Story lore | { type: 'ADD_LORE_ENTRY'; worldId: string; storyId: string | null; entry: LoreEntry } | { type: 'EDIT_LORE_ENTRY'; worldId: string; storyId: string | null; entryId: string; updates: Partial } @@ -278,7 +278,6 @@ function reducer(state: IState, action: Action): IState { worlds: [...state.worlds, world], currentWorldId: world.id, currentStoryId: null, - currentTab: 'menu', }; } case 'RENAME_WORLD': { @@ -299,7 +298,6 @@ function reducer(state: IState, action: Action): IState { ...state, currentWorldId: action.worldId, currentStoryId: null, - currentTab: 'menu', }; } case 'CREATE_STORY': { @@ -320,7 +318,6 @@ function reducer(state: IState, action: Action): IState { ...updateWorld(state, action.worldId, w => ({ ...w, stories: [...w.stories, story] })), currentWorldId: action.worldId, currentStoryId: story.id, - currentTab: 'menu', }; } case 'RENAME_STORY': { @@ -347,20 +344,24 @@ function reducer(state: IState, action: Action): IState { }; } case 'SELECT_STORY': { - const world = state.worlds.find(w => w.id === action.worldId); return { ...state, currentWorldId: action.worldId, currentStoryId: action.id, - currentTab: 'menu', }; } case 'DUPLICATE_STORY': { const world = state.worlds.find(w => w.id === action.worldId); const original = world?.stories.find(s => s.id === action.id); if (!original) return state; - const firstMessage = original.chatMessages[0]; - const chatMessages = world?.chatOnly && firstMessage && firstMessage.role === 'assistant' ? [firstMessage] : []; + let chatMessages: ChatMessage[]; + if (action.upToMessageId) { + const idx = original.chatMessages.findIndex(m => m.id === action.upToMessageId); + chatMessages = idx !== -1 ? original.chatMessages.slice(0, idx + 1) : [...original.chatMessages]; + } else { + const firstMessage = original.chatMessages[0]; + chatMessages = world?.chatOnly && firstMessage && firstMessage.role === 'assistant' ? [firstMessage] : []; + } const newStory: Story = { id: crypto.randomUUID(), title: `${original.title} (Copy)`, @@ -375,7 +376,6 @@ function reducer(state: IState, action: Action): IState { return { ...updateWorld(state, action.worldId, w => ({ ...w, stories: [...w.stories, newStory] })), currentStoryId: newStory.id, - currentTab: 'menu', }; } case 'ADD_LORE_ENTRY': { @@ -582,7 +582,6 @@ function reducer(state: IState, action: Action): IState { worlds: [...state.worlds, world], currentWorldId: world.id, currentStoryId: null, - currentTab: 'menu', }; } }