diff --git a/src/apps/chat/AppChat.tsx b/src/apps/chat/AppChat.tsx
index 12f5cb998..ce4b082a7 100644
--- a/src/apps/chat/AppChat.tsx
+++ b/src/apps/chat/AppChat.tsx
@@ -665,6 +665,7 @@ export function AppChat() {
{isMultiPane && !isZenMode && (
diff --git a/src/apps/chat/components/PaneTitleOverlay.tsx b/src/apps/chat/components/PaneTitleOverlay.tsx
index aeb5e9ab3..6068f0437 100644
--- a/src/apps/chat/components/PaneTitleOverlay.tsx
+++ b/src/apps/chat/components/PaneTitleOverlay.tsx
@@ -1,50 +1,148 @@
import * as React from 'react';
-import { Sheet } from '@mui/joy';
+
+import { Box, IconButton, Sheet } from '@mui/joy';
+import ClearIcon from '@mui/icons-material/Clear';
+import EditRoundedIcon from '@mui/icons-material/EditRounded';
+import OpenInFullIcon from '@mui/icons-material/OpenInFull';
import type { DConversationId } from '~/common/stores/chat/chat.conversation';
+import { InlineTextarea } from '~/common/components/InlineTextarea';
import { useConversationTitle } from '~/common/stores/chat/hooks/useConversationTitle';
+import { chatPanesActions } from './panes/store-panes-manager';
-const _style = {
- position: 'absolute',
- top: 0,
- left: '50%',
- transform: 'translateX(-50%)',
- zIndex: 10,
- p: '1px 1rem 4px',
- fontSize: 'sm',
- fontWeight: 'md',
- borderBottomLeftRadius: '8px',
- borderBottomRightRadius: '8px',
- // boxShadow: 'xs',
- // border: '1px solid',
- // borderColor: 'background.popup',
- borderTop: 'none',
- maxWidth: '78%',
- overflow: 'hidden',
- textOverflow: 'ellipsis',
- whiteSpace: 'nowrap',
+const _styles = {
+ tileBar: {
+ position: 'absolute',
+ top: 0,
+ left: '50%',
+ transform: 'translateX(-50%)',
+ zIndex: 10,
+ padding: '0 0.125rem 0.125rem',
+ fontSize: 'sm',
+ fontWeight: 'md',
+ borderBottomLeftRadius: '8px',
+ borderBottomRightRadius: '8px',
+ // boxShadow: 'xs',
+ // border: '1px solid',
+ // borderColor: 'background.popup',
+ borderTop: 'none',
+ maxWidth: '78%',
+ display: 'flex',
+ alignItems: 'center',
+ gap: 1,
+ } as const,
+ title: {
+ flex: 1,
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ whiteSpace: 'nowrap',
+ } as const,
+ toolButton: {
+ '--IconButton-size': '1.5rem',
+ backgroundColor: 'transparent',
+ } as const,
+ toolIconLg: {
+ fontSize: 'lg',
+ } as const,
} as const;
-export function PaneTitleOverlay(props: { conversationId: DConversationId | null, isFocused: boolean }) {
+export function PaneTitleOverlay(props: {
+ paneIdx: number,
+ conversationId: DConversationId | null,
+ isFocused: boolean,
+}) {
+
+ // state
+ const [editingTitle, setEditingTitle] = React.useState(false);
// external state
- const title = useConversationTitle(props.conversationId);
- if (!title || title?.length < 3)
- return null;
+ const { title, setUserTitle } = useConversationTitle(props.conversationId);
+ // if (!title || title?.length < 3)
+ // return null;
+
+
+ // close tabs handlers
+
+ const handleCloseThis = React.useCallback(() => {
+ chatPanesActions().removePane(props.paneIdx);
+ }, [props.paneIdx]);
+
+ const handleCloseOthers = React.useCallback(() => {
+ chatPanesActions().removeOtherPanes(props.paneIdx);
+ }, [props.paneIdx]);
+
+
+ // title handles
+
+ const handleTitleEditBegin = React.useCallback(() => {
+ setEditingTitle(true);
+ }, []);
+
+ const handleTitleEditChange = React.useCallback((newTitle: string) => {
+ setUserTitle(newTitle);
+ setEditingTitle(false);
+ }, [setUserTitle]);
+
+ const handleTitleEditEnd = React.useCallback(() => {
+ setEditingTitle(false);
+ }, []);
+
// don't render if not focused
// if (!props.isFocused)
// return null;
+ const hasTitle = title && title.length > 0;
+ const color = props.isFocused ? 'primary' : 'neutral';
+ const variantO = props.isFocused ? 'solid' : 'outlined';
+ const variantP = props.isFocused ? 'solid' : 'plain';
+
return (
- {title}
+ {/* Close Others*/}
+ {/**/}
+ {!editingTitle &&
+
+ }
+ {/**/}
+
+ {/* Title */}
+ {editingTitle ? (
+
+ ) : hasTitle ? (
+
+ {title}
+
+ ) : !!props.conversationId && (
+
+
+
+ )}
+
+ {/* Close This */}
+ {/**/}
+ {!editingTitle &&
+
+ }
+ {/**/}
);
}
\ No newline at end of file
diff --git a/src/common/stores/chat/hooks/useConversationTitle.ts b/src/common/stores/chat/hooks/useConversationTitle.ts
index 5a13a78d2..e320044d9 100644
--- a/src/common/stores/chat/hooks/useConversationTitle.ts
+++ b/src/common/stores/chat/hooks/useConversationTitle.ts
@@ -1,12 +1,25 @@
+import * as React from 'react';
import { useShallow } from 'zustand/react/shallow';
import { conversationTitle, DConversationId } from '../chat.conversation';
import { useChatStore } from '../store-chats';
-export function useConversationTitle(conversationId: DConversationId | null) {
- return useChatStore(useShallow(({ conversations }) => {
+export function useConversationTitle(conversationId: DConversationId | null, fallbackTitle?: string) {
+
+ // react to the title
+ const { title, setUserTitle: storeSetUserTitle } = useChatStore(useShallow(({ conversations, setUserTitle }) => {
const conversation = conversationId ? conversations.find(_c => _c.id === conversationId) : null;
- return conversation ? conversationTitle(conversation) : null;
+ return {
+ title: conversation ? conversationTitle(conversation, fallbackTitle) : null,
+ setUserTitle,
+ };
}));
+
+ // closure to set the title
+ const setUserTitle = React.useCallback((newTitle: string) => {
+ conversationId && storeSetUserTitle(conversationId, newTitle);
+ }, [conversationId, storeSetUserTitle]);
+
+ return { title, setUserTitle };
}