streaming, markdown, model selecting, and lots more
This commit is contained in:
@@ -1,5 +1,23 @@
|
||||
<script setup lang="ts">
|
||||
const route = useRoute();
|
||||
const topicsListRef = ref<HTMLElement | null>(null);
|
||||
const topicsListHeight = ref('auto');
|
||||
const topicsListOpacity = ref(1);
|
||||
const topicsListScale = ref(1);
|
||||
const { getAgent } = await useAgents();
|
||||
|
||||
const triplit = useTriplitClient();
|
||||
|
||||
const activeAgent = computed(() => getAgent(route.params.id as string));
|
||||
|
||||
const topics = computed(() => {
|
||||
if (activeAgent.value === undefined) return [];
|
||||
// return the todos but sorted and in a new array do not add messages or anything to the object, JUST SORT IT
|
||||
return activeAgent.value.topics
|
||||
.map((topic) => topic)
|
||||
.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime())
|
||||
.reverse();
|
||||
});
|
||||
|
||||
const routeParts = computed(() => {
|
||||
return route.path.replace('/agent/', '').split('/');
|
||||
@@ -18,19 +36,122 @@ const pageInfo = computed(() => {
|
||||
return 'agent-profile';
|
||||
}
|
||||
});
|
||||
|
||||
const topicsOpen = ref(true);
|
||||
|
||||
function easeInOutQuad(x: number): number {
|
||||
return x < 0.5 ? 2 * x * x : 1 - (-2 * x + 2) ** 2 / 2;
|
||||
}
|
||||
|
||||
const toggleAgentsList = () => {
|
||||
if (!topicsListRef.value) return;
|
||||
const animationLength = 200;
|
||||
let animationStart: number | null = null;
|
||||
|
||||
let startHeight: number;
|
||||
const startOpacity = topicsListOpacity.value;
|
||||
const startScale = topicsListScale.value;
|
||||
if (topicsListHeight.value === 'auto') {
|
||||
startHeight = topicsListRef.value.clientHeight;
|
||||
} else {
|
||||
startHeight = Number(topicsListHeight.value.replace('px', ''));
|
||||
}
|
||||
|
||||
const targetHeight = topicsOpen.value ? 0 : topicsListRef.value.scrollHeight;
|
||||
const targetOpacity = topicsOpen.value ? 0 : 1;
|
||||
const targetScale = topicsOpen.value ? 0.95 : 1;
|
||||
topicsOpen.value = !topicsOpen.value;
|
||||
|
||||
const animate = (timestamp: number) => {
|
||||
if (!animationStart) animationStart = timestamp;
|
||||
|
||||
const elapsed = timestamp - animationStart;
|
||||
const progress = Math.min(elapsed / animationLength, 1);
|
||||
|
||||
const currentHeight = startHeight + (targetHeight - startHeight) * easeInOutQuad(progress);
|
||||
const currentOpacity = startOpacity + (targetOpacity - startOpacity) * easeInOutQuad(progress);
|
||||
const currentScale = startScale + (targetScale - startScale) * easeInOutQuad(progress);
|
||||
|
||||
topicsListOpacity.value = currentOpacity;
|
||||
topicsListScale.value = currentScale;
|
||||
topicsListHeight.value = `${currentHeight}px`;
|
||||
|
||||
if (progress < 1) {
|
||||
requestAnimationFrame(animate);
|
||||
} else {
|
||||
if (topicsOpen.value) {
|
||||
topicsListHeight.value = 'auto';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
requestAnimationFrame(animate);
|
||||
};
|
||||
|
||||
const renameTopic = (topicId: string) => {
|
||||
console.log('renameTopic', topicId);
|
||||
};
|
||||
|
||||
const deleteTopic = async (topicId: string) => {
|
||||
if (route.params.topicId === topicId) {
|
||||
if (route.params.id) {
|
||||
await navigateTo(`/agent/${route.params.id}`);
|
||||
} else {
|
||||
await navigateTo('/');
|
||||
}
|
||||
}
|
||||
await triplit.delete('topics', topicId);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav class="flex flex-col gap-1">
|
||||
|
||||
<!-- Agent Info Link -->
|
||||
<div class="mt-2">
|
||||
<NuxtLink :to="`/agent/${routeParts[0]}/profile`" class="decoration-none text-[var(--color-subtle)]">
|
||||
<SidenavItem name="Agent Info" icon="mynaui:info-square" :active="pageInfo === 'agent-profile'" />
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<div class="mt-2 flex flex-col">
|
||||
<SidenavItem :to="`/agent/${routeParts[0]}/profile`" name="Agent Info" icon="mynaui:info-square"
|
||||
:active="pageInfo === 'agent-profile'" />
|
||||
|
||||
<!-- Topics Section -->
|
||||
<SidenavNavAgentTopics />
|
||||
<!-- Topics Section -->
|
||||
<button @click="toggleAgentsList"
|
||||
class="flex items-center justify-between gap-2 px-2 py-2 rounded-lg bg-transparent hover:bg-[var(--color-highlight)] focus-visible:bg-[var(--color-highlight)] transition-colors w-full text-left">
|
||||
<span class="text-sm font-medium">Topics</span>
|
||||
<Icon name="mynaui:chevron-down" :class="['w-4 h-4', topicsOpen ? '' : '-rotate-90']" />
|
||||
</button>
|
||||
|
||||
<div ref="topicsListRef" :inert="!topicsOpen"
|
||||
:style="{ height: topicsListHeight, opacity: topicsListOpacity, transform: `scale(${topicsListScale})` }"
|
||||
class="mt-1 gap-1 flex flex-col transform-origin-center-top overflow-y-hidden">
|
||||
<SidenavItem draggable="false" class="[&>div>div>div>[dots]]:hover:opacity-100 relative"
|
||||
v-if="activeAgent?.topics !== undefined" v-for="topic in topics"
|
||||
:to="`/agent/${activeAgent.id}/topic/${topic.id}`" :active="topic.id === route.params.topicId"
|
||||
:name="topic.name" :key="topic.id">
|
||||
<Dropdown class="shrink-0" verticality="descending" placement="right">
|
||||
<template #trigger="{ toggle, isOpen }">
|
||||
<div dots @click.prevent.stop="toggle"
|
||||
class="opacity-0 p-1 flex items-center justify-center rounded-md hover:bg-[var(--color-highlight)] focus-visible:bg-[var(--color-highlight)] transition-all duration-200 ease-[cubic-bezier(0.5,_1,_0.89,_1)]">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18"
|
||||
viewBox="0 0 24 24"><!-- Icon from Solar by 480 Design - https://creativecommons.org/licenses/by/4.0/ -->
|
||||
<path fill="currentColor"
|
||||
d="M7 12a2 2 0 1 1-4 0a2 2 0 0 1 4 0m7 0a2 2 0 1 1-4 0a2 2 0 0 1 4 0m7 0a2 2 0 1 1-4 0a2 2 0 0 1 4 0" />
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="shadow-lg rounded p-1 flex flex-col min-w-[120px] gap-1">
|
||||
<button @click.prevent="renameTopic(topic.id)"
|
||||
class="text-left px-3 py-1.5 text-sm hover:bg-[var(--color-highlight)] rounded-lg transition-colors duration-200 ease-[cubic-bezier(0.5,_1,_0.89,_1)]">
|
||||
Rename
|
||||
</button>
|
||||
<button @click.prevent="deleteTopic(topic.id)"
|
||||
class="text-left px-3 py-1.5 text-sm text-red-600 hover:bg-red-600/20 rounded-lg transition-colors duration-200 ease-[cubic-bezier(0.5,_1,_0.89,_1)]">
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</Dropdown>
|
||||
</SidenavItem>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user