refactor: replace focusedIndex with activeChildId

- Replace focusedIndex integer with activeChildId string in messages schema
- Add migration to convert existing data and drop focused_index column
- Add  hooks for providers.updatedAt and messageParts.lastUpdatedAt
- Add file size column to files table
- Update message navigation to use child ID-based lookup
- Simplify delete logic by removing focusedIndex math
- Fix file delete endpoint filename ([id[ -> [id])
This commit is contained in:
Zoe
2026-05-11 17:41:57 -05:00
parent 7a944afdb5
commit 103dedd9cc
16 changed files with 2106 additions and 1793 deletions
+59 -52
View File
@@ -16,66 +16,56 @@ const emit = defineEmits<{
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,
});
watch(() => message.activeChildId, async (newValue) => {
if (reqAbortController) {
reqAbortController.abort();
reqAbortController = null;
}
reqAbortController = new AbortController();
await $fetch(`/api/messages/${message.id}`, {
method: 'PATCH',
body: {
activeChildId: newValue
},
signal: reqAbortController.signal,
});
});
watch(() => message.children.length, (newCount, oldCount) => {
if (newCount === oldCount) return;
// if we are deleting children, only move the focus index if its no longer valid
// 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 (newCount === 0) {
emit('patch', { activeChildId: null });
return;
}
if (oldCount > newCount) {
if (message.deleted === true && (message.focusedIndex || 0) === newCount) {
console.log('focusedIndex Math.max(0, newCount - 1)');
message.focusedIndex = Math.max(0, newCount - 1);
return;
const activeChild = message.children.find(c => c.id === message.activeChildId);
if (!activeChild) {
emit('patch', { activeChildId: message.children[newCount - 1]?.id ?? null });
}
if ((message.focusedIndex || 0) > newCount) {
console.log('focusedIndex newCount');
message.focusedIndex = newCount;
}
return;
}
if (message.deleted === true) {
console.log('focusedIndex newCount - 1');
message.focusedIndex = newCount - 1;
return;
const newChild = message.children[newCount - 1];
if (newChild) {
emit('patch', { activeChildId: newChild.id });
}
console.log('focusedIndex newCount');
message.focusedIndex = newCount;
});
const activeMessage = computed(() => {
if (message.children.length === 0 || ((message.focusedIndex || 0) === 0 && !message.deleted)) {
return message;
if (message.activeChildId && message.children.length > 0) {
const child = message.children.find(c => c.id === message.activeChildId);
if (child) return child;
}
if (message.deleted === true) {
return message.children[Math.min((message.focusedIndex || 0), message.children.length - 1)];
if (message.deleted === true && message.children.length > 0) {
return message.children[0];
}
return message.children[message.focusedIndex! - 1];
return message;
});
const copied = ref(false);
@@ -137,10 +127,6 @@ const handleEdit = async () => {
};
const deleteMessage = () => {
// if (focusedIndex.value !== 0 && focusedIndex.value === message.children.length) {
// focusedIndex.value = Math.max(0, focusedIndex.value - 1);
// }
emit('delete');
};
@@ -151,6 +137,27 @@ const messageCount = computed(() => {
return message.children.length + 1;
});
const currentChildIndex = computed(() => {
if (!message.activeChildId) return 0;
const idx = message.children.findIndex(c => c.id === message.activeChildId);
return idx >= 0 ? idx + 1 : 0;
});
const navigateChild = (direction: -1 | 1) => {
const allItems = message.deleted === true
? message.children
: [null, ...message.children];
const currentIdx = allItems.findIndex(item =>
item === null ? !message.activeChildId : item.id === message.activeChildId
);
const newIdx = Math.max(0, Math.min(allItems.length - 1, currentIdx + direction));
const newItem = allItems[newIdx];
emit('patch', { activeChildId: newItem?.id ?? null });
};
</script>
<template>
@@ -162,21 +169,21 @@ const messageCount = computed(() => {
<div class="flex justify-between items-center">
<div>
<div v-if="messageCount > 1" class="flex gap-1">
<Tooltip :inert="message.focusedIndex === 0" :hotkey="['alt', '[']">
<button @click="emit('patch', { focusedIndex: message.focusedIndex! - 1 })"
<Tooltip :inert="currentChildIndex === 0" :hotkey="['alt', '[']">
<button @click="navigateChild(-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="message.focusedIndex === 0 ? 'opacity-0' : ''"></span>
:class="currentChildIndex === 0 ? 'opacity-0' : ''"></span>
</button>
</Tooltip>
<span class="text-xs text-[var(--text-secondary)]">
{{ (message.focusedIndex || 0) + 1 }} / {{ messageCount }}
{{ currentChildIndex + 1 }} / {{ messageCount }}
</span>
<Tooltip :inert="(message.focusedIndex || 0) + 1 === messageCount" :hotkey="['alt', ']']">
<button @click="emit('patch', { focusedIndex: (message.focusedIndex || 0) + 1 })"
<Tooltip :inert="currentChildIndex + 1 === messageCount" :hotkey="['alt', ']']">
<button @click="navigateChild(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="(message.focusedIndex || 0) + 1 === messageCount ? 'opacity-0' : ''"></span>
:class="currentChildIndex + 1 === messageCount ? 'opacity-0' : ''"></span>
</button>
</Tooltip>
</div>