This commit is contained in:
2026-02-20 04:32:50 +00:00
parent d7503ca56b
commit d9f6eb14ed
24 changed files with 1426 additions and 1183 deletions
@@ -1,4 +1,4 @@
import { useState, useEffect, useRef, useMemo } from 'react';
import { useState, useEffect, useRef } from 'react';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
@@ -8,12 +8,8 @@ import { ArrowLeft, Pencil, Plus, Check, X, Trash2, Search, ChevronRight } from
import { Button } from '@/components/ui/button';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/dialog';
import { useClient } from 'hooks/useClient';
import { useVisibleClaudeModels, useVisibleOpenCodeModels, useVisiblePiMonoModels } from '@/state/useModels';
import { useSettings } from '@/state/useSettings';
import { useVisiblePiMonoModels } from '@/state/useModels';
import { Card } from '@/components/Card';
import type { ChatMessage } from 'apps/Chat';
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';
type CapabilitySummary = {
@@ -60,130 +56,14 @@ type CapabilityChatProps = {
onResponseEnd?: () => void;
};
type CapabilityChatInnerProps = CapabilityChatProps & {
onProviderChange: (p: 'claude' | 'opencode' | 'pi-mono') => void;
};
const CapabilityChatClaude = ({
kind,
endpoint,
dirName,
filePath,
resourceDir,
chatSessionId,
isNew,
description,
onResponseEnd,
onProviderChange,
}: CapabilityChatInnerProps) => {
const client = useClient();
const claudeModels = useVisibleClaudeModels();
const seedFile = `${kind.toUpperCase()}.md`;
const promptFrontmatter = chatSessionId
? undefined
: `<frontmatter>\ninput file: ${filePath}\n${seedFile}: ${filePath}\ndir: ${resourceDir}\n\nBe aware of any extra files alongside the same dir as the ${kind} file we're handling, for possible extra context. You can also, if pertinent, create scripts or other files that will help you in the future.\n</frontmatter>`;
const defaultInput = chatSessionId
? undefined
: isNew
? description ?? `Help me create the content for this new ${kind} file`
: `Help me understand and improve this ${kind} file`;
const storage = useMemo(
() => ({
load: async () => {
const data = await client.get<{ sessionId: string | null; messages: ChatMessage[] }>(
`${endpoint}/${dirName}/chat`,
);
return { sessionId: data.sessionId, messages: data.messages ?? [] };
},
save: async (sessionId: string, messages: ChatMessage[]) => {
await client.put(`${endpoint}/${dirName}/chat`, { sessionId, messages });
},
}),
[endpoint, dirName],
);
const claude = useClaude(chatSessionId ?? undefined, undefined, {
replaceUrl: false,
storage,
resourceChatDir: resourceDir,
});
const onResponseEndRef = useRef(onResponseEnd);
onResponseEndRef.current = onResponseEnd;
const wasGenerating = useRef(false);
useEffect(() => {
if (wasGenerating.current && !claude.isGenerating) {
onResponseEndRef.current?.();
}
wasGenerating.current = claude.isGenerating;
}, [claude.isGenerating]);
return (
<EmbeddableChat
chat={claude}
provider="claude"
availableModels={claudeModels}
onProviderChange={onProviderChange}
defaultInput={defaultInput}
promptPrefix={promptFrontmatter}
className="h-full"
/>
);
};
const CapabilityChatOpenCode = ({
export const CapabilityChat = ({
kind,
filePath,
resourceDir,
isNew,
description,
onResponseEnd,
onProviderChange,
}: CapabilityChatInnerProps) => {
const openCodeModels = useVisibleOpenCodeModels();
const seedFile = `${kind.toUpperCase()}.md`;
const promptFrontmatter = `<frontmatter>\ninput file: ${filePath}\n${seedFile}: ${filePath}\ndir: ${resourceDir}\n\nBe aware of any extra files alongside the same dir as the ${kind} file we're handling, for possible extra context. You can also, if pertinent, create scripts or other files that will help you in the future.\n</frontmatter>`;
const defaultInput = isNew
? description ?? `Help me create the content for this new ${kind} file`
: `Help me understand and improve this ${kind} file`;
const opencode = useOpenCode(undefined, undefined, { replaceUrl: false });
const onResponseEndRef = useRef(onResponseEnd);
onResponseEndRef.current = onResponseEnd;
const wasGenerating = useRef(false);
useEffect(() => {
if (wasGenerating.current && !opencode.isGenerating) {
onResponseEndRef.current?.();
}
wasGenerating.current = opencode.isGenerating;
}, [opencode.isGenerating]);
return (
<EmbeddableChat
chat={opencode}
provider="opencode"
availableModels={openCodeModels}
onProviderChange={onProviderChange}
defaultInput={defaultInput}
promptPrefix={promptFrontmatter}
className="h-full"
/>
);
};
const CapabilityChatPiMono = ({
kind,
filePath,
resourceDir,
isNew,
description,
onResponseEnd,
onProviderChange,
}: CapabilityChatInnerProps) => {
}: CapabilityChatProps) => {
const piMonoModels = useVisiblePiMonoModels();
const seedFile = `${kind.toUpperCase()}.md`;
const promptFrontmatter = `<frontmatter>\ninput file: ${filePath}\n${seedFile}: ${filePath}\ndir: ${resourceDir}\n\nBe aware of any extra files alongside the same dir as the ${kind} file we're handling, for possible extra context. You can also, if pertinent, create scripts or other files that will help you in the future.\n</frontmatter>`;
@@ -207,9 +87,7 @@ const CapabilityChatPiMono = ({
return (
<EmbeddableChat
chat={piMono}
provider="pi-mono"
availableModels={piMonoModels}
onProviderChange={onProviderChange}
defaultInput={defaultInput}
promptPrefix={promptFrontmatter}
className="h-full"
@@ -217,19 +95,6 @@ const CapabilityChatPiMono = ({
);
};
export const CapabilityChat = (props: CapabilityChatProps) => {
const { settings } = useSettings();
const [provider, setProvider] = useState<'claude' | 'opencode' | 'pi-mono'>(settings.chat.defaultProvider);
if (provider === 'claude') {
return <CapabilityChatClaude key="claude" {...props} onProviderChange={setProvider} />;
}
if (provider === 'opencode') {
return <CapabilityChatOpenCode key="opencode" {...props} onProviderChange={setProvider} />;
}
return <CapabilityChatPiMono key="pi-mono" {...props} onProviderChange={setProvider} />;
};
export const FrontmatterBlock = ({ yaml }: { yaml: string }) => {
const [open, setOpen] = useState(false);