Show usernames in chat
This commit is contained in:
parent
ed83c9a0b8
commit
ae0d232331
|
|
@ -2,7 +2,7 @@ import { ContentEditable } from "@common/components/ContentEditable";
|
||||||
import { highlight } from "@common/highlight";
|
import { highlight } from "@common/highlight";
|
||||||
import { useInputState } from "@common/hooks/useInputState";
|
import { useInputState } from "@common/hooks/useInputState";
|
||||||
import clsx from "clsx";
|
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 { useCallback, useEffect, useMemo, useRef, useState } from "preact/hooks";
|
||||||
import styles from '../assets/chat-sidebar.module.css';
|
import styles from '../assets/chat-sidebar.module.css';
|
||||||
import sidebarStyles from '../assets/sidebar.module.css';
|
import sidebarStyles from '../assets/sidebar.module.css';
|
||||||
|
|
@ -20,6 +20,8 @@ interface RoleHeaderProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const RoleHeader = ({ message, chatMessages }: RoleHeaderProps) => {
|
const RoleHeader = ({ message, chatMessages }: RoleHeaderProps) => {
|
||||||
|
const { currentWorld, userName } = useAppState();
|
||||||
|
|
||||||
const toolName = useMemo(() => {
|
const toolName = useMemo(() => {
|
||||||
if (message.role !== 'tool') return;
|
if (message.role !== 'tool') return;
|
||||||
for (const m of chatMessages.toReversed()) {
|
for (const m of chatMessages.toReversed()) {
|
||||||
|
|
@ -29,12 +31,28 @@ const RoleHeader = ({ message, chatMessages }: RoleHeaderProps) => {
|
||||||
}
|
}
|
||||||
}, [message, chatMessages]);
|
}, [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 (
|
return (
|
||||||
<div class={styles.role}>
|
<div class={styles.role}>
|
||||||
{message.role}
|
{displayName}
|
||||||
{toolName && (
|
{roleLabel && (
|
||||||
<span class={styles.toolBadge}>
|
<span class={styles.toolBadge}>
|
||||||
{toolName}
|
{message.role}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -362,6 +380,16 @@ export const ChatPanel = () => {
|
||||||
});
|
});
|
||||||
}, [currentStory, currentWorld, dispatch]);
|
}, [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) => {
|
const handleStartEdit = useCallback((message: ChatMessage) => {
|
||||||
setEditingMessageId(message.id);
|
setEditingMessageId(message.id);
|
||||||
setEditingContent(message.content);
|
setEditingContent(message.content);
|
||||||
|
|
@ -416,6 +444,15 @@ export const ChatPanel = () => {
|
||||||
|
|
||||||
{!isLoading && canEdit && (
|
{!isLoading && canEdit && (
|
||||||
<div class={styles.messageActions}>
|
<div class={styles.messageActions}>
|
||||||
|
{currentWorld?.chatOnly && (
|
||||||
|
<button
|
||||||
|
class={styles.iconButton}
|
||||||
|
onClick={() => handleForkChat(message.id)}
|
||||||
|
title="Fork chat up to this message"
|
||||||
|
>
|
||||||
|
<GitFork size={12} />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
<button
|
<button
|
||||||
class={styles.iconButton}
|
class={styles.iconButton}
|
||||||
onClick={() => handleStartEdit(message)}
|
onClick={() => handleStartEdit(message)}
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ const WorldItem = ({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div class={clsx(styles.itemWrapper, isWorldActive && styles.active)}>
|
<div class={clsx(styles.itemWrapper, isWorldActive && styles.active)} onClick={isExpanded.setTrue}>
|
||||||
<button class={styles.expandButton} onClick={toggleExpand} title={isExpanded.value ? 'Collapse' : 'Expand'}>
|
<button class={styles.expandButton} onClick={toggleExpand} title={isExpanded.value ? 'Collapse' : 'Expand'}>
|
||||||
{isExpanded.value ? <ChevronDown size={12} /> : <ChevronRight size={12} />}
|
{isExpanded.value ? <ChevronDown size={12} /> : <ChevronRight size={12} />}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,7 @@ type Action =
|
||||||
| { type: 'EDIT_SCRATCHPAD'; worldId: string; id: string; text: string }
|
| { type: 'EDIT_SCRATCHPAD'; worldId: string; id: string; text: string }
|
||||||
| { type: 'DELETE_STORY'; worldId: string; id: string }
|
| { type: 'DELETE_STORY'; worldId: string; id: string }
|
||||||
| { type: 'SELECT_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
|
// Story lore
|
||||||
| { type: 'ADD_LORE_ENTRY'; worldId: string; storyId: string | null; entry: LoreEntry }
|
| { type: 'ADD_LORE_ENTRY'; worldId: string; storyId: string | null; entry: LoreEntry }
|
||||||
| { type: 'EDIT_LORE_ENTRY'; worldId: string; storyId: string | null; entryId: string; updates: Partial<LoreEntry> }
|
| { type: 'EDIT_LORE_ENTRY'; worldId: string; storyId: string | null; entryId: string; updates: Partial<LoreEntry> }
|
||||||
|
|
@ -278,7 +278,6 @@ function reducer(state: IState, action: Action): IState {
|
||||||
worlds: [...state.worlds, world],
|
worlds: [...state.worlds, world],
|
||||||
currentWorldId: world.id,
|
currentWorldId: world.id,
|
||||||
currentStoryId: null,
|
currentStoryId: null,
|
||||||
currentTab: 'menu',
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case 'RENAME_WORLD': {
|
case 'RENAME_WORLD': {
|
||||||
|
|
@ -299,7 +298,6 @@ function reducer(state: IState, action: Action): IState {
|
||||||
...state,
|
...state,
|
||||||
currentWorldId: action.worldId,
|
currentWorldId: action.worldId,
|
||||||
currentStoryId: null,
|
currentStoryId: null,
|
||||||
currentTab: 'menu',
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case 'CREATE_STORY': {
|
case 'CREATE_STORY': {
|
||||||
|
|
@ -320,7 +318,6 @@ function reducer(state: IState, action: Action): IState {
|
||||||
...updateWorld(state, action.worldId, w => ({ ...w, stories: [...w.stories, story] })),
|
...updateWorld(state, action.worldId, w => ({ ...w, stories: [...w.stories, story] })),
|
||||||
currentWorldId: action.worldId,
|
currentWorldId: action.worldId,
|
||||||
currentStoryId: story.id,
|
currentStoryId: story.id,
|
||||||
currentTab: 'menu',
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case 'RENAME_STORY': {
|
case 'RENAME_STORY': {
|
||||||
|
|
@ -347,20 +344,24 @@ function reducer(state: IState, action: Action): IState {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case 'SELECT_STORY': {
|
case 'SELECT_STORY': {
|
||||||
const world = state.worlds.find(w => w.id === action.worldId);
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
currentWorldId: action.worldId,
|
currentWorldId: action.worldId,
|
||||||
currentStoryId: action.id,
|
currentStoryId: action.id,
|
||||||
currentTab: 'menu',
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case 'DUPLICATE_STORY': {
|
case 'DUPLICATE_STORY': {
|
||||||
const world = state.worlds.find(w => w.id === action.worldId);
|
const world = state.worlds.find(w => w.id === action.worldId);
|
||||||
const original = world?.stories.find(s => s.id === action.id);
|
const original = world?.stories.find(s => s.id === action.id);
|
||||||
if (!original) return state;
|
if (!original) return state;
|
||||||
const firstMessage = original.chatMessages[0];
|
let chatMessages: ChatMessage[];
|
||||||
const chatMessages = world?.chatOnly && firstMessage && firstMessage.role === 'assistant' ? [firstMessage] : [];
|
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 = {
|
const newStory: Story = {
|
||||||
id: crypto.randomUUID(),
|
id: crypto.randomUUID(),
|
||||||
title: `${original.title} (Copy)`,
|
title: `${original.title} (Copy)`,
|
||||||
|
|
@ -375,7 +376,6 @@ function reducer(state: IState, action: Action): IState {
|
||||||
return {
|
return {
|
||||||
...updateWorld(state, action.worldId, w => ({ ...w, stories: [...w.stories, newStory] })),
|
...updateWorld(state, action.worldId, w => ({ ...w, stories: [...w.stories, newStory] })),
|
||||||
currentStoryId: newStory.id,
|
currentStoryId: newStory.id,
|
||||||
currentTab: 'menu',
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case 'ADD_LORE_ENTRY': {
|
case 'ADD_LORE_ENTRY': {
|
||||||
|
|
@ -582,7 +582,6 @@ function reducer(state: IState, action: Action): IState {
|
||||||
worlds: [...state.worlds, world],
|
worlds: [...state.worlds, world],
|
||||||
currentWorldId: world.id,
|
currentWorldId: world.id,
|
||||||
currentStoryId: null,
|
currentStoryId: null,
|
||||||
currentTab: 'menu',
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue