change sizing slightly and add sentry
This commit is contained in:
@@ -68,6 +68,13 @@ watch(selectedModel, (newModel) => {
|
||||
|
||||
// Initialize when providers change
|
||||
watch(() => props.providers, initializeModel, { immediate: true });
|
||||
watch(() => props.agent?.defaultModelId, (newModelId) => {
|
||||
const agentModel = allModels.value.find((m) => m.id === newModelId);
|
||||
if (agentModel) {
|
||||
selectedModel.value = agentModel;
|
||||
return;
|
||||
}
|
||||
})
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (props.loading) {
|
||||
|
||||
@@ -4,13 +4,15 @@ import type schema from '#triplit/schema';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
model: Entity<typeof schema, 'models'>;
|
||||
size?: 'small' | 'normal';
|
||||
size?: 'small' | 'medium' | 'large';
|
||||
details?: boolean;
|
||||
showCost?: boolean;
|
||||
showEdit?: boolean;
|
||||
showExternalId?: boolean;
|
||||
showReleaseDate?: boolean;
|
||||
}>(), {
|
||||
size: 'normal',
|
||||
size: 'large',
|
||||
details: false,
|
||||
showCost: false,
|
||||
showExternalId: false,
|
||||
showReleaseDate: false,
|
||||
@@ -63,12 +65,15 @@ const deleteModel = async () => {
|
||||
<template>
|
||||
<div class="flex items-center justify-between w-full" v-bind="$attrs">
|
||||
<div class="flex items-center gap-2 min-w-0 flex-1">
|
||||
<ModelIcon :class="size === 'normal' ? 'rounded-lg overflow-hidden' : ''" :avatar="true" variant="color"
|
||||
:model-id="model.externalId" :size="size === 'small' ? '20' : '32'" />
|
||||
<ModelIcon :class="{
|
||||
'rounded-lg overflow-hidden': size === 'large',
|
||||
'rounded-md overflow-hidden': size === 'medium' || size === 'small',
|
||||
}" :avatar="true" variant="color" :model-id="model.externalId"
|
||||
:size="size === 'small' ? '20' : size === 'medium' ? '26' : '32'" />
|
||||
|
||||
<div class="flex flex-col gap-1 min-w-0 flex-1">
|
||||
<div class="flex items-center gap-1 min-w-0">
|
||||
<span class="text-sm font-medium text-[var(--color-text)] truncate min-w-0">
|
||||
<span class="text-[15px] font-medium text-[var(--color-text)] truncate min-w-0">
|
||||
{{ model.name }}
|
||||
</span>
|
||||
<span v-if="showExternalId"
|
||||
@@ -83,7 +88,7 @@ const deleteModel = async () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="size === 'normal'" class="flex items-center gap-1.5 flex-wrap">
|
||||
<div v-if="details" class="flex items-center gap-1.5 flex-wrap">
|
||||
<span v-if="showReleaseDate && model.releasedAt"
|
||||
class="text-xs text-[var(--color-muted)] whitespace-nowrap">
|
||||
Released on {{ model.releasedAt.toISOString().split('T')[0] }}
|
||||
|
||||
@@ -164,7 +164,7 @@ onUnmounted(() => {
|
||||
|
||||
<div v-for="provider in filteredProviders" :key="provider.id" class="mb-2">
|
||||
<div
|
||||
class="px-4 py-1.5 text-xs font-medium text-[var(--color-muted)] capitalize tracking-wider flex justify-between">
|
||||
class="px-4 py-1.5 text-[13px] font-medium text-[var(--color-muted)] capitalize tracking-wider flex justify-between">
|
||||
{{ provider.name }}
|
||||
<button @click="isOpen = true; setPage('providers', provider.id)"
|
||||
class="flex h-4.5 w-4.5 items-center justify-center hover:bg-[var(--color-highlight)] rounded transition-colors duration-200 ease-[cubic-bezier(0.5,_1,_0.89,_1)]">
|
||||
@@ -177,7 +177,7 @@ onUnmounted(() => {
|
||||
:key="model.id" @click="selectModel(model, provider); isOpen = false"
|
||||
class="text-white w-full min-h-9 px-4 py-2 flex items-center justify-between hover:bg-[var(--color-highlight-low)] transition-colors duration-150"
|
||||
:class="{ 'bg-[var(--color-highlight-low)]': selectedModel?.id === model.id }">
|
||||
<ModelInfo :model="model" size="small" />
|
||||
<ModelInfo :model="model" size="medium" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,7 +9,7 @@ const props = defineProps<{
|
||||
<template>
|
||||
<div
|
||||
class="p-3 text-white flex max-w-full items-center justify-between gap-2 group hover:bg-[var(--color-highlight-low)] transition duration-200 ease-[cubic-bezier(0.5,_1,_0.89,_1)]">
|
||||
<ModelInfo :model="model" :show-edit="true" :show-cost="true" :show-external-id="true"
|
||||
<ModelInfo :details="true" :model="model" :show-edit="true" :show-cost="true" :show-external-id="true"
|
||||
:show-release-date="true" />
|
||||
</div>
|
||||
</template>
|
||||
+85
-65
@@ -1,7 +1,7 @@
|
||||
import type { User, Session } from 'better-auth';
|
||||
import type { Result } from '~~/types/result';
|
||||
import { Ok, Err } from '~~/types/result';
|
||||
import type { BetterAuthClientOptions, InferSessionFromClient, InferUserFromClient, User } from 'better-auth/client';
|
||||
import { createAuthClient } from 'better-auth/vue';
|
||||
import { Err, Ok, type Result } from '~~/types/result';
|
||||
import { assert } from '~~/utils/assert';
|
||||
|
||||
export enum AuthError {
|
||||
NotAuthenticated = 'NOT_AUTHENTICATED',
|
||||
@@ -11,62 +11,86 @@ export enum AuthError {
|
||||
NetworkError = 'NETWORK_ERROR',
|
||||
}
|
||||
|
||||
export interface AuthState {
|
||||
user: User | null;
|
||||
session: Session | null;
|
||||
isLoading: boolean;
|
||||
}
|
||||
type sessionData = {
|
||||
session: InferSessionFromClient<BetterAuthClientOptions> | null;
|
||||
user: InferUserFromClient<BetterAuthClientOptions> | null;
|
||||
};
|
||||
|
||||
export const useAuth = () => {
|
||||
const url = useRequestURL();
|
||||
const headers = useRequestHeaders();
|
||||
const client = createAuthClient({
|
||||
baseURL: url.origin,
|
||||
fetchOptions: {
|
||||
headers
|
||||
const authClient = createAuthClient({});
|
||||
const session = useState<InferSessionFromClient<BetterAuthClientOptions> | null>('auth:session', () => null);
|
||||
const user = useState<InferUserFromClient<BetterAuthClientOptions> | null>('auth:user', () => null);
|
||||
const sessionFetching = import.meta.server ? ref(false) : useState('auth:sessionFetching', () => false);
|
||||
|
||||
let sessionPromise: Promise<Result<sessionData, AuthError>> | null = null;
|
||||
|
||||
const fetchSession = async (): Promise<Result<sessionData, AuthError>> => {
|
||||
if (sessionPromise) {
|
||||
console.log('already fetching session');
|
||||
return sessionPromise;
|
||||
}
|
||||
});
|
||||
|
||||
const state = useState<AuthState>('auth:state', () => ({
|
||||
user: null,
|
||||
session: null,
|
||||
isLoading: false,
|
||||
}));
|
||||
|
||||
const isAuthenticated = computed(() => !!state.value.session);
|
||||
const userId = computed(() => state.value.user?.id ?? null);
|
||||
|
||||
const fetchSession = async (): Promise<Result<{ session: Session | null; user: User | null }, AuthError>> => {
|
||||
state.value.isLoading = true;
|
||||
let finish: (value: Result<sessionData, AuthError>) => void;
|
||||
sessionFetching.value = true;
|
||||
sessionPromise = new Promise(async (resolve, reject) => {
|
||||
finish = resolve;
|
||||
});
|
||||
let data: {
|
||||
session: InferSessionFromClient<BetterAuthClientOptions>;
|
||||
user: InferUserFromClient<BetterAuthClientOptions>;
|
||||
} | null = null;
|
||||
|
||||
try {
|
||||
const { data } = await client.getSession();
|
||||
|
||||
if (data) {
|
||||
state.value.session = data.session;
|
||||
state.value.user = data.user;
|
||||
return Ok({ session: data.session, user: data.user });
|
||||
if (import.meta.server) {
|
||||
data =
|
||||
(
|
||||
await useFetch<{
|
||||
session: InferSessionFromClient<BetterAuthClientOptions>;
|
||||
user: InferUserFromClient<BetterAuthClientOptions>;
|
||||
}>('/api/auth/get-session')
|
||||
).data.value ?? null;
|
||||
} else {
|
||||
data = (await authClient.getSession()).data;
|
||||
}
|
||||
|
||||
state.value.session = null;
|
||||
state.value.user = null;
|
||||
return Ok({ session: null, user: null });
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch session:', error);
|
||||
return Err(AuthError.NetworkError);
|
||||
} finally {
|
||||
state.value.isLoading = false;
|
||||
sessionFetching.value = false;
|
||||
finish!(Err(AuthError.NetworkError));
|
||||
return sessionPromise;
|
||||
}
|
||||
|
||||
session.value = data?.session || null;
|
||||
user.value = data?.user || null;
|
||||
sessionFetching.value = false;
|
||||
sessionFetching.value = false;
|
||||
finish!(Ok({ session: data?.session || null, user: data?.user || null }));
|
||||
return sessionPromise;
|
||||
};
|
||||
|
||||
if (import.meta.client) {
|
||||
authClient.$store.listen('$sessionSignal', async (signal) => {
|
||||
if (!signal) return;
|
||||
await fetchSession();
|
||||
|
||||
if (!session.value) return;
|
||||
|
||||
const triplit = useTriplitClient();
|
||||
if ('updateOptions' in triplit) {
|
||||
triplit.updateOptions({
|
||||
token: session.value.token,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const signIn = async (
|
||||
email: string,
|
||||
password: string
|
||||
): Promise<Result<{ user: User; token: string }, { error: AuthError, data?: any }>> => {
|
||||
state.value.isLoading = true;
|
||||
sessionFetching.value = true;
|
||||
|
||||
try {
|
||||
const { data, error } = await client.signIn.email({
|
||||
const { data, error } = await authClient.signIn.email({
|
||||
email,
|
||||
password,
|
||||
});
|
||||
@@ -80,7 +104,7 @@ export const useAuth = () => {
|
||||
return Err({ error: AuthError.SignInFailed });
|
||||
}
|
||||
|
||||
state.value.user = data.user;
|
||||
user.value = data.user;
|
||||
|
||||
const triplit = useTriplitClient();
|
||||
if ('startSession' in triplit && data.token) {
|
||||
@@ -94,7 +118,7 @@ export const useAuth = () => {
|
||||
console.error('Sign in error:', err);
|
||||
return Err({ error: AuthError.NetworkError });
|
||||
} finally {
|
||||
state.value.isLoading = false;
|
||||
sessionFetching.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -103,10 +127,10 @@ export const useAuth = () => {
|
||||
password: string,
|
||||
name: string
|
||||
): Promise<Result<{ user: User; token: string }, { error: AuthError, data?: any }>> => {
|
||||
state.value.isLoading = true;
|
||||
sessionFetching.value = true;
|
||||
|
||||
try {
|
||||
const { data, error } = await client.signUp.email({
|
||||
const { data, error } = await authClient.signUp.email({
|
||||
email,
|
||||
password,
|
||||
name,
|
||||
@@ -121,12 +145,11 @@ export const useAuth = () => {
|
||||
return Err({ error: AuthError.SignUpFailed });
|
||||
}
|
||||
|
||||
state.value.user = data.user;
|
||||
user.value = data.user;
|
||||
|
||||
const triplit = useTriplitClient();
|
||||
if ('startSession' in triplit) {
|
||||
await triplit.startSession(data.token);
|
||||
}
|
||||
assert('startSession' in triplit);
|
||||
await triplit.startSession(data.token);
|
||||
|
||||
clearNuxtData();
|
||||
|
||||
@@ -135,28 +158,27 @@ export const useAuth = () => {
|
||||
console.error('Sign up error:', err);
|
||||
return Err({ error: AuthError.NetworkError });
|
||||
} finally {
|
||||
state.value.isLoading = false;
|
||||
sessionFetching.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const signOut = async (): Promise<Result<void, AuthError>> => {
|
||||
state.value.isLoading = true;
|
||||
sessionFetching.value = true;
|
||||
|
||||
try {
|
||||
const { error } = await client.signOut();
|
||||
const { error } = await authClient.signOut();
|
||||
|
||||
if (error) {
|
||||
console.error('Sign out failed:', error);
|
||||
return Err(AuthError.SignOutFailed);
|
||||
}
|
||||
|
||||
state.value.user = null;
|
||||
state.value.session = null;
|
||||
user.value = null;
|
||||
session.value = null;
|
||||
|
||||
const triplit = useTriplitClient();
|
||||
if ('disconnect' in triplit) {
|
||||
triplit.disconnect();
|
||||
}
|
||||
assert('disconnect' in triplit);
|
||||
triplit.disconnect();
|
||||
|
||||
clearNuxtData();
|
||||
|
||||
@@ -165,20 +187,18 @@ export const useAuth = () => {
|
||||
console.error('Sign out error:', err);
|
||||
return Err(AuthError.NetworkError);
|
||||
} finally {
|
||||
state.value.isLoading = false;
|
||||
sessionFetching.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
client,
|
||||
user: computed(() => state.value.user),
|
||||
session: computed(() => state.value.session),
|
||||
isLoading: computed(() => state.value.isLoading),
|
||||
isAuthenticated,
|
||||
userId,
|
||||
fetchSession,
|
||||
client: authClient,
|
||||
session,
|
||||
user,
|
||||
loggedIn: computed(() => !!session.value),
|
||||
signIn,
|
||||
signUp,
|
||||
signOut,
|
||||
fetchSession,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { assert } from "~~/utils/assert";
|
||||
|
||||
export default defineNuxtPlugin({
|
||||
name: 'better-auth-triplit',
|
||||
enforce: 'pre',
|
||||
@@ -19,9 +21,8 @@ export default defineNuxtPlugin({
|
||||
return;
|
||||
}
|
||||
|
||||
if ('startSession' in triplit) {
|
||||
await triplit.startSession(session.value.token);
|
||||
}
|
||||
assert('startSession' in triplit)
|
||||
await triplit.startSession(session.value.token);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||
unified()
|
||||
.use(remarkParse)
|
||||
.use(remarkGfm)
|
||||
.use(remarkMath, { singleDollarTextMath: false })
|
||||
.use(remarkMath)
|
||||
.use(remarkRehype, { allowDangerousHtml: true })
|
||||
.use(rehypeKatex);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user