pi-mono
This commit is contained in:
@@ -3,14 +3,15 @@ import { useLocation } from 'react-router';
|
||||
import { Trash2, Archive } from 'lucide-react';
|
||||
import { usePanelChannel } from 'hooks/usePanelChannel';
|
||||
import { useChatSessions } from '@/state/useChatSessions';
|
||||
import { useVisibleClaudeModels, useVisibleOpenCodeModels } from '@/state/useModels';
|
||||
import { useVisibleClaudeModels, useVisibleOpenCodeModels, useVisiblePiMonoModels } from '@/state/useModels';
|
||||
import { useClaude } from '@/Screens/Dashboard/Chat/useClaude';
|
||||
import { useOpenCode } from '@/Screens/Dashboard/Chat/useOpenCode';
|
||||
import { usePiMono } from '@/Screens/Dashboard/Chat/usePiMono';
|
||||
import { EmbeddableChat } from '@/Screens/Dashboard/Chat/EmbeddableChat';
|
||||
|
||||
export type SelectedSession = {
|
||||
id: string;
|
||||
provider: 'claude' | 'opencode';
|
||||
provider: 'claude' | 'opencode' | 'pi-mono';
|
||||
model?: string | null;
|
||||
} | null;
|
||||
|
||||
@@ -26,7 +27,7 @@ type ChatLocationState = {
|
||||
} | null;
|
||||
|
||||
type DetailBarProps = {
|
||||
provider: 'claude' | 'opencode';
|
||||
provider: 'claude' | 'opencode' | 'pi-mono';
|
||||
sessionTitle: string | undefined;
|
||||
isConnected: boolean;
|
||||
isGenerating: boolean;
|
||||
@@ -138,7 +139,33 @@ const OpenCodeInner = ({ sessionId, model }: InnerProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const NewClaudeInner = ({ onProviderChange }: { onProviderChange: (p: 'claude' | 'opencode') => void }) => {
|
||||
const PiMonoInner = ({ sessionId, model }: InnerProps) => {
|
||||
const chat = usePiMono(sessionId, model, { replaceUrl: false });
|
||||
const models = useVisiblePiMonoModels();
|
||||
const { sessions, deleteSession } = useChatSessions();
|
||||
const [, setSelected] = usePanelChannel<SelectedSession>(CHANNEL, null);
|
||||
const sessionTitle = sessions.find((s) => s.id === sessionId)?.title;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
<DetailBar
|
||||
provider="pi-mono"
|
||||
sessionTitle={sessionTitle}
|
||||
isConnected={chat.isConnected}
|
||||
isGenerating={chat.isGenerating}
|
||||
onArchive={undefined}
|
||||
onDelete={async () => {
|
||||
await deleteSession('pi-mono', sessionId);
|
||||
setSelected(null);
|
||||
window.history.replaceState(null, '', '/chat');
|
||||
}}
|
||||
/>
|
||||
<EmbeddableChat chat={chat} provider="pi-mono" availableModels={models} className="flex-1 min-h-0" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const NewClaudeInner = ({ onProviderChange }: { onProviderChange: (p: 'claude' | 'opencode' | 'pi-mono') => void }) => {
|
||||
const location = useLocation();
|
||||
const locationState = location.state as ChatLocationState;
|
||||
const initialSentRef = useRef(false);
|
||||
@@ -189,7 +216,7 @@ const NewClaudeInner = ({ onProviderChange }: { onProviderChange: (p: 'claude' |
|
||||
);
|
||||
};
|
||||
|
||||
const NewOpenCodeInner = ({ onProviderChange }: { onProviderChange: (p: 'claude' | 'opencode') => void }) => {
|
||||
const NewOpenCodeInner = ({ onProviderChange }: { onProviderChange: (p: 'claude' | 'opencode' | 'pi-mono') => void }) => {
|
||||
const location = useLocation();
|
||||
const locationState = location.state as ChatLocationState;
|
||||
const initialSentRef = useRef(false);
|
||||
@@ -239,8 +266,58 @@ const NewOpenCodeInner = ({ onProviderChange }: { onProviderChange: (p: 'claude'
|
||||
);
|
||||
};
|
||||
|
||||
const NewPiMonoInner = ({ onProviderChange }: { onProviderChange: (p: 'claude' | 'opencode' | 'pi-mono') => void }) => {
|
||||
const location = useLocation();
|
||||
const locationState = location.state as ChatLocationState;
|
||||
const initialSentRef = useRef(false);
|
||||
const chat = usePiMono();
|
||||
const models = useVisiblePiMonoModels();
|
||||
const [, setSelected] = usePanelChannel<SelectedSession>(CHANNEL, null);
|
||||
|
||||
useEffect(() => {
|
||||
if (chat.sessionId) {
|
||||
setSelected({ id: chat.sessionId, provider: 'pi-mono', model: chat.model });
|
||||
}
|
||||
}, [chat.sessionId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!locationState || initialSentRef.current || !chat.isConnected) return;
|
||||
if (locationState.prefillInput) {
|
||||
initialSentRef.current = true;
|
||||
window.history.replaceState({}, '', location.pathname);
|
||||
return;
|
||||
}
|
||||
if (!locationState.initialMessage) return;
|
||||
initialSentRef.current = true;
|
||||
if (locationState.model) chat.setSelectedModel(locationState.model);
|
||||
chat.sendPrompt(locationState.initialMessage, locationState.attachmentIds, locationState.images);
|
||||
window.history.replaceState({}, '', location.pathname);
|
||||
}, [chat.isConnected, location.state]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
<DetailBar
|
||||
provider="pi-mono"
|
||||
sessionTitle={undefined}
|
||||
isConnected={chat.isConnected}
|
||||
isGenerating={chat.isGenerating}
|
||||
onArchive={undefined}
|
||||
onDelete={undefined}
|
||||
/>
|
||||
<EmbeddableChat
|
||||
chat={chat}
|
||||
provider="pi-mono"
|
||||
availableModels={models}
|
||||
onProviderChange={onProviderChange}
|
||||
defaultInput={locationState?.prefillInput ?? ''}
|
||||
className="flex-1 min-h-0"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
type NewChatPanelProps = {
|
||||
initialProvider?: 'claude' | 'opencode';
|
||||
initialProvider?: 'claude' | 'opencode' | 'pi-mono';
|
||||
};
|
||||
|
||||
const NewChatPanel = ({ initialProvider = 'claude' }: NewChatPanelProps) => {
|
||||
@@ -248,24 +325,28 @@ const NewChatPanel = ({ initialProvider = 'claude' }: NewChatPanelProps) => {
|
||||
|
||||
const provider = selected?.provider ?? initialProvider;
|
||||
|
||||
const handleProviderChange = (p: 'claude' | 'opencode') => {
|
||||
const handleProviderChange = (p: 'claude' | 'opencode' | 'pi-mono') => {
|
||||
setSelected({ id: 'new', provider: p });
|
||||
};
|
||||
|
||||
// Once a session is created, the inner component updates selected via the channel
|
||||
if (selected && selected.id !== 'new') {
|
||||
return selected.provider === 'claude' ? (
|
||||
<ClaudeInner key={selected.id} sessionId={selected.id} model={selected.model} />
|
||||
) : (
|
||||
<OpenCodeInner key={selected.id} sessionId={selected.id} model={selected.model} />
|
||||
);
|
||||
if (selected.provider === 'claude') {
|
||||
return <ClaudeInner key={selected.id} sessionId={selected.id} model={selected.model} />;
|
||||
}
|
||||
if (selected.provider === 'opencode') {
|
||||
return <OpenCodeInner key={selected.id} sessionId={selected.id} model={selected.model} />;
|
||||
}
|
||||
return <PiMonoInner key={selected.id} sessionId={selected.id} model={selected.model} />;
|
||||
}
|
||||
|
||||
return provider === 'claude' ? (
|
||||
<NewClaudeInner key="new-claude" onProviderChange={handleProviderChange} />
|
||||
) : (
|
||||
<NewOpenCodeInner key="new-opencode" onProviderChange={handleProviderChange} />
|
||||
);
|
||||
if (provider === 'claude') {
|
||||
return <NewClaudeInner key="new-claude" onProviderChange={handleProviderChange} />;
|
||||
}
|
||||
if (provider === 'opencode') {
|
||||
return <NewOpenCodeInner key="new-opencode" onProviderChange={handleProviderChange} />;
|
||||
}
|
||||
return <NewPiMonoInner key="new-pi-mono" onProviderChange={handleProviderChange} />;
|
||||
};
|
||||
|
||||
export const ChatDetailPanel = () => {
|
||||
@@ -283,9 +364,11 @@ export const ChatDetailPanel = () => {
|
||||
return <NewChatPanel key="new" initialProvider={selected.provider} />;
|
||||
}
|
||||
|
||||
return selected.provider === 'claude' ? (
|
||||
<ClaudeInner key={selected.id} sessionId={selected.id} model={selected.model} />
|
||||
) : (
|
||||
<OpenCodeInner key={selected.id} sessionId={selected.id} model={selected.model} />
|
||||
);
|
||||
if (selected.provider === 'claude') {
|
||||
return <ClaudeInner key={selected.id} sessionId={selected.id} model={selected.model} />;
|
||||
}
|
||||
if (selected.provider === 'opencode') {
|
||||
return <OpenCodeInner key={selected.id} sessionId={selected.id} model={selected.model} />;
|
||||
}
|
||||
return <PiMonoInner key={selected.id} sessionId={selected.id} model={selected.model} />;
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@ import { usePanelChannel } from 'hooks/usePanelChannel';
|
||||
import { useChatSessions } from '@/state/useChatSessions';
|
||||
import type { SelectedSession } from './ChatDetailPanel';
|
||||
|
||||
type Filter = 'all' | 'claude' | 'opencode';
|
||||
type Filter = 'all' | 'claude' | 'opencode' | 'pi-mono';
|
||||
|
||||
export const SessionList = () => {
|
||||
const [filter, setFilter] = useState<Filter>('all');
|
||||
@@ -29,11 +29,16 @@ export const SessionList = () => {
|
||||
|
||||
const handleSelect = (session: (typeof sessions)[number]) => {
|
||||
setSelected({ id: session.id, provider: session.provider, model: session.model ?? null });
|
||||
const path = session.provider === 'claude' ? `/chat/${session.id}` : `/chat/opencode/${session.id}`;
|
||||
const path =
|
||||
session.provider === 'claude'
|
||||
? `/chat/${session.id}`
|
||||
: session.provider === 'opencode'
|
||||
? `/chat/opencode/${session.id}`
|
||||
: `/chat/pi-mono/${session.id}`;
|
||||
window.history.replaceState(null, '', path);
|
||||
};
|
||||
|
||||
const handleDelete = async (provider: 'claude' | 'opencode', id: string) => {
|
||||
const handleDelete = async (provider: 'claude' | 'opencode' | 'pi-mono', id: string) => {
|
||||
if (selected?.id === id && selected?.provider === provider) {
|
||||
setSelected(null);
|
||||
window.history.replaceState(null, '', '/chat');
|
||||
@@ -49,7 +54,7 @@ export const SessionList = () => {
|
||||
<div className="flex items-center gap-2">
|
||||
{/* Radio filter */}
|
||||
<div className="flex items-center gap-0.5 rounded-lg bg-duck-dark/5 dark:bg-foreground/5 p-0.5">
|
||||
{(['all', 'claude', 'opencode'] as const).map((value) => (
|
||||
{(['all', 'claude', 'opencode', 'pi-mono'] as const).map((value) => (
|
||||
<button
|
||||
key={value}
|
||||
onClick={() => setFilter(value)}
|
||||
@@ -59,7 +64,7 @@ export const SessionList = () => {
|
||||
: 'text-duck-dark/50 dark:text-foreground/50 hover:text-duck-dark/80 dark:hover:text-foreground/80'
|
||||
}`}
|
||||
>
|
||||
{value === 'all' ? 'All' : value === 'claude' ? 'Claude' : 'OpenCode'}
|
||||
{value === 'all' ? 'All' : value === 'claude' ? 'Claude' : value === 'opencode' ? 'OpenCode' : 'Pi'}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
@@ -114,10 +119,14 @@ export const SessionList = () => {
|
||||
})}
|
||||
<span
|
||||
className={`ml-2 text-xs font-medium ${
|
||||
session.provider === 'claude' ? 'text-duck-teal' : 'text-duck-orange'
|
||||
session.provider === 'claude'
|
||||
? 'text-duck-teal'
|
||||
: session.provider === 'opencode'
|
||||
? 'text-duck-orange'
|
||||
: 'text-purple-500'
|
||||
}`}
|
||||
>
|
||||
{session.provider === 'claude' ? 'Claude' : 'OpenCode'}
|
||||
{session.provider === 'claude' ? 'Claude' : session.provider === 'opencode' ? 'OpenCode' : 'Pi'}
|
||||
</span>
|
||||
<span className="ml-2 font-mono text-duck-dark/25 dark:text-foreground/25">
|
||||
{session.id.slice(0, 8)}
|
||||
|
||||
@@ -22,7 +22,7 @@ const layout: LayoutNode = {
|
||||
};
|
||||
|
||||
type SessionListPageProps = {
|
||||
provider?: 'claude' | 'opencode';
|
||||
provider?: 'claude' | 'opencode' | 'pi-mono';
|
||||
isNew?: boolean;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user