feat: replace settings with a full page
This commit is contained in:
@@ -1,6 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
import { DialogType } from '~/composables/useDialog';
|
||||
|
||||
const { user, signOut } = useAuth();
|
||||
|
||||
// to prevent the user details from going blank for a
|
||||
@@ -16,10 +14,14 @@ watch(user, () => {
|
||||
cachedUser.value = user.value;
|
||||
})
|
||||
|
||||
const { openDialog } = useDialog();
|
||||
|
||||
const { isHovered } = useSidenavContext();
|
||||
|
||||
const openSettings = async () => {
|
||||
dropdownOpen.value = false;
|
||||
|
||||
await navigateTo('/settings');
|
||||
};
|
||||
|
||||
const handleLogout = async () => {
|
||||
await signOut();
|
||||
|
||||
@@ -31,11 +33,7 @@ const handleLogout = async () => {
|
||||
<header class="flex items-center justify-between overflow-hidden">
|
||||
<button @click="dropdownOpen = !dropdownOpen"
|
||||
class="flex gap-1 pr-1 items-center @hover:bg-[var(--color-hover)] focus-visible:bg-[var(--color-hover)] rounded-lg transition-colors duration-200 ease-[cubic-bezier(0.5,_1,_0.89,_1)]">
|
||||
<div
|
||||
:class="['w-9 h-9 md:w-7 md:h-7 flex-shrink-0 rounded-lg overflow-hidden bg-[var(--bg-surface)] flex items-center justify-center', user?.image ? '' : 'border border-[var(--color-border)]']">
|
||||
<img v-if="cachedUser?.image" :src="cachedUser.image" class="w-full h-full object-cover" />
|
||||
<span v-else class="w-5.5 h-5.5 md:h-4 md:w-4 i-mynaui-user text-[var(--text-secondary)]"></span>
|
||||
</div>
|
||||
<UserAvatar :user="cachedUser" class="w-9 h-9 md:w-7 md:h-7 rounded-lg md:text-sm" />
|
||||
<span
|
||||
class="md:text-sm font-medium text-ellipsis overflow-hidden text-[var(--text-primary)] whitespace-nowrap">
|
||||
{{ cachedUser?.name }}
|
||||
@@ -55,7 +53,7 @@ const handleLogout = async () => {
|
||||
leave-from-class="opacity-100 scale-100" leave-to-class="opacity-0 scale-95">
|
||||
<div v-if="dropdownOpen" v-click-outside="() => dropdownOpen = false"
|
||||
class="w-full top-full text-sm mt-1 absolute z-30 bg-[var(--bg-surface)] border border-[var(--color-border)] rounded-xl p-1.5 flex flex-col gap-2">
|
||||
<button @click="dropdownOpen = false; openDialog(DialogType.Settings)"
|
||||
<button @click="openSettings"
|
||||
class="text-left px-3 py-1.5 items-center gap-1 @hover:bg-[var(--color-hover)] rounded-lg transition-colors duration-200 ease-[cubic-bezier(0.5,_1,_0.89,_1)]">
|
||||
<span class="i-mynaui-cog-four text-4.5"></span>
|
||||
<span>Settings</span>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
const returnTo = useState('settings:returnTo', () => '/');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header class="flex items-center rounded-lg overflow-hidden flex-1 min-w-0">
|
||||
<NuxtLink :to="returnTo" aria-label="Back"
|
||||
class="flex items-center gap-1 p-1.5 pr-2.5 @hover:bg-[var(--color-hover)] focus-visible:bg-[var(--color-hover)] rounded-lg decoration-none transition-inherit">
|
||||
<span class="i-mynaui-chevron-left text-5.5 md:text-4.5 text-[var(--text-secondary)]"></span>
|
||||
<span
|
||||
class="md:text-sm font-medium text-ellipsis overflow-hidden text-[var(--text-primary)] whitespace-nowrap">
|
||||
Settings
|
||||
</span>
|
||||
</NuxtLink>
|
||||
</header>
|
||||
</template>
|
||||
@@ -0,0 +1,63 @@
|
||||
<script setup lang="ts">
|
||||
import { providerIcons } from '~/utils/model-mapping';
|
||||
|
||||
const route = useRoute();
|
||||
const { providers } = await useModels();
|
||||
|
||||
const groups = [
|
||||
{
|
||||
label: 'Account',
|
||||
items: [
|
||||
{ to: '/settings/profile', name: 'Profile', icon: 'i-mynaui-user' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'App',
|
||||
items: [
|
||||
{ to: '/settings/general', name: 'General', icon: 'i-mynaui-cog-four' },
|
||||
{ to: '/settings/appearance', name: 'Appearance', icon: 'i-tabler-palette' },
|
||||
{ to: '/settings/providers', name: 'AI Providers', icon: 'i-mynaui-api' },
|
||||
{ to: '/settings/assistants', name: 'System Assistants', icon: 'i-mynaui-sparkles' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const inProviders = computed(() => route.path.startsWith('/settings/providers'));
|
||||
|
||||
// enabled providers first, same order the provider list uses
|
||||
const sortedProviders = computed(() => [
|
||||
...providers.value.filter(p => p.enabled),
|
||||
...providers.value.filter(p => !p.enabled),
|
||||
]);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav aria-label="Settings" class="flex flex-col gap-1 overflow-auto">
|
||||
<template v-for="group in groups" :key="group.label">
|
||||
<div class="px-2 pt-3 pb-0.5 text-sm md:text-xs font-medium uppercase tracking-wider">
|
||||
{{ group.label }}
|
||||
</div>
|
||||
|
||||
<template v-for="item in group.items" :key="item.to">
|
||||
<SidenavItem :to="item.to" :name="item.name" :icon="item.icon" :active="route.path === item.to" />
|
||||
|
||||
<!-- the providers are listed under their section while it is open -->
|
||||
<div v-if="item.to === '/settings/providers' && inProviders" class="flex flex-col gap-1 pl-4">
|
||||
<NuxtLink v-for="p in sortedProviders" :key="p.id" :to="`/settings/providers/${p.id}`" :class="[
|
||||
'case-capital decoration-none flex items-center gap-2 shrink-0 px-2 h-9 rounded-lg transition-colors',
|
||||
route.path === `/settings/providers/${p.id}`
|
||||
? 'text-[var(--text-primary)] bg-[var(--color-hover)]'
|
||||
: 'text-[var(--text-secondary)] @hover:bg-[var(--color-hover)] focus-visible:bg-[var(--color-hover)]',
|
||||
p.enabled ? '' : 'opacity-50',
|
||||
]">
|
||||
<component v-if="providerIcons[p.type]" :color="true" :is="providerIcons[p.type]"
|
||||
class="h-6 w-6 md:w-4 md:h-4 flex-shrink-0 text-[var(--text-primary)]" />
|
||||
<span class="md:text-sm font-medium overflow-hidden text-ellipsis whitespace-nowrap">
|
||||
{{ p.name }}
|
||||
</span>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</nav>
|
||||
</template>
|
||||
@@ -1,6 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
import { DialogType } from '~/composables/useDialog';
|
||||
|
||||
const { close: closeSidebar, open, sidebarWidth, resize, saveWidth } = useSidebar();
|
||||
const route = useRoute();
|
||||
|
||||
@@ -37,8 +35,6 @@ const onFocusOut = (e: FocusEvent) => {
|
||||
}
|
||||
};
|
||||
|
||||
const { openDialog } = useDialog();
|
||||
|
||||
const onResizeStart = (event: MouseEvent) => {
|
||||
isResizing.value = true;
|
||||
startX.value = event.clientX;
|
||||
@@ -97,8 +93,18 @@ const navKind = computed(() => {
|
||||
if (route.path === '/') return 'home';
|
||||
if (route.path.startsWith('/agent/')) return 'agent';
|
||||
if (route.path === '/resources') return 'resources';
|
||||
if (route.path.startsWith('/settings')) return 'settings';
|
||||
return null;
|
||||
});
|
||||
|
||||
// remember where the user was so the settings back button can return there
|
||||
const settingsReturnTo = useState('settings:returnTo', () => '/');
|
||||
|
||||
watch(() => route.fullPath, (path) => {
|
||||
if (!path.startsWith('/settings')) {
|
||||
settingsReturnTo.value = path;
|
||||
}
|
||||
}, { immediate: true });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -126,6 +132,7 @@ const navKind = computed(() => {
|
||||
<SidenavHeader v-if="navKind === 'home'" />
|
||||
<SidenavHeaderResources v-else-if="navKind === 'resources'" />
|
||||
<SidenavHeaderAgent v-else-if="navKind === 'agent'" />
|
||||
<SidenavHeaderSettings v-else-if="navKind === 'settings'" />
|
||||
|
||||
<div class="flex items-center justify-end text-[var(--text-secondary)] gap-0.5">
|
||||
<div :class="[
|
||||
@@ -158,15 +165,18 @@ const navKind = computed(() => {
|
||||
<SidenavNavHome v-if="navKind === 'home'" />
|
||||
<SidenavNavAgent v-else-if="navKind === 'agent'" />
|
||||
<SidenavNavResources v-else-if="navKind === 'resources'" />
|
||||
<SidenavNavSettings v-else-if="navKind === 'settings'" />
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between pt-2 z-25 bg-[var(--bg-base)] pb-2">
|
||||
<div class="flex">
|
||||
<Tooltip :hotkey="['ctrl', ',']">
|
||||
<button @click="openDialog(DialogType.Settings)"
|
||||
class="flex items-center justify-center h-9 w-9 md:h-7 md:w-7 cursor-pointer @hover:bg-[var(--color-hover)] focus-visible:bg-[var(--color-hover)] rounded-lg transition-colors text-[var(--text-secondary)] active:text-[var(--text-primary)]">
|
||||
<NuxtLink to="/settings" aria-label="Settings" :class="[
|
||||
'flex items-center justify-center h-9 w-9 md:h-7 md:w-7 cursor-pointer @hover:bg-[var(--color-hover)] focus-visible:bg-[var(--color-hover)] rounded-lg transition-colors active:text-[var(--text-primary)]',
|
||||
navKind === 'settings' ? 'text-[var(--text-primary)] bg-[var(--color-hover)]' : 'text-[var(--text-secondary)]',
|
||||
]">
|
||||
<span class="i-mynaui-cog-four text-6 md:text-5"></span>
|
||||
</button>
|
||||
</NuxtLink>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user