feat: add timestamp and forking
This commit is contained in:
@@ -7,14 +7,19 @@ defineProps<{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col gap-2 max-w-full bg-[var(--bg-container)] py-2 px-3 rounded-xl">
|
||||
<MarkdownRenderer v-if="message.content" :finished="true" :content="message.content" :id="message.id" />
|
||||
<div v-if="message.attachments && message.attachments.length > 0" class="flex flex-col gap-2">
|
||||
<div v-for="attachment in message.attachments" :key="attachment.id"
|
||||
class="flex flex-wrap items-center gap-2">
|
||||
<div
|
||||
class="flex-shrink-0 rounded-lg overflow-hidden bg-[var(--bg-surface)] flex items-center justify-center">
|
||||
<AttachmentDisplay :file="attachment.file" />
|
||||
<div class="flex flex-col items-end gap-1 max-w-full">
|
||||
<time class="text-[10px] text-[var(--text-secondary)] opacity-60 select-none pr-1">
|
||||
{{ new Date(message.createdAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) }}
|
||||
</time>
|
||||
<div class="flex flex-col gap-2 max-w-full bg-[var(--bg-container)] py-2 px-3 rounded-xl">
|
||||
<MarkdownRenderer v-if="message.content" :finished="true" :content="message.content" :id="message.id" />
|
||||
<div v-if="message.attachments && message.attachments.length > 0" class="flex flex-col gap-2">
|
||||
<div v-for="attachment in message.attachments" :key="attachment.id"
|
||||
class="flex flex-wrap items-center gap-2">
|
||||
<div
|
||||
class="flex-shrink-0 rounded-lg overflow-hidden bg-[var(--bg-surface)] flex items-center justify-center">
|
||||
<AttachmentDisplay :file="attachment.file" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,6 +12,7 @@ const emit = defineEmits<{
|
||||
delete: [];
|
||||
edit: [value: string];
|
||||
patch: [updates: Partial<Message>];
|
||||
fork: [];
|
||||
}>();
|
||||
|
||||
let reqAbortController: AbortController | null = null;
|
||||
@@ -126,6 +127,10 @@ const handleEdit = async () => {
|
||||
});
|
||||
};
|
||||
|
||||
const forkMessage = () => {
|
||||
emit('fork');
|
||||
};
|
||||
|
||||
const deleteMessage = () => {
|
||||
emit('delete');
|
||||
};
|
||||
@@ -205,6 +210,10 @@ const navigateChild = (direction: -1 | 1) => {
|
||||
class="flex justify-center items-center w-7 h-6 @hover:bg-[var(--color-hover)]">
|
||||
<span :class="copied ? 'i-mynaui-check text-emerald-500' : 'i-mynaui-copy text-4.5'"></span>
|
||||
</button>
|
||||
<button @click="forkMessage"
|
||||
class="flex justify-center items-center w-7 h-6 @hover:bg-[var(--color-hover)]">
|
||||
<span class="i-mynaui-git-branch text-4.5"></span>
|
||||
</button>
|
||||
<Tooltip :hotkey="['ctrl', 'shift', 'backspace']">
|
||||
<button @click="deleteMessage"
|
||||
class="flex justify-center items-center w-7 h-6 text-red-500 @hover:bg-[var(--color-hover)]">
|
||||
|
||||
@@ -198,11 +198,43 @@ export const useAgents = async () => {
|
||||
});
|
||||
}
|
||||
|
||||
const forkTopic = async (topicId: string, messageId: string): Promise<string | null> => {
|
||||
try {
|
||||
const result = await $fetch<{ ok: boolean; topicId: string; name: string }>(`/api/topic/${topicId}/fork`, {
|
||||
method: 'POST',
|
||||
body: { messageId },
|
||||
});
|
||||
|
||||
if (result.ok) {
|
||||
const agent = agents.value.find(a => a.topics.some(t => t.id === topicId));
|
||||
if (agent) {
|
||||
const newTopic: Topic = {
|
||||
id: result.topicId,
|
||||
name: result.name,
|
||||
agentId: agent.id,
|
||||
userId: agent.userId,
|
||||
renaming: false,
|
||||
createdAt: new Date(),
|
||||
};
|
||||
agents.value = agents.value.map(a =>
|
||||
a.id === agent.id ? { ...a, topics: [newTopic, ...a.topics] } as AgentWithTopics : a
|
||||
);
|
||||
}
|
||||
return result.topicId;
|
||||
}
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error('Failed to fork topic:', error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
agents,
|
||||
refresh,
|
||||
createAgent,
|
||||
createTopic,
|
||||
forkTopic,
|
||||
getAgent,
|
||||
patchAgentLocally,
|
||||
patchTopicLocally,
|
||||
|
||||
@@ -8,7 +8,7 @@ const rootStart = Date.now();
|
||||
const chatPaneWrapper = ref<HTMLElement | null>(null);
|
||||
const inputValue = ref<BaseMessage>({ content: '', fileIds: [] });
|
||||
const route = useRoute();
|
||||
const { getAgent, deleteTopic } = await useAgents();
|
||||
const { getAgent, deleteTopic, forkTopic } = await useAgents();
|
||||
const { open: sidebarOpen, openSidebar } = useSidebar();
|
||||
const { providers, allModels } = await useModels();
|
||||
const { addShortcut } = useKeyboardShortcuts();
|
||||
@@ -114,6 +114,19 @@ const flatMessages = computed(() => {
|
||||
return messages;
|
||||
});
|
||||
|
||||
const handleFork = async (message: Message) => {
|
||||
const messageId = message.activeChildId && message.children.length > 0
|
||||
? message.activeChildId
|
||||
: message.id;
|
||||
|
||||
const newTopicId = await forkTopic(topicId.value, messageId);
|
||||
if (newTopicId) {
|
||||
await navigateTo(`/agent/${route.params.id}/topic/${newTopicId}`);
|
||||
} else {
|
||||
console.error('Failed to fork topic');
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async (rootMessage: Message) => {
|
||||
let messageId: string;
|
||||
if (rootMessage.activeChildId && rootMessage.children.length > 0) {
|
||||
@@ -306,7 +319,8 @@ console.log("full page render took", Date.now() - rootStart);
|
||||
<Message v-for="message in topic.messages" :key="message.id" :message="message"
|
||||
@edit="(value) => patchMessageLocally(message.id, { content: value })"
|
||||
@patch="(updates) => patchMessageLocally(message.id, updates)"
|
||||
@delete="handleDelete(message)" @regenerate="handleRegenerate(message)" />
|
||||
@delete="handleDelete(message)" @regenerate="handleRegenerate(message)"
|
||||
@fork="handleFork(message)" />
|
||||
</template>
|
||||
</Suspense>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user