feat: ditch triplit, move to postgresql + drizzle orm

This commit is contained in:
Zoe
2026-04-08 17:03:07 -05:00
parent c341c96798
commit e2e3ac6e86
121 changed files with 6680 additions and 4373 deletions
+73 -49
View File
@@ -1,21 +1,36 @@
<script setup lang="ts">
import type { Message } from '~/composables/useChat';
import { assert } from '~~/utils/assert';
const triplit = useTriplitClient();
const { openDialog } = useDialog();
const { message } = defineProps<{
message: Message
}>();
const focusedIndex = computed({
get: () => {
return message.focusedIndex || 0;
},
set: (newValue) => {
triplit.update('messages', message.id, {
focusedIndex: newValue
const emit = defineEmits<{
regenerate: [];
delete: [];
edit: [value: string];
patch: [updates: Partial<Message>];
}>();
let reqAbortController: AbortController | null = null;
watch(() => message.focusedIndex, async (newValue) => {
if (newValue !== undefined) {
if (reqAbortController) {
reqAbortController.abort();
reqAbortController = null;
}
reqAbortController = new AbortController();
await $fetch(`/api/messages/${message.id}`, {
method: 'PATCH',
body: {
focusedIndex: newValue
},
signal: reqAbortController.signal,
});
}
});
@@ -27,44 +42,42 @@ watch(() => message.children.length, (newCount, oldCount) => {
// e.g. if we we have 4 children, and are focused on the 2nd, if we delete it,
// we want to keep the focus on the 2nd index. It just makes me feel better
if (oldCount > newCount) {
if (message.deleted === true && focusedIndex.value === newCount) {
focusedIndex.value = Math.max(0, newCount - 1);
if (message.deleted === true && (message.focusedIndex || 0) === newCount) {
console.log('focusedIndex Math.max(0, newCount - 1)');
message.focusedIndex = Math.max(0, newCount - 1);
return;
}
if (focusedIndex.value > newCount) {
focusedIndex.value = newCount;
if ((message.focusedIndex || 0) > newCount) {
console.log('focusedIndex newCount');
message.focusedIndex = newCount;
}
return;
}
if (message.deleted === true) {
focusedIndex.value = newCount - 1;
console.log('focusedIndex newCount - 1');
message.focusedIndex = newCount - 1;
return;
}
focusedIndex.value = newCount;
console.log('focusedIndex newCount');
message.focusedIndex = newCount;
});
const activeMessage = computed(() => {
if (message.children.length === 0 || (focusedIndex.value === 0 && !message.deleted)) {
if (message.children.length === 0 || ((message.focusedIndex || 0) === 0 && !message.deleted)) {
return message;
}
if (message.deleted === true) {
return message.children[Math.min(focusedIndex.value, message.children.length - 1)];
return message.children[Math.min((message.focusedIndex || 0), message.children.length - 1)];
}
return message.children[focusedIndex.value - 1];
return message.children[message.focusedIndex! - 1];
});
const emit = defineEmits<{
regenerate: []
delete: []
}>();
const copied = ref(false);
const copyMessage = async () => {
if (activeMessage.value!.role === 'user') {
@@ -91,33 +104,44 @@ const regenerateMessage = async () => {
}
const handleEdit = async () => {
const originalContent = message.content!;
openDialog<string>(DialogType.Textbox, async (res) => {
if (res.ok) {
if (!res.data) return;
await triplit.update('messages', message.id, {
content: res.data
const req = await $fetch(`/api/messages/${message.id}`, {
method: 'PATCH',
body: {
content: res.data
},
onRequest() {
message.content = res.data;
emit('edit', res.data);
},
onResponseError() {
message.content = originalContent;
emit('edit', originalContent);
},
});
assert('flush' in triplit);
await triplit.flush();
if (!req.ok) {
return;
}
await regenerateMessage();
}
}, {
title: 'Edit Message',
initialValue: message.content
initialValue: message.content!
});
};
const deleteMessage = () => {
if (focusedIndex.value !== 0 && focusedIndex.value === message.children.length) {
focusedIndex.value = Math.max(0, focusedIndex.value - 1);
}
// if (focusedIndex.value !== 0 && focusedIndex.value === message.children.length) {
// focusedIndex.value = Math.max(0, focusedIndex.value - 1);
// }
nextTick(() => {
emit('delete');
});
emit('delete');
};
const messageCount = computed(() => {
@@ -138,21 +162,21 @@ const messageCount = computed(() => {
<div class="flex justify-between items-center">
<div>
<div v-if="messageCount > 1" class="flex gap-1">
<Tooltip :inert="focusedIndex === 0" :hotkey="['alt', '[']">
<button @click="focusedIndex = focusedIndex! - 1"
class="hover:bg-[var(--color-hover)] rounded transition duration-150 ease-[cubic-bezier(0.5,_1,_0.89,_1)]">
<Tooltip :inert="message.focusedIndex === 0" :hotkey="['alt', '[']">
<button @click="emit('patch', { focusedIndex: message.focusedIndex! - 1 })"
class="@hover:bg-[var(--color-hover)] rounded transition duration-150 ease-[cubic-bezier(0.5,_1,_0.89,_1)]">
<span class="i-mynaui-chevron-left w-4 h-4 text-[var(--text-secondary)]"
:class="focusedIndex === 0 ? 'opacity-0' : ''"></span>
:class="message.focusedIndex === 0 ? 'opacity-0' : ''"></span>
</button>
</Tooltip>
<span class="text-xs text-[var(--text-secondary)]">
{{ focusedIndex + 1 }} / {{ messageCount }}
{{ (message.focusedIndex || 0) + 1 }} / {{ messageCount }}
</span>
<Tooltip :inert="focusedIndex + 1 === messageCount" :hotkey="['alt', ']']">
<button @click="focusedIndex = focusedIndex + 1"
class="hover:bg-[var(--color-hover)] rounded transition duration-150 ease-[cubic-bezier(0.5,_1,_0.89,_1)]">
<Tooltip :inert="(message.focusedIndex || 0) + 1 === messageCount" :hotkey="['alt', ']']">
<button @click="emit('patch', { focusedIndex: (message.focusedIndex || 0) + 1 })"
class="@hover:bg-[var(--color-hover)] rounded transition duration-150 ease-[cubic-bezier(0.5,_1,_0.89,_1)]">
<span class="i-mynaui-chevron-right w-4 h-4 text-[var(--text-secondary)]"
:class="focusedIndex + 1 === messageCount ? 'opacity-0' : ''"></span>
:class="(message.focusedIndex || 0) + 1 === messageCount ? 'opacity-0' : ''"></span>
</button>
</Tooltip>
</div>
@@ -162,21 +186,21 @@ const messageCount = computed(() => {
class="self-end mt-1 w-fit bg-[var(--bg-container)] text-[var(--text-secondary)] gap-px flex items-center rounded-md overflow-hidden opacity-0 group-hover:opacity-100 transition-opacity duration-200 ease-[cubic-bezier(0.5,_1,_0.89,_1)]">
<Tooltip :hotkey="['ctrl', 'shift', 'enter']">
<button @click="regenerateMessage"
class="flex justify-center items-center w-7 h-6 hover:bg-[var(--color-hover)]">
class="flex justify-center items-center w-7 h-6 @hover:bg-[var(--color-hover)]">
<span class="i-mynaui-refresh text-4.5"></span>
</button>
</Tooltip>
<button v-if="message.role === 'user'" @click="handleEdit"
class="flex justify-center items-center w-7 h-6 hover:bg-[var(--color-hover)]">
class="flex justify-center items-center w-7 h-6 @hover:bg-[var(--color-hover)]">
<span class="i-mynaui-pencil text-4.5"></span>
</button>
<button @click="copyMessage" :class="{ 'text-emerald-500': copied }"
class="flex justify-center items-center w-7 h-6 hover:bg-[var(--color-hover)]">
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>
<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)]">
class="flex justify-center items-center w-7 h-6 text-red-500 @hover:bg-[var(--color-hover)]">
<span class="i-mynaui-trash text-5"></span>
</button>
</Tooltip>