|
|
@ -28,7 +28,7 @@ export default function ChatPage() {
|
|
|
|
const pageContext = usePageContext();
|
|
|
|
const pageContext = usePageContext();
|
|
|
|
const conversationId = pageContext.routeParams.id;
|
|
|
|
const conversationId = pageContext.routeParams.id;
|
|
|
|
const conversationTitle = useStore(
|
|
|
|
const conversationTitle = useStore(
|
|
|
|
(state) => state.conversations.find((c) => c.id === conversationId)?.title,
|
|
|
|
(state) => state.conversations.find((c) => c.id === conversationId)?.title
|
|
|
|
);
|
|
|
|
);
|
|
|
|
const messages = useStore((state) => state.messages);
|
|
|
|
const messages = useStore((state) => state.messages);
|
|
|
|
const message = useStore((state) => state.message);
|
|
|
|
const message = useStore((state) => state.message);
|
|
|
@ -54,30 +54,33 @@ export default function ChatPage() {
|
|
|
|
const [editingFactContent, setEditingFactContent] = useState("");
|
|
|
|
const [editingFactContent, setEditingFactContent] = useState("");
|
|
|
|
|
|
|
|
|
|
|
|
// State for editing fact triggers
|
|
|
|
// State for editing fact triggers
|
|
|
|
const [editingFactTriggerId, setEditingFactTriggerId] = useState<string | null>(null);
|
|
|
|
const [editingFactTriggerId, setEditingFactTriggerId] = useState<
|
|
|
|
const [editingFactTriggerContent, setEditingFactTriggerContent] = useState("");
|
|
|
|
string | null
|
|
|
|
|
|
|
|
>(null);
|
|
|
|
|
|
|
|
const [editingFactTriggerContent, setEditingFactTriggerContent] =
|
|
|
|
|
|
|
|
useState("");
|
|
|
|
|
|
|
|
|
|
|
|
// Handle clicking outside to cancel editing
|
|
|
|
// Handle clicking outside to cancel editing
|
|
|
|
useEffect(() => {
|
|
|
|
useEffect(() => {
|
|
|
|
function handleClickOutside(event: MouseEvent) {
|
|
|
|
function handleClickOutside(event: MouseEvent) {
|
|
|
|
if (editingFactId && event.target instanceof Element) {
|
|
|
|
if (editingFactId && event.target instanceof Element) {
|
|
|
|
const editingElement = event.target.closest('.editing-fact');
|
|
|
|
const editingElement = event.target.closest(".editing-fact");
|
|
|
|
if (!editingElement) {
|
|
|
|
if (!editingElement) {
|
|
|
|
setEditingFactId(null);
|
|
|
|
setEditingFactId(null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (editingFactTriggerId && event.target instanceof Element) {
|
|
|
|
if (editingFactTriggerId && event.target instanceof Element) {
|
|
|
|
const editingElement = event.target.closest('.editing-fact-trigger');
|
|
|
|
const editingElement = event.target.closest(".editing-fact-trigger");
|
|
|
|
if (!editingElement) {
|
|
|
|
if (!editingElement) {
|
|
|
|
setEditingFactTriggerId(null);
|
|
|
|
setEditingFactTriggerId(null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
document.addEventListener('mousedown', handleClickOutside);
|
|
|
|
document.addEventListener("mousedown", handleClickOutside);
|
|
|
|
return () => {
|
|
|
|
return () => {
|
|
|
|
document.removeEventListener('mousedown', handleClickOutside);
|
|
|
|
document.removeEventListener("mousedown", handleClickOutside);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}, [editingFactId, editingFactTriggerId]);
|
|
|
|
}, [editingFactId, editingFactTriggerId]);
|
|
|
|
|
|
|
|
|
|
|
@ -123,10 +126,10 @@ export default function ChatPage() {
|
|
|
|
|
|
|
|
|
|
|
|
async function handleUpdateFact(factId: string, content: string) {
|
|
|
|
async function handleUpdateFact(factId: string, content: string) {
|
|
|
|
// Update the local state first
|
|
|
|
// Update the local state first
|
|
|
|
setFacts(facts.map(fact =>
|
|
|
|
setFacts(
|
|
|
|
fact.id === factId ? { ...fact, content } : fact
|
|
|
|
facts.map((fact) => (fact.id === factId ? { ...fact, content } : fact))
|
|
|
|
));
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// Then update the database
|
|
|
|
// Then update the database
|
|
|
|
await trpc.chat.facts.update.mutate({ factId, content });
|
|
|
|
await trpc.chat.facts.update.mutate({ factId, content });
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -136,12 +139,19 @@ export default function ChatPage() {
|
|
|
|
await trpc.chat.factTriggers.deleteOne.mutate({ factTriggerId });
|
|
|
|
await trpc.chat.factTriggers.deleteOne.mutate({ factTriggerId });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function handleUpdateFactTrigger(factTriggerId: string, content: string) {
|
|
|
|
async function handleUpdateFactTrigger(
|
|
|
|
|
|
|
|
factTriggerId: string,
|
|
|
|
|
|
|
|
content: string
|
|
|
|
|
|
|
|
) {
|
|
|
|
// Update the local state first
|
|
|
|
// Update the local state first
|
|
|
|
setFactTriggers(factTriggers.map(factTrigger =>
|
|
|
|
setFactTriggers(
|
|
|
|
factTrigger.id === factTriggerId ? { ...factTrigger, content } : factTrigger
|
|
|
|
factTriggers.map((factTrigger) =>
|
|
|
|
));
|
|
|
|
factTrigger.id === factTriggerId
|
|
|
|
|
|
|
|
? { ...factTrigger, content }
|
|
|
|
|
|
|
|
: factTrigger
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// Then update the database
|
|
|
|
// Then update the database
|
|
|
|
await trpc.chat.factTriggers.update.mutate({ factTriggerId, content });
|
|
|
|
await trpc.chat.factTriggers.update.mutate({ factTriggerId, content });
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -185,7 +195,10 @@ export default function ChatPage() {
|
|
|
|
e.preventDefault();
|
|
|
|
e.preventDefault();
|
|
|
|
const messagesWithNewUserMessage = [
|
|
|
|
const messagesWithNewUserMessage = [
|
|
|
|
...messages,
|
|
|
|
...messages,
|
|
|
|
{ role: "user" as const, content: message } as DraftMessage,
|
|
|
|
{
|
|
|
|
|
|
|
|
role: "user" as const,
|
|
|
|
|
|
|
|
parts: [{ type: "text", text: message }],
|
|
|
|
|
|
|
|
} as DraftMessage,
|
|
|
|
];
|
|
|
|
];
|
|
|
|
setMessages(messagesWithNewUserMessage);
|
|
|
|
setMessages(messagesWithNewUserMessage);
|
|
|
|
setLoading(true);
|
|
|
|
setLoading(true);
|
|
|
@ -201,7 +214,8 @@ export default function ChatPage() {
|
|
|
|
id: response.insertedUserMessage?.id,
|
|
|
|
id: response.insertedUserMessage?.id,
|
|
|
|
conversationId,
|
|
|
|
conversationId,
|
|
|
|
role: "user" as const,
|
|
|
|
role: "user" as const,
|
|
|
|
content: message,
|
|
|
|
// content: message,
|
|
|
|
|
|
|
|
parts: [{ type: "text", text: message }],
|
|
|
|
index: response.insertedUserMessage?.index,
|
|
|
|
index: response.insertedUserMessage?.index,
|
|
|
|
runningSummary: undefined,
|
|
|
|
runningSummary: undefined,
|
|
|
|
} as CommittedMessage,
|
|
|
|
} as CommittedMessage,
|
|
|
@ -209,7 +223,9 @@ export default function ChatPage() {
|
|
|
|
id: response.insertedAssistantMessage?.id,
|
|
|
|
id: response.insertedAssistantMessage?.id,
|
|
|
|
conversationId,
|
|
|
|
conversationId,
|
|
|
|
role: "assistant" as const,
|
|
|
|
role: "assistant" as const,
|
|
|
|
content: response.insertedAssistantMessage?.content,
|
|
|
|
// content: response.insertedAssistantMessage?.content,
|
|
|
|
|
|
|
|
// parts: [{ type: "text", text: response.insertedAssistantMessage?.content }],
|
|
|
|
|
|
|
|
parts: response.insertedAssistantMessage?.parts,
|
|
|
|
index: response.insertedAssistantMessage?.index,
|
|
|
|
index: response.insertedAssistantMessage?.index,
|
|
|
|
runningSummary:
|
|
|
|
runningSummary:
|
|
|
|
response.insertedAssistantMessage?.runningSummary ||
|
|
|
|
response.insertedAssistantMessage?.runningSummary ||
|
|
|
@ -270,9 +286,7 @@ export default function ChatPage() {
|
|
|
|
>
|
|
|
|
>
|
|
|
|
<IconCheck size={16} />
|
|
|
|
<IconCheck size={16} />
|
|
|
|
</ActionIcon>
|
|
|
|
</ActionIcon>
|
|
|
|
<ActionIcon
|
|
|
|
<ActionIcon onClick={() => setEditingFactId(null)}>
|
|
|
|
onClick={() => setEditingFactId(null)}
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<IconX size={16} />
|
|
|
|
<IconX size={16} />
|
|
|
|
</ActionIcon>
|
|
|
|
</ActionIcon>
|
|
|
|
</Group>
|
|
|
|
</Group>
|
|
|
@ -310,11 +324,16 @@ export default function ChatPage() {
|
|
|
|
<Group wrap="nowrap" className="editing-fact-trigger">
|
|
|
|
<Group wrap="nowrap" className="editing-fact-trigger">
|
|
|
|
<Textarea
|
|
|
|
<Textarea
|
|
|
|
value={editingFactTriggerContent}
|
|
|
|
value={editingFactTriggerContent}
|
|
|
|
onChange={(e) => setEditingFactTriggerContent(e.target.value)}
|
|
|
|
onChange={(e) =>
|
|
|
|
|
|
|
|
setEditingFactTriggerContent(e.target.value)
|
|
|
|
|
|
|
|
}
|
|
|
|
onKeyDown={(e) => {
|
|
|
|
onKeyDown={(e) => {
|
|
|
|
if (e.key === "Enter") {
|
|
|
|
if (e.key === "Enter") {
|
|
|
|
e.preventDefault();
|
|
|
|
e.preventDefault();
|
|
|
|
handleUpdateFactTrigger(factTrigger.id, editingFactTriggerContent);
|
|
|
|
handleUpdateFactTrigger(
|
|
|
|
|
|
|
|
factTrigger.id,
|
|
|
|
|
|
|
|
editingFactTriggerContent
|
|
|
|
|
|
|
|
);
|
|
|
|
setEditingFactTriggerId(null);
|
|
|
|
setEditingFactTriggerId(null);
|
|
|
|
} else if (e.key === "Escape") {
|
|
|
|
} else if (e.key === "Escape") {
|
|
|
|
setEditingFactTriggerId(null);
|
|
|
|
setEditingFactTriggerId(null);
|
|
|
@ -325,15 +344,16 @@ export default function ChatPage() {
|
|
|
|
/>
|
|
|
|
/>
|
|
|
|
<ActionIcon
|
|
|
|
<ActionIcon
|
|
|
|
onClick={() => {
|
|
|
|
onClick={() => {
|
|
|
|
handleUpdateFactTrigger(factTrigger.id, editingFactTriggerContent);
|
|
|
|
handleUpdateFactTrigger(
|
|
|
|
|
|
|
|
factTrigger.id,
|
|
|
|
|
|
|
|
editingFactTriggerContent
|
|
|
|
|
|
|
|
);
|
|
|
|
setEditingFactTriggerId(null);
|
|
|
|
setEditingFactTriggerId(null);
|
|
|
|
}}
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
>
|
|
|
|
<IconCheck size={16} />
|
|
|
|
<IconCheck size={16} />
|
|
|
|
</ActionIcon>
|
|
|
|
</ActionIcon>
|
|
|
|
<ActionIcon
|
|
|
|
<ActionIcon onClick={() => setEditingFactTriggerId(null)}>
|
|
|
|
onClick={() => setEditingFactTriggerId(null)}
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<IconX size={16} />
|
|
|
|
<IconX size={16} />
|
|
|
|
</ActionIcon>
|
|
|
|
</ActionIcon>
|
|
|
|
</Group>
|
|
|
|
</Group>
|
|
|
@ -396,7 +416,12 @@ function Messages({
|
|
|
|
p="md"
|
|
|
|
p="md"
|
|
|
|
bdrs="md"
|
|
|
|
bdrs="md"
|
|
|
|
>
|
|
|
|
>
|
|
|
|
<Markdown>{message.content}</Markdown>
|
|
|
|
<Markdown>
|
|
|
|
|
|
|
|
{message.parts
|
|
|
|
|
|
|
|
.filter((p) => p.type === "text")
|
|
|
|
|
|
|
|
.map((p) => p.text)
|
|
|
|
|
|
|
|
.join("\n")}
|
|
|
|
|
|
|
|
</Markdown>
|
|
|
|
</Box>
|
|
|
|
</Box>
|
|
|
|
</HoverCard.Target>
|
|
|
|
</HoverCard.Target>
|
|
|
|
<HoverCard.Dropdown>
|
|
|
|
<HoverCard.Dropdown>
|
|
|
|