email chat uses officerdev/chat app with session history and email context scoping
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,8 @@ import { useGlobal } from 'hooks/useGlobal';
|
||||
import { defaultLayout } from './defaultLayout';
|
||||
import { EmailList } from './EmailList';
|
||||
import { EmailReader } from './EmailReader';
|
||||
import { EmailChat } from './EmailChat';
|
||||
|
||||
const PROMPT_PREFIX = `You are an email assistant. The user has a local SQLite email database available via the "email-db" tool — use it for all email queries (search, count, stats, aggregations, deletions) unless the user explicitly asks you to use Gmail. Do not use the Gmail integration for questions about existing emails.`;
|
||||
|
||||
export const EmailScreen = () => {
|
||||
const isMobile = useIsMobile();
|
||||
@@ -16,7 +17,6 @@ export const EmailScreen = () => {
|
||||
() => ({
|
||||
'email-list': EmailList,
|
||||
'email-reader': EmailReader,
|
||||
'email-chat': EmailChat,
|
||||
}),
|
||||
[],
|
||||
);
|
||||
@@ -30,6 +30,8 @@ export const EmailScreen = () => {
|
||||
layout={defaultLayout}
|
||||
onLayoutChange={() => {}}
|
||||
components={components}
|
||||
workspaceId="email"
|
||||
promptPrefix={PROMPT_PREFIX}
|
||||
isMobile={isMobile}
|
||||
mobilePanelId={mobilePanelId}
|
||||
onMobileBack={mobilePanelId ? onMobileBack : null}
|
||||
|
||||
@@ -13,7 +13,7 @@ export const defaultLayout: LayoutNode = {
|
||||
direction: 'vertical',
|
||||
children: [
|
||||
{ node: { type: 'panel', id: 'email-reader', appType: null }, size: 60 },
|
||||
{ node: { type: 'panel', id: 'email-chat', appType: null }, size: 40 },
|
||||
{ node: { type: 'panel', id: 'email-chat', appType: 'officerdev/chat' }, size: 40 },
|
||||
],
|
||||
},
|
||||
size: 75,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from 'react';
|
||||
import { MessageSquare, History, Plus } from 'lucide-react';
|
||||
import { Popover, PopoverTrigger, PopoverContent } from '@/components/ui/popover';
|
||||
import { useWorkspace } from '../../components/Workspace/WorkspaceContext';
|
||||
import { useChatSessions } from './useChatSessions';
|
||||
import { useChatSessions } from 'state/useChatSessions';
|
||||
import { usePanelChannel } from 'hooks/usePanelChannel';
|
||||
import { getProviderDisplayName } from 'state/useModels';
|
||||
|
||||
@@ -21,9 +21,15 @@ function formatModel(model: string): string {
|
||||
}
|
||||
|
||||
export const ChatHeader = () => {
|
||||
const { cwd, root } = useWorkspace();
|
||||
const scoped = cwd !== '~';
|
||||
const { sessions } = useChatSessions(scoped ? { cwd, cwdRoot: root } : {});
|
||||
const { workspaceId } = useWorkspace();
|
||||
const contextFilter = workspaceId === 'email'
|
||||
? { context: 'email' as const }
|
||||
: workspaceId?.startsWith('proj-layout-')
|
||||
? { context: 'project' as const, contextId: workspaceId.replace('proj-layout-', '') }
|
||||
: workspaceId && !workspaceId.startsWith('screens/')
|
||||
? { context: 'workspace' as const, contextId: workspaceId }
|
||||
: undefined;
|
||||
const { sessions } = useChatSessions(contextFilter);
|
||||
const [selection, setSelection] = usePanelChannel<ChatSessionSelection | null>('chat:panel-session', null);
|
||||
const [activeSessionId] = usePanelChannel<string | null>('chat:active-session', null);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
@@ -10,16 +10,18 @@ type ChatSessionSelection = {
|
||||
};
|
||||
|
||||
export const ChatPanelWrapper = () => {
|
||||
const { workspaceId, cwd, root } = useWorkspace();
|
||||
const { workspaceId, cwd, root, promptPrefix } = useWorkspace();
|
||||
const scoped = cwd !== '~';
|
||||
const hostRoot = root === '~' || root === 'officer.dev';
|
||||
const sandboxed = !hostRoot;
|
||||
|
||||
const chatContext = workspaceId?.startsWith('proj-layout-')
|
||||
? { context: 'project' as const, contextId: workspaceId.replace('proj-layout-', '') }
|
||||
: workspaceId && !workspaceId.startsWith('screens/')
|
||||
? { context: 'workspace' as const, contextId: workspaceId }
|
||||
: {};
|
||||
const chatContext = workspaceId === 'email'
|
||||
? { context: 'email' as const }
|
||||
: workspaceId?.startsWith('proj-layout-')
|
||||
? { context: 'project' as const, contextId: workspaceId.replace('proj-layout-', '') }
|
||||
: workspaceId && !workspaceId.startsWith('screens/')
|
||||
? { context: 'workspace' as const, contextId: workspaceId }
|
||||
: {};
|
||||
|
||||
const [selection] = usePanelChannel<ChatSessionSelection | null>('chat:panel-session', null);
|
||||
const [, setActiveSession] = usePanelChannel<string | null>('chat:active-session', null);
|
||||
@@ -45,6 +47,7 @@ export const ChatPanelWrapper = () => {
|
||||
cwd={cwdParam}
|
||||
sandboxed={sandboxed}
|
||||
replaceUrl={false}
|
||||
promptPrefix={promptPrefix}
|
||||
{...chatContext}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -13,6 +13,7 @@ type WorkspaceContextValue = {
|
||||
root?: string;
|
||||
initialFilePath?: string;
|
||||
defaultFileSort?: DefaultFileSort;
|
||||
promptPrefix?: string;
|
||||
swapSourceId: string | null;
|
||||
setSwapSourceId: (id: string | null) => void;
|
||||
onSwap: (sourceId: string, targetId: string) => void;
|
||||
|
||||
@@ -12,6 +12,7 @@ type WorkspaceLayoutProps = {
|
||||
components?: PanelComponents;
|
||||
workspaceId?: string;
|
||||
cwd?: string;
|
||||
promptPrefix?: string;
|
||||
noHeader?: boolean;
|
||||
isMobile?: boolean;
|
||||
mobilePanelId?: string;
|
||||
@@ -20,7 +21,7 @@ type WorkspaceLayoutProps = {
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
export const WorkspaceLayout = ({ layout, onLayoutChange, registry: registryProp, components, workspaceId, cwd, noHeader, isMobile, mobilePanelId, onMobileBack }: WorkspaceLayoutProps) => {
|
||||
export const WorkspaceLayout = ({ layout, onLayoutChange, registry: registryProp, components, workspaceId, cwd, promptPrefix, noHeader, isMobile, mobilePanelId, onMobileBack }: WorkspaceLayoutProps) => {
|
||||
const { registry: globalRegistry } = useAppRegistry();
|
||||
const registry = registryProp ?? globalRegistry;
|
||||
|
||||
@@ -32,7 +33,7 @@ export const WorkspaceLayout = ({ layout, onLayoutChange, registry: registryProp
|
||||
);
|
||||
|
||||
return (
|
||||
<WorkspaceProvider value={{ workspaceId: workspaceId ?? null, cwd: cwd ?? '~', swapSourceId: null, setSwapSourceId: noop, onSwap: noop, dragSourceId: null, setDragSourceId: noop, onMove: noop, maximizedPanelId: null, setMaximizedPanelId: noop, transitioningPanelId: null, isMobile: isMobile ?? false, onMobileBack: onMobileBack ?? null }}>
|
||||
<WorkspaceProvider value={{ workspaceId: workspaceId ?? null, cwd: cwd ?? '~', promptPrefix, swapSourceId: null, setSwapSourceId: noop, onSwap: noop, dragSourceId: null, setDragSourceId: noop, onMove: noop, maximizedPanelId: null, setMaximizedPanelId: noop, transitioningPanelId: null, isMobile: isMobile ?? false, onMobileBack: onMobileBack ?? null }}>
|
||||
<WorkspaceRenderer
|
||||
layout={layout}
|
||||
registry={registry}
|
||||
|
||||
Reference in New Issue
Block a user