feat: instance invites

This commit is contained in:
Zoe
2026-10-08 13:50:44 -05:00
parent f6e1bc1f47
commit fae05ebc4c
17 changed files with 3962 additions and 26 deletions
+9 -4
View File
@@ -178,7 +178,12 @@ watch(() => props.agent?.defaultModelId, (newModelId) => {
}
})
// sending needs a model, and there are none until a provider is set up
const noModels = computed(() => allModels.value.length === 0);
const handleSubmit = () => {
if (noModels.value) return;
if (isUploading.value || hasFailedUploads.value) {
return;
}
@@ -454,8 +459,8 @@ onUnmounted(() => {
<div class="flex items-center gap-1 shrink-0">
<FileSelector ref="fileSelectorRef" :selected-model="selectedModel" v-model="files"></FileSelector>
<div v-if="allowManualRole" class="flex items-center">
<button aria-label="Send message" @click="handleSubmit"
:disabled="((!inputValue.content.trim() && files.length === 0) && !loading) || isUploading || hasFailedUploads"
<button :aria-label="noModels ? 'Send (add a provider first)' : 'Send message'" @click="handleSubmit"
:disabled="((!inputValue.content.trim() && files.length === 0) && !loading) || isUploading || hasFailedUploads || noModels"
:class="[
'h-8 w-8 rounded-l-lg rounded-r-none transition-all duration-200 flex items-center justify-center disabled:cursor-not-allowed disabled:bg-transparent',
(inputValue.content.trim() || files.length > 0) && !loading && !isUploading && !hasFailedUploads
@@ -496,8 +501,8 @@ onUnmounted(() => {
</template>
</Dropdown>
</div>
<button v-else aria-label="Send message" @click="handleSubmit"
:disabled="((!inputValue.content.trim() && files.length === 0) && !loading) || isUploading || hasFailedUploads"
<button v-else :aria-label="noModels ? 'Send (add a provider first)' : 'Send message'" @click="handleSubmit"
:disabled="((!inputValue.content.trim() && files.length === 0) && !loading) || isUploading || hasFailedUploads || noModels"
:class="[
'h-8 w-8 rounded-xl transition-all duration-200 flex items-center justify-center disabled:cursor-not-allowed disabled:bg-transparent',
(inputValue.content.trim() || files.length > 0) && !loading && !isUploading && !hasFailedUploads
+22 -4
View File
@@ -22,6 +22,9 @@ const openProviderSettings = async (providerId?: string) => {
};
const isOpen = ref(false);
// nothing to pick from until a provider is set up, so the picker explains that
const noModels = computed(() => allModels.value.length === 0);
const dropdownButton = ref<HTMLElement | null>(null);
const dropdownContent = ref<HTMLElement | null>(null);
const searchInputRef = ref<HTMLInputElement | null>(null);
@@ -221,11 +224,13 @@ onUnmounted(() => {
isOpen
? 'bg-[var(--color-hover)] text-[var(--text-primary)]'
: 'text-[var(--text-secondary)] @hover:text-[var(--text-primary)] @hover:bg-[var(--color-hover)]',
noModels ? 'border border-dashed border-[var(--color-border-active)] !text-[var(--text-primary)]' : '',
]">
<ModelIcon v-if="selectedModel" class="text-white" :avatar="true" variant="color"
<span v-if="noModels" class="i-mynaui-api text-4.5" aria-hidden="true"></span>
<ModelIcon v-if="selectedModel && !noModels" class="text-white" :avatar="true" variant="color"
:model-id="selectedModel.externalId" size="22" />
<span class="max-w-full truncate">
{{ selectedModel ? selectedModel.name : 'Select a model' }}
{{ noModels ? 'No models yet' : selectedModel ? selectedModel.name : 'Select a model' }}
</span>
<span class="i-mynaui-chevron-down text-3.5 transition-transform duration-200"
:class="{ 'rotate-180': isOpen }"></span>
@@ -246,9 +251,21 @@ onUnmounted(() => {
? 'hidden'
: 'visible',
}"
class="fixed z-100 w-full max-w-[420px] min-w-[280px] flex flex-col rounded-xl border border-[var(--color-border)] bg-[var(--bg-surface)] shadow-lg overflow-hidden"
:class="transformOrigin">
:class="[transformOrigin, noModels ? 'max-w-80' : 'max-w-[420px]']"
class="fixed z-100 w-full min-w-[280px] flex flex-col rounded-xl border border-[var(--color-border)] bg-[var(--bg-surface)] shadow-lg overflow-hidden">
<div v-if="noModels" class="flex flex-col gap-2.5 p-4">
<div class="font-semibold">No models yet</div>
<p class="text-sm leading-normal text-[var(--text-secondary)]">
Veridian uses your own API keys. Add a provider and its models show up here.
</p>
<button type="button" @click="openProviderSettings()"
class="self-start flex items-center h-8.5 px-3 text-sm font-semibold rounded-md bg-[var(--color-accent)] text-[var(--color-accent-text)]">
Add a provider
</button>
</div>
<template v-else>
<div class="relative">
<span
class="i-mynaui-search absolute left-3 top-1/2 -translate-y-1/2 text-4 text-[var(--text-secondary)]"></span>
@@ -305,6 +322,7 @@ onUnmounted(() => {
<span class="i-mynaui-arrow-right text-4 ml-auto"></span>
</button>
</div>
</template>
</div>
</KeepAlive>
</Transition>