mirror of
https://github.com/versia-pub/frontend.git
synced 2025-12-06 08:28:20 +01:00
Compare commits
4 commits
cefdd47204
...
53b71afdd5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53b71afdd5 | ||
|
|
97733c18ee | ||
|
|
5ef26f03a4 | ||
|
|
e1e4709d19 |
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.0.5/schema.json",
|
||||
"$schema": "https://biomejs.dev/schemas/2.1.1/schema.json",
|
||||
"assist": { "actions": { "source": { "organizeImports": "on" } } },
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
|
|
|
|||
41
components/instance/small-card.vue
Normal file
41
components/instance/small-card.vue
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<template>
|
||||
<Card class="grid grid-cols-[auto_1fr] gap-2">
|
||||
<Avatar :src="instance.thumbnail?.url ??
|
||||
'https://cdn.versia.pub/branding/icon.svg'
|
||||
" :name="instance.title" />
|
||||
<div class="grid text-sm leading-tight *:line-clamp-1">
|
||||
<span class="truncate font-semibold">
|
||||
{{
|
||||
instance.domain
|
||||
}}
|
||||
</span>
|
||||
<span class="line-clamp-3 text-xs">
|
||||
{{
|
||||
instance.versia_version || instance.version
|
||||
}}
|
||||
</span>
|
||||
|
||||
</div>
|
||||
<h1 class="line-clamp-1 text-sm font-semibold col-span-2">
|
||||
{{
|
||||
instance.title
|
||||
}}
|
||||
</h1>
|
||||
<p class="line-clamp-5 text-xs col-span-2">
|
||||
{{
|
||||
instance.description
|
||||
}}
|
||||
</p>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { Instance } from "@versia/client/schemas";
|
||||
import type z from "zod";
|
||||
import Avatar from "../profiles/avatar.vue";
|
||||
import { Card } from "../ui/card";
|
||||
|
||||
const { instance } = defineProps<{
|
||||
instance: z.infer<typeof Instance>;
|
||||
}>();
|
||||
</script>
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
<template>
|
||||
<span :class="cn('text-primary group', $props.class)">
|
||||
<span class="group-hover:hidden">
|
||||
<slot />
|
||||
</span>
|
||||
<span class="hidden group-hover:inline">
|
||||
<span @click="copyText"
|
||||
class="select-none cursor-pointer space-x-1">
|
||||
<Clipboard class="size-4 -translate-y-0.5 inline" />
|
||||
{{ m.clean_yummy_owl_reside() }}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script lang="tsx" setup>
|
||||
import { Check, Clipboard } from "lucide-vue-next";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { toast } from "vue-sonner";
|
||||
import { cn } from "@/lib/utils";
|
||||
import * as m from "~/paraglide/messages.js";
|
||||
|
||||
const { text } = defineProps<{
|
||||
text: string;
|
||||
class?: HTMLAttributes["class"];
|
||||
}>();
|
||||
|
||||
const { copy } = useClipboard();
|
||||
const copyText = () => {
|
||||
copy(text);
|
||||
toast.success("Copied to clipboard");
|
||||
};
|
||||
</script>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="rounded flex flex-row items-center gap-3">
|
||||
<div class="rounded grid grid-cols-[auto_1fr_auto] items-center gap-3">
|
||||
<HoverCard v-model:open="popupOpen" @update:open="() => {
|
||||
if (!preferences.popup_avatar_hover) {
|
||||
popupOpen = false;
|
||||
|
|
@ -16,24 +16,18 @@
|
|||
<SmallCard :account="author" />
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
<div
|
||||
:class="cn('flex flex-col gap-0.5 justify-center flex-1 text-left leading-tight', smallLayout && 'text-sm')">
|
||||
<span class="truncate font-semibold" v-render-emojis="author.emojis">{{
|
||||
<Col
|
||||
:class="smallLayout && 'text-sm'">
|
||||
<Text class="font-semibold" v-render-emojis="author.emojis">{{
|
||||
author.display_name
|
||||
}}</span>
|
||||
<span class="truncate text-sm tracking-tight">
|
||||
<span>
|
||||
<span
|
||||
class="font-semibold bg-gradient-to-tr from-pink-700 dark:from-indigo-400 via-purple-700 dark:via-purple-400 to-indigo-700 dark:to-indigo-400 text-transparent bg-clip-text">
|
||||
@{{ username }}
|
||||
</span>
|
||||
<span class="text-muted-foreground">{{ instance && "@" }}{{ instance }}</span>
|
||||
</span>
|
||||
}}</Text>
|
||||
<div class="-mt-1">
|
||||
<Address as="span" :username="username" :domain="instance" />
|
||||
·
|
||||
<span class="text-muted-foreground ml-auto tracking-normal" :title="fullTime">{{ timeAgo }}</span>
|
||||
</span>
|
||||
<Text as="span" muted class="ml-auto tracking-normal" :title="fullTime">{{ timeAgo }}</Text>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1 h-full justify-center items-end" v-if="!smallLayout">
|
||||
</Col>
|
||||
<div v-if="!smallLayout">
|
||||
<NuxtLink :href="noteUrlAsPath" class="text-xs text-muted-foreground"
|
||||
:title="visibilities[visibility].text">
|
||||
<component :is="visibilities[visibility].icon" class="size-4" />
|
||||
|
|
@ -52,8 +46,12 @@ import { AtSign, Globe, Lock, LockOpen } from "lucide-vue-next";
|
|||
import type { z } from "zod";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { getLocale } from "~/paraglide/runtime";
|
||||
import Address from "../profiles/address.vue";
|
||||
import Avatar from "../profiles/avatar.vue";
|
||||
import SmallCard from "../profiles/small-card.vue";
|
||||
import Col from "../typography/layout/col.vue";
|
||||
import Row from "../typography/layout/row.vue";
|
||||
import Text from "../typography/text.vue";
|
||||
import {
|
||||
HoverCard,
|
||||
HoverCardContent,
|
||||
|
|
|
|||
|
|
@ -8,13 +8,7 @@
|
|||
follower.display_name
|
||||
}}</span>
|
||||
<span class="truncate tracking-tight">
|
||||
<CopyableText :text="follower.acct">
|
||||
<span
|
||||
class="font-semibold bg-gradient-to-tr from-pink-700 dark:from-indigo-400 via-purple-700 dark:via-purple-400 to-indigo-700 dark:to-indigo-400 text-transparent bg-clip-text">
|
||||
@{{ username }}
|
||||
</span>
|
||||
<span class="text-muted-foreground">{{ instance && "@" }}{{ instance }}</span>
|
||||
</CopyableText>
|
||||
<Address :username="username" :domain="domain" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -39,9 +33,9 @@ import type { Account } from "@versia/client/schemas";
|
|||
import { Check, Loader, X } from "lucide-vue-next";
|
||||
import { toast } from "vue-sonner";
|
||||
import type { z } from "zod";
|
||||
import CopyableText from "~/components/notes/copyable-text.vue";
|
||||
import { Button } from "~/components/ui/button";
|
||||
import * as m from "~/paraglide/messages.js";
|
||||
import Address from "../profiles/address.vue";
|
||||
import Avatar from "../profiles/avatar.vue";
|
||||
|
||||
const { follower } = defineProps<{
|
||||
|
|
@ -50,7 +44,7 @@ const { follower } = defineProps<{
|
|||
|
||||
const loading = ref(true);
|
||||
const followerUrl = `/@${follower.acct}`;
|
||||
const [username, instance] = follower.acct.split("@");
|
||||
const [username, domain] = follower.acct.split("@");
|
||||
const { relationship } = useRelationship(client, follower.id);
|
||||
|
||||
// TODO: Add "followed" notification
|
||||
|
|
|
|||
15
components/profiles/address.vue
Normal file
15
components/profiles/address.vue
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<template>
|
||||
<Text class="font-semibold text-sm tracking-tight">
|
||||
<span class="text-accent-foreground">@{{ username }}</span>
|
||||
<span v-if="domain" class="text-muted-foreground">@{{ domain }}</span>
|
||||
</Text>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Text from "../typography/text.vue";
|
||||
|
||||
const { username, domain } = defineProps<{
|
||||
username: string;
|
||||
domain?: string;
|
||||
}>();
|
||||
</script>
|
||||
|
|
@ -1,31 +1,27 @@
|
|||
<template>
|
||||
<Tooltip>
|
||||
<TooltipTrigger :as-child="true">
|
||||
<Badge variant="outline" class="gap-1">
|
||||
<svg viewBox="0 0 22 22" v-if="verified" aria-hidden="true" class="size-4 fill-secondary-foreground">
|
||||
<g>
|
||||
<path
|
||||
d="M20.396 11c-.018-.646-.215-1.275-.57-1.816-.354-.54-.852-.972-1.438-1.246.223-.607.27-1.264.14-1.897-.131-.634-.437-1.218-.882-1.687-.47-.445-1.053-.75-1.687-.882-.633-.13-1.29-.083-1.897.14-.273-.587-.704-1.086-1.245-1.44S11.647 1.62 11 1.604c-.646.017-1.273.213-1.813.568s-.969.854-1.24 1.44c-.608-.223-1.267-.272-1.902-.14-.635.13-1.22.436-1.69.882-.445.47-.749 1.055-.878 1.688-.13.633-.08 1.29.144 1.896-.587.274-1.087.705-1.443 1.245-.356.54-.555 1.17-.574 1.817.02.647.218 1.276.574 1.817.356.54.856.972 1.443 1.245-.224.606-.274 1.263-.144 1.896.13.634.433 1.218.877 1.688.47.443 1.054.747 1.687.878.633.132 1.29.084 1.897-.136.274.586.705 1.084 1.246 1.439.54.354 1.17.551 1.816.569.647-.016 1.276-.213 1.817-.567s.972-.854 1.245-1.44c.604.239 1.266.296 1.903.164.636-.132 1.22-.447 1.68-.907.46-.46.776-1.044.908-1.681s.075-1.299-.165-1.903c.586-.274 1.084-.705 1.439-1.246.354-.54.551-1.17.569-1.816zM9.662 14.85l-3.429-3.428 1.293-1.302 2.072 2.072 4.4-4.794 1.347 1.246z">
|
||||
</path>
|
||||
</g>
|
||||
</svg>
|
||||
<Badge variant="default" class="gap-1">
|
||||
<BadgeCheck v-if="verified" />
|
||||
<img v-else-if="icon" :src="icon" alt="" class="size-4 rounded" />
|
||||
{{ name }}
|
||||
</Badge>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent v-if="description">
|
||||
<p>{{ description }}</p>
|
||||
<Text>{{ description }}</Text>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { BadgeCheck } from "lucide-vue-next";
|
||||
import { Badge } from "~/components/ui/badge";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from "~/components/ui/tooltip";
|
||||
import Text from "../typography/text.vue";
|
||||
|
||||
defineProps<{
|
||||
name: string;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<template>
|
||||
<div
|
||||
class="flex flex-row flex-wrap gap-2 -mx-2"
|
||||
<Row class="gap-2" wrap
|
||||
v-if="isDeveloper || account.bot || roles.length > 0"
|
||||
>
|
||||
<ProfileBadge
|
||||
|
|
@ -21,13 +20,14 @@
|
|||
:description="role.description"
|
||||
:icon="role.icon"
|
||||
/>
|
||||
</div>
|
||||
</Row>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { Account } from "@versia/client/schemas";
|
||||
import type { z } from "zod";
|
||||
import * as m from "~/paraglide/messages.js";
|
||||
import Row from "../typography/layout/row.vue";
|
||||
import ProfileBadge from "./profile-badge.vue";
|
||||
|
||||
const { account } = defineProps<{
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
<template>
|
||||
<div :class="['prose prose-sm block relative dark:prose-invert duration-200 !max-w-full break-words prose-a:no-underline prose-a:hover:underline', $style.content]" v-html="content" v-render-emojis="emojis">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { CustomEmoji } from "@versia/client/schemas";
|
||||
import type { z } from "zod";
|
||||
|
||||
const { content } = defineProps<{
|
||||
content: string;
|
||||
emojis: z.infer<typeof CustomEmoji>[];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style module>
|
||||
@import url("~/styles/content.css");
|
||||
</style>
|
||||
|
|
@ -1,15 +1,18 @@
|
|||
<template>
|
||||
<div class="flex flex-col gap-y-4">
|
||||
<div v-for="field in fields" :key="field.name" class="flex flex-col gap-1 break-words">
|
||||
<h3 class="font-semibold text-sm" v-render-emojis="emojis">{{ field.name }}</h3>
|
||||
<div v-html="field.value" class="prose prose-sm prose-zinc dark:prose-invert" v-render-emojis="emojis"></div>
|
||||
</div>
|
||||
</div>
|
||||
<Col class="gap-y-4">
|
||||
<Col v-for="field in fields" :key="field.name" class="gap-1 break-words">
|
||||
<HeadingSmall v-render-emojis="emojis">{{ field.name }}</HeadingSmall>
|
||||
<Html v-html="field.value" v-render-emojis="emojis" />
|
||||
</Col>
|
||||
</Col>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { CustomEmoji, Field } from "@versia/client/schemas";
|
||||
import type { z } from "zod";
|
||||
import HeadingSmall from "~/components/typography/headings/small.vue";
|
||||
import Html from "../typography/html.vue";
|
||||
import Col from "../typography/layout/col.vue";
|
||||
|
||||
defineProps<{
|
||||
fields: z.infer<typeof Field>[];
|
||||
|
|
|
|||
80
components/profiles/profile-relationship-actions.vue
Normal file
80
components/profiles/profile-relationship-actions.vue
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
<template>
|
||||
<Button variant="secondary" :disabled="isLoading || relationship?.requested" v-if="!isMe && identity"
|
||||
@click="relationship?.following ? unfollow() : follow()">
|
||||
<Loader v-if="isLoading" class="animate-spin" />
|
||||
<span v-else>
|
||||
{{
|
||||
relationship?.following
|
||||
? m.brief_upper_otter_cuddle()
|
||||
: relationship?.requested
|
||||
? m.weak_bright_larva_grasp()
|
||||
: m.lazy_major_loris_grasp()
|
||||
}}
|
||||
</span>
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { Account } from "@versia/client/schemas";
|
||||
import { Loader } from "lucide-vue-next";
|
||||
import { toast } from "vue-sonner";
|
||||
import type { z } from "zod";
|
||||
import { Button } from "~/components/ui/button";
|
||||
import * as m from "~/paraglide/messages.js";
|
||||
import { confirmModalService } from "../modals/composable";
|
||||
|
||||
const { account } = defineProps<{
|
||||
account: z.infer<typeof Account>;
|
||||
}>();
|
||||
|
||||
const { relationship, isLoading } = useRelationship(client, account.id);
|
||||
const isMe = identity.value?.account.id === account.id;
|
||||
|
||||
const follow = async () => {
|
||||
if (preferences.confirm_actions.value.includes("follow")) {
|
||||
const confirmation = await confirmModalService.confirm({
|
||||
title: m.many_fair_capybara_imagine(),
|
||||
message: m.mellow_yummy_jannes_cuddle({
|
||||
acct: `@${account.acct}`,
|
||||
}),
|
||||
confirmText: m.cuddly_even_tern_loop(),
|
||||
cancelText: m.soft_bold_ant_attend(),
|
||||
});
|
||||
|
||||
if (!confirmation.confirmed) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const id = toast.loading(m.quick_basic_peacock_bubble());
|
||||
const { data } = await client.value.followAccount(account.id);
|
||||
toast.dismiss(id);
|
||||
|
||||
relationship.value = data;
|
||||
toast.success(m.awake_quick_cuckoo_smile());
|
||||
};
|
||||
|
||||
const unfollow = async () => {
|
||||
if (preferences.confirm_actions.value.includes("follow")) {
|
||||
const confirmation = await confirmModalService.confirm({
|
||||
title: m.funny_aloof_swan_loop(),
|
||||
message: m.white_best_dolphin_catch({
|
||||
acct: `@${account.acct}`,
|
||||
}),
|
||||
confirmText: m.cute_polite_oryx_blend(),
|
||||
cancelText: m.soft_bold_ant_attend(),
|
||||
});
|
||||
|
||||
if (!confirmation.confirmed) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const id = toast.loading(m.big_safe_guppy_mix());
|
||||
const { data } = await client.value.unfollowAccount(account.id);
|
||||
toast.dismiss(id);
|
||||
|
||||
relationship.value = data;
|
||||
toast.success(m.misty_level_stingray_expand());
|
||||
};
|
||||
</script>
|
||||
|
|
@ -1,41 +1,30 @@
|
|||
<template>
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex flex-row flex-wrap gap-2 *:flex *:items-center *:gap-1 *:text-muted-foreground">
|
||||
<div>
|
||||
<CalendarDays class="size-4" />
|
||||
{{ m.gross_fancy_platypus_seek() }} <span class="text-primary font-semibold">{{ formattedCreationDate }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-row flex-wrap gap-2 *:flex *:items-center *:gap-1 *:text-muted-foreground">
|
||||
<div>
|
||||
<span class="text-primary font-semibold">{{ noteCount }}</span> {{ m.real_gray_stork_seek() }}
|
||||
</div>
|
||||
·
|
||||
<div>
|
||||
<span class="text-primary font-semibold">{{ followerCount }}</span> {{ m.teal_helpful_parakeet_hike() }}
|
||||
</div>
|
||||
·
|
||||
<div>
|
||||
<span class="text-primary font-semibold">{{ followingCount }}</span> {{ m.aloof_royal_samuel_startle() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Row class="gap-2 w-full justify-around">
|
||||
<Col centered>
|
||||
<Bold>{{ noteCount }}</Bold>
|
||||
<Small muted>{{ m.real_gray_stork_seek() }}</Small>
|
||||
</Col>
|
||||
<Col centered>
|
||||
<Bold>{{ followerCount }}</Bold>
|
||||
<Small muted>{{ m.teal_helpful_parakeet_hike() }}</Small>
|
||||
</Col>
|
||||
<Col centered>
|
||||
<Bold>{{ followingCount }}</Bold>
|
||||
<Small muted>{{ m.aloof_royal_samuel_startle() }}</Small>
|
||||
</Col>
|
||||
</Row>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { CalendarDays } from "lucide-vue-next";
|
||||
import * as m from "~/paraglide/messages.js";
|
||||
import { getLocale } from "~/paraglide/runtime";
|
||||
import Bold from "../typography/bold.vue";
|
||||
import Col from "../typography/layout/col.vue";
|
||||
import Row from "../typography/layout/row.vue";
|
||||
import Small from "../typography/small.vue";
|
||||
|
||||
const { creationDate } = defineProps<{
|
||||
creationDate: Date;
|
||||
const { noteCount, followerCount, followingCount } = defineProps<{
|
||||
noteCount: number;
|
||||
followerCount: number;
|
||||
followingCount: number;
|
||||
}>();
|
||||
|
||||
const formattedCreationDate = new Intl.DateTimeFormat(getLocale(), {
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
}).format(creationDate);
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,141 +1,65 @@
|
|||
<template>
|
||||
<Card class="*:w-full">
|
||||
<ProfileHeader
|
||||
:header="account.header"
|
||||
:avatar="account.avatar"
|
||||
:display-name="account.display_name"
|
||||
/>
|
||||
<CardContent>
|
||||
<div class="flex flex-row justify-end gap-2">
|
||||
<Button
|
||||
variant="secondary"
|
||||
:disabled="isLoading || relationship?.requested"
|
||||
v-if="!isMe && identity"
|
||||
@click="relationship?.following ? unfollow() : follow()"
|
||||
>
|
||||
<Loader v-if="isLoading" class="animate-spin" />
|
||||
<span v-else>
|
||||
{{
|
||||
relationship?.following
|
||||
? m.brief_upper_otter_cuddle()
|
||||
: relationship?.requested
|
||||
? m.weak_bright_larva_grasp()
|
||||
: m.lazy_major_loris_grasp()
|
||||
}}
|
||||
</span>
|
||||
</Button>
|
||||
<Card class="gap-4">
|
||||
<ProfileHeader :header="account.header" :avatar="account.avatar" :display-name="account.display_name" />
|
||||
<Row class="justify-end gap-2">
|
||||
<ProfileRelationshipActions :account="account" />
|
||||
<ProfileActions :account="account">
|
||||
<Button variant="secondary" size="icon">
|
||||
<Ellipsis />
|
||||
</Button>
|
||||
</ProfileActions>
|
||||
</div>
|
||||
<div class="flex flex-col -mt-1 gap-1 justify-center">
|
||||
<CardTitle class="" v-render-emojis="account.emojis">
|
||||
</Row>
|
||||
<Col class="justify-center">
|
||||
<Text class="font-bold" v-render-emojis="account.emojis">
|
||||
{{ account.display_name }}
|
||||
</CardTitle>
|
||||
<CopyableText :text="account.acct">
|
||||
<span
|
||||
class="font-semibold bg-gradient-to-tr from-pink-700 dark:from-indigo-400 via-purple-700 dark:via-purple-400 to-indigo-700 dark:to-indigo-400 text-transparent bg-clip-text"
|
||||
>
|
||||
@{{ username }}
|
||||
</span>
|
||||
<span class="text-muted-foreground"
|
||||
>{{ instance && "@" }}{{ instance }}</span
|
||||
>
|
||||
</CopyableText>
|
||||
</div>
|
||||
<ProfileBadges :account="account" class="my-2" />
|
||||
<ProfileContent :content="account.note" :emojis="account.emojis" />
|
||||
</CardContent>
|
||||
<CardFooter class="flex-col *:w-full gap-4">
|
||||
<ProfileStats
|
||||
:creation-date="new Date(account.created_at || 0)"
|
||||
:follower-count="account.followers_count"
|
||||
:following-count="account.following_count"
|
||||
:note-count="account.statuses_count"
|
||||
/>
|
||||
</Text>
|
||||
<Address :username="username" :domain="domain" />
|
||||
</Col>
|
||||
<ProfileBadges :account="account" />
|
||||
<Html v-html="account.note" v-render-emojis="account.emojis" />
|
||||
<Separator />
|
||||
<ProfileFields v-if="account.fields.length > 0" :fields="account.fields" :emojis="account.emojis" />
|
||||
<Separator v-if="account.fields.length > 0" />
|
||||
<ProfileFields
|
||||
v-if="account.fields.length > 0"
|
||||
:fields="account.fields"
|
||||
:emojis="account.emojis"
|
||||
/>
|
||||
</CardFooter>
|
||||
<Row>
|
||||
<HeadingSmall class="flex items-center gap-1">
|
||||
<CalendarDays class="size-4" /> {{ formattedCreationDate }}
|
||||
</HeadingSmall>
|
||||
</Row>
|
||||
<Separator />
|
||||
<ProfileStats :follower-count="account.followers_count" :following-count="account.following_count"
|
||||
:note-count="account.statuses_count" />
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { Account } from "@versia/client/schemas";
|
||||
import { Ellipsis, Loader } from "lucide-vue-next";
|
||||
import { toast } from "vue-sonner";
|
||||
import { CalendarDays, Ellipsis } from "lucide-vue-next";
|
||||
import type { z } from "zod";
|
||||
import CopyableText from "~/components/notes/copyable-text.vue";
|
||||
import { Button } from "~/components/ui/button";
|
||||
import { Card, CardContent, CardFooter, CardTitle } from "~/components/ui/card";
|
||||
import { Card } from "~/components/ui/card";
|
||||
import { Separator } from "~/components/ui/separator";
|
||||
import * as m from "~/paraglide/messages.js";
|
||||
import { confirmModalService } from "../modals/composable";
|
||||
import { getLocale } from "~/paraglide/runtime";
|
||||
import HeadingSmall from "../typography/headings/small.vue";
|
||||
import Html from "../typography/html.vue";
|
||||
import Col from "../typography/layout/col.vue";
|
||||
import Row from "../typography/layout/row.vue";
|
||||
import Text from "../typography/text.vue";
|
||||
import Address from "./address.vue";
|
||||
import ProfileActions from "./profile-actions.vue";
|
||||
import ProfileBadges from "./profile-badges.vue";
|
||||
import ProfileContent from "./profile-content.vue";
|
||||
import ProfileFields from "./profile-fields.vue";
|
||||
import ProfileHeader from "./profile-header.vue";
|
||||
import ProfileRelationshipActions from "./profile-relationship-actions.vue";
|
||||
import ProfileStats from "./profile-stats.vue";
|
||||
|
||||
const { account } = defineProps<{
|
||||
account: z.infer<typeof Account>;
|
||||
}>();
|
||||
|
||||
const { relationship, isLoading } = useRelationship(client, account.id);
|
||||
const isMe = identity.value?.account.id === account.id;
|
||||
const [username, instance] = account.acct.split("@");
|
||||
const [username, domain] = account.acct.split("@");
|
||||
|
||||
const follow = async () => {
|
||||
if (preferences.confirm_actions.value.includes("follow")) {
|
||||
const confirmation = await confirmModalService.confirm({
|
||||
title: m.many_fair_capybara_imagine(),
|
||||
message: m.mellow_yummy_jannes_cuddle({
|
||||
acct: `@${account.acct}`,
|
||||
}),
|
||||
confirmText: m.cuddly_even_tern_loop(),
|
||||
cancelText: m.soft_bold_ant_attend(),
|
||||
});
|
||||
|
||||
if (!confirmation.confirmed) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const id = toast.loading(m.quick_basic_peacock_bubble());
|
||||
const { data } = await client.value.followAccount(account.id);
|
||||
toast.dismiss(id);
|
||||
|
||||
relationship.value = data;
|
||||
toast.success(m.awake_quick_cuckoo_smile());
|
||||
};
|
||||
|
||||
const unfollow = async () => {
|
||||
if (preferences.confirm_actions.value.includes("follow")) {
|
||||
const confirmation = await confirmModalService.confirm({
|
||||
title: m.funny_aloof_swan_loop(),
|
||||
message: m.white_best_dolphin_catch({
|
||||
acct: `@${account.acct}`,
|
||||
}),
|
||||
confirmText: m.cute_polite_oryx_blend(),
|
||||
cancelText: m.soft_bold_ant_attend(),
|
||||
});
|
||||
|
||||
if (!confirmation.confirmed) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const id = toast.loading(m.big_safe_guppy_mix());
|
||||
const { data } = await client.value.unfollowAccount(account.id);
|
||||
toast.dismiss(id);
|
||||
|
||||
relationship.value = data;
|
||||
toast.success(m.misty_level_stingray_expand());
|
||||
};
|
||||
const formattedCreationDate = new Intl.DateTimeFormat(getLocale(), {
|
||||
dateStyle: "long",
|
||||
timeStyle: "short",
|
||||
}).format(new Date(account.created_at || 0));
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -23,23 +23,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col justify-center items-center mt-8">
|
||||
<span class="font-semibold" v-render-emojis="account.emojis">
|
||||
<Text class="font-bold" v-render-emojis="account.emojis">
|
||||
{{ account.display_name }}
|
||||
</span>
|
||||
<CopyableText :text="account.acct" class="text-sm">
|
||||
<span
|
||||
class="font-semibold bg-gradient-to-tr from-pink-700 dark:from-indigo-400 via-purple-700 dark:via-purple-400 to-indigo-700 dark:to-indigo-400 text-transparent bg-clip-text"
|
||||
>
|
||||
@{{ username }}
|
||||
</span>
|
||||
<span class="text-muted-foreground"
|
||||
>{{ instance && "@" }}{{ instance }}</span
|
||||
>
|
||||
</CopyableText>
|
||||
</Text>
|
||||
<Address :username="username" :domain="domain" />
|
||||
</div>
|
||||
<ProfileContent
|
||||
:content="account.note"
|
||||
:emojis="account.emojis"
|
||||
<Html
|
||||
v-html="account.note"
|
||||
v-render-emojis="account.emojis"
|
||||
class="mt-4 max-h-72 overflow-y-auto"
|
||||
/>
|
||||
<Separator v-if="account.fields.length > 0" class="mt-4" />
|
||||
|
|
@ -55,14 +46,15 @@
|
|||
import type { Account } from "@versia/client/schemas";
|
||||
import type { z } from "zod";
|
||||
import { Separator } from "~/components/ui/separator";
|
||||
import CopyableText from "../notes/copyable-text.vue";
|
||||
import Html from "../typography/html.vue";
|
||||
import Text from "../typography/text.vue";
|
||||
import Address from "./address.vue";
|
||||
import Avatar from "./avatar.vue";
|
||||
import ProfileContent from "./profile-content.vue";
|
||||
import ProfileFields from "./profile-fields.vue";
|
||||
|
||||
const { account } = defineProps<{
|
||||
account: z.infer<typeof Account>;
|
||||
}>();
|
||||
|
||||
const [username, instance] = account.acct.split("@");
|
||||
const [username, domain] = account.acct.split("@");
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -5,14 +5,10 @@
|
|||
>
|
||||
<Avatar :src="account.avatar" :name="account.display_name" class="size-10" />
|
||||
<CardContent class="leading-tight">
|
||||
<span
|
||||
class="font-semibold"
|
||||
v-render-emojis="account.emojis"
|
||||
>{{ account.display_name }}</span
|
||||
>
|
||||
<span class="text-xs">
|
||||
@{{ account.username }}@{{ domain }}
|
||||
</span>
|
||||
<Text class="font-semibold" v-render-emojis="account.emojis">
|
||||
{{ account.display_name }}
|
||||
</Text>
|
||||
<Address :username="account.username" :domain="domain" />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</template>
|
||||
|
|
@ -21,6 +17,8 @@
|
|||
import type { Account } from "@versia/client/schemas";
|
||||
import type { z } from "zod";
|
||||
import { Card, CardContent } from "~/components/ui/card";
|
||||
import Text from "../typography/text.vue";
|
||||
import Address from "./address.vue";
|
||||
import Avatar from "./avatar.vue";
|
||||
|
||||
const { account, domain, naked } = defineProps<{
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
<script setup lang="ts">
|
||||
import Avatar from "~/components/profiles/avatar.vue";
|
||||
import InstanceSmallCard from "~/components/instance/small-card.vue";
|
||||
import {
|
||||
SidebarHeader,
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
} from "~/components/ui/sidebar";
|
||||
import * as m from "~/paraglide/messages.js";
|
||||
|
||||
const instance = useInstance();
|
||||
</script>
|
||||
|
|
@ -16,32 +14,7 @@ const instance = useInstance();
|
|||
<SidebarMenu>
|
||||
<SidebarMenuItem>
|
||||
<NuxtLink href="/">
|
||||
<SidebarMenuButton size="lg">
|
||||
<Avatar
|
||||
class="size-8"
|
||||
:src="
|
||||
instance?.thumbnail?.url ??
|
||||
'https://cdn.versia.pub/branding/icon.svg'
|
||||
"
|
||||
:name="instance?.title"
|
||||
/>
|
||||
<div
|
||||
class="grid flex-1 text-left text-sm leading-tight"
|
||||
>
|
||||
<span class="truncate font-semibold">
|
||||
{{
|
||||
instance?.title ??
|
||||
m.short_zippy_felix_kick()
|
||||
}}
|
||||
</span>
|
||||
<span class="truncate text-xs">
|
||||
{{
|
||||
instance?.description ??
|
||||
m.top_active_ocelot_cure()
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</SidebarMenuButton>
|
||||
<InstanceSmallCard v-if="instance" :instance="instance" />
|
||||
</NuxtLink>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
|
|
|
|||
23
components/typography/bold.vue
Normal file
23
components/typography/bold.vue
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<script setup lang="ts">
|
||||
import { Primitive, type PrimitiveProps } from "reka-ui";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { cn } from "~/lib/utils";
|
||||
|
||||
interface Props extends PrimitiveProps {
|
||||
class?: HTMLAttributes["class"];
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
as: "strong",
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive
|
||||
:as="as"
|
||||
:as-child="asChild"
|
||||
:class="cn('font-semibold', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</Primitive>
|
||||
</template>
|
||||
23
components/typography/headings/large.vue
Normal file
23
components/typography/headings/large.vue
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<script setup lang="ts">
|
||||
import { Primitive, type PrimitiveProps } from "reka-ui";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { cn } from "~/lib/utils";
|
||||
|
||||
interface Props extends PrimitiveProps {
|
||||
class?: HTMLAttributes["class"];
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
as: "h1",
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive
|
||||
:as="as"
|
||||
:as-child="asChild"
|
||||
:class="cn('text-4xl font-extrabold tracking-tight text-balance', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</Primitive>
|
||||
</template>
|
||||
23
components/typography/headings/medium.vue
Normal file
23
components/typography/headings/medium.vue
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<script setup lang="ts">
|
||||
import { Primitive, type PrimitiveProps } from "reka-ui";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { cn } from "~/lib/utils";
|
||||
|
||||
interface Props extends PrimitiveProps {
|
||||
class?: HTMLAttributes["class"];
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
as: "h2",
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive
|
||||
:as="as"
|
||||
:as-child="asChild"
|
||||
:class="cn('text-3xl font-semibold tracking-tight', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</Primitive>
|
||||
</template>
|
||||
23
components/typography/headings/small.vue
Normal file
23
components/typography/headings/small.vue
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<script setup lang="ts">
|
||||
import { Primitive, type PrimitiveProps } from "reka-ui";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { cn } from "~/lib/utils";
|
||||
|
||||
interface Props extends PrimitiveProps {
|
||||
class?: HTMLAttributes["class"];
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
as: "h3",
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive
|
||||
:as="as"
|
||||
:as-child="asChild"
|
||||
:class="cn('text-sm font-semibold tracking-tight', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</Primitive>
|
||||
</template>
|
||||
27
components/typography/html.vue
Normal file
27
components/typography/html.vue
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<script setup lang="ts">
|
||||
import { Primitive, type PrimitiveProps } from "reka-ui";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { cn } from "~/lib/utils";
|
||||
|
||||
interface Props extends PrimitiveProps {
|
||||
class?: HTMLAttributes["class"];
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
as: "div",
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive
|
||||
:as="as"
|
||||
:as-child="asChild"
|
||||
:class="cn('prose prose-sm block relative dark:prose-invert duration-200 !max-w-full break-words prose-a:no-underline prose-a:hover:underline', $style.content, props.class)"
|
||||
>
|
||||
<slot />
|
||||
</Primitive>
|
||||
</template>
|
||||
|
||||
<style module>
|
||||
@import url("~/styles/content.css");
|
||||
</style>
|
||||
25
components/typography/layout/col.vue
Normal file
25
components/typography/layout/col.vue
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<script setup lang="ts">
|
||||
import { Primitive, type PrimitiveProps } from "reka-ui";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { cn } from "~/lib/utils";
|
||||
|
||||
interface Props extends PrimitiveProps {
|
||||
class?: HTMLAttributes["class"];
|
||||
wrap?: boolean;
|
||||
centered?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
as: "div",
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive
|
||||
:as="as"
|
||||
:as-child="asChild"
|
||||
:class="cn('flex flex-col', props.wrap && 'flex-wrap', props.class, props.centered && 'items-center')"
|
||||
>
|
||||
<slot />
|
||||
</Primitive>
|
||||
</template>
|
||||
25
components/typography/layout/row.vue
Normal file
25
components/typography/layout/row.vue
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<script setup lang="ts">
|
||||
import { Primitive, type PrimitiveProps } from "reka-ui";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { cn } from "~/lib/utils";
|
||||
|
||||
interface Props extends PrimitiveProps {
|
||||
class?: HTMLAttributes["class"];
|
||||
wrap?: boolean;
|
||||
centered?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
as: "div",
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive
|
||||
:as="as"
|
||||
:as-child="asChild"
|
||||
:class="cn('flex flex-row', props.wrap && 'flex-wrap', props.class, props.centered && 'items-center')"
|
||||
>
|
||||
<slot />
|
||||
</Primitive>
|
||||
</template>
|
||||
24
components/typography/small.vue
Normal file
24
components/typography/small.vue
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<script setup lang="ts">
|
||||
import { Primitive, type PrimitiveProps } from "reka-ui";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { cn } from "~/lib/utils";
|
||||
|
||||
interface Props extends PrimitiveProps {
|
||||
class?: HTMLAttributes["class"];
|
||||
muted?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
as: "span",
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive
|
||||
:as="as"
|
||||
:as-child="asChild"
|
||||
:class="cn('text-xs', props.class, props.muted && 'text-muted-foreground')"
|
||||
>
|
||||
<slot />
|
||||
</Primitive>
|
||||
</template>
|
||||
24
components/typography/text.vue
Normal file
24
components/typography/text.vue
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<script setup lang="ts">
|
||||
import { Primitive, type PrimitiveProps } from "reka-ui";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { cn } from "~/lib/utils";
|
||||
|
||||
interface Props extends PrimitiveProps {
|
||||
class?: HTMLAttributes["class"];
|
||||
muted?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
as: "p",
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive
|
||||
:as="as"
|
||||
:as-child="asChild"
|
||||
:class="cn('leading-7', props.class, props.muted && 'text-muted-foreground')"
|
||||
>
|
||||
<slot />
|
||||
</Primitive>
|
||||
</template>
|
||||
|
|
@ -20,11 +20,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1744536153,
|
||||
"narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=",
|
||||
"lastModified": 1751949589,
|
||||
"narHash": "sha256-mgFxAPLWw0Kq+C8P3dRrZrOYEQXOtKuYVlo9xvPntt8=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11",
|
||||
"rev": "9b008d60392981ad674e04016d25619281550a9d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ in
|
|||
|
||||
pnpmDeps = pnpm.fetchDeps {
|
||||
inherit (finalAttrs) pname version src;
|
||||
hash = "sha256-vNx9MNsMqllLxRxC93slvzHza6cHtPKgWxbvTy6QH4M=";
|
||||
hash = "sha256-or+GR3zZGOqcC/6h2pHlhK2SGyysIAnghik8gpJhAlk=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [
|
||||
|
|
|
|||
46
package.json
46
package.json
|
|
@ -37,26 +37,26 @@
|
|||
"@tailwindcss/typography": "^0.5.16",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@tanstack/vue-table": "^8.21.3",
|
||||
"@tiptap/extension-highlight": "^2.22.3",
|
||||
"@tiptap/extension-image": "^2.22.3",
|
||||
"@tiptap/extension-link": "^2.22.3",
|
||||
"@tiptap/extension-mention": "^2.22.3",
|
||||
"@tiptap/extension-placeholder": "^2.22.3",
|
||||
"@tiptap/extension-subscript": "^2.22.3",
|
||||
"@tiptap/extension-superscript": "^2.22.3",
|
||||
"@tiptap/extension-task-item": "^2.22.3",
|
||||
"@tiptap/extension-task-list": "^2.22.3",
|
||||
"@tiptap/extension-underline": "^2.22.3",
|
||||
"@tiptap/pm": "^2.22.3",
|
||||
"@tiptap/starter-kit": "^2.22.3",
|
||||
"@tiptap/suggestion": "^2.22.3",
|
||||
"@tiptap/vue-3": "^2.22.3",
|
||||
"@tiptap/extension-highlight": "^2.25.0",
|
||||
"@tiptap/extension-image": "^2.25.0",
|
||||
"@tiptap/extension-link": "^2.25.0",
|
||||
"@tiptap/extension-mention": "^2.25.0",
|
||||
"@tiptap/extension-placeholder": "^2.25.0",
|
||||
"@tiptap/extension-subscript": "^2.25.0",
|
||||
"@tiptap/extension-superscript": "^2.25.0",
|
||||
"@tiptap/extension-task-item": "^2.25.0",
|
||||
"@tiptap/extension-task-list": "^2.25.0",
|
||||
"@tiptap/extension-underline": "^2.25.0",
|
||||
"@tiptap/pm": "^2.25.0",
|
||||
"@tiptap/starter-kit": "^2.25.0",
|
||||
"@tiptap/suggestion": "^2.25.0",
|
||||
"@tiptap/vue-3": "^2.25.0",
|
||||
"@vee-validate/zod": "^4.15.1",
|
||||
"@versia/client": "0.2.0-alpha.4",
|
||||
"@videojs-player/vue": "^1.0.0",
|
||||
"@vite-pwa/nuxt": "^1.0.4",
|
||||
"@vueuse/core": "^13.4.0",
|
||||
"@vueuse/nuxt": "^13.4.0",
|
||||
"@vueuse/core": "^13.5.0",
|
||||
"@vueuse/nuxt": "^13.5.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"embla-carousel-vue": "^8.6.0",
|
||||
|
|
@ -64,18 +64,18 @@
|
|||
"emojibase-data": "^16.0.3",
|
||||
"fuzzysort": "^3.1.0",
|
||||
"html-to-text": "^9.0.5",
|
||||
"lucide-vue-next": "^0.523.0",
|
||||
"lucide-vue-next": "^0.525.0",
|
||||
"magic-regexp": "^0.10.0",
|
||||
"mitt": "^3.0.1",
|
||||
"nanoid": "^5.1.5",
|
||||
"nuxt": "^3.17.5",
|
||||
"nuxt": "^3.17.6",
|
||||
"nuxt-security": "^2.2.0",
|
||||
"reka-ui": "^2.3.1",
|
||||
"reka-ui": "^2.3.2",
|
||||
"shadcn-nuxt": "2.2.0",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwindcss": "^4.1.11",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"tw-animate-css": "^1.3.4",
|
||||
"tw-animate-css": "^1.3.5",
|
||||
"vaul-vue": "^0.4.1",
|
||||
"vee-validate": "^4.15.1",
|
||||
"virtua": "^0.41.5",
|
||||
|
|
@ -86,19 +86,19 @@
|
|||
"zod": "^3.25.67"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^2.0.5",
|
||||
"@biomejs/biome": "^2.1.1",
|
||||
"@iconify-json/fluent-emoji": "^1.2.3",
|
||||
"@iconify-json/fluent-emoji-flat": "^1.2.3",
|
||||
"@iconify-json/noto": "^1.2.3",
|
||||
"@iconify-json/twemoji": "^1.2.2",
|
||||
"@iconify/utils": "^2.3.0",
|
||||
"@inlang/paraglide-js": "2.1.0",
|
||||
"@inlang/paraglide-js": "2.2.0",
|
||||
"@inlang/plugin-m-function-matcher": "^2.1.0",
|
||||
"@inlang/plugin-message-format": "^4.0.0",
|
||||
"@tailwindcss/forms": "^0.5.10",
|
||||
"@types/html-to-text": "^9.0.4",
|
||||
"typescript": "^5.8.3",
|
||||
"vue-tsc": "^2.2.10"
|
||||
"vue-tsc": "^3.0.1"
|
||||
},
|
||||
"trustedDependencies": [
|
||||
"@biomejs/biome",
|
||||
|
|
|
|||
2042
pnpm-lock.yaml
2042
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
|
@ -6,78 +6,72 @@
|
|||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
:root {
|
||||
--radius: 0.65rem;
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.145 0 0);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.141 0.005 285.823);
|
||||
--card-foreground: oklch(0.145 0 0);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.141 0.005 285.823);
|
||||
--primary: oklch(0.21 0.006 285.885);
|
||||
--popover-foreground: oklch(0.145 0 0);
|
||||
--primary: oklch(0.205 0 0);
|
||||
--primary-foreground: oklch(0.985 0 0);
|
||||
--secondary: oklch(0.967 0.001 286.375);
|
||||
--secondary-foreground: oklch(0.21 0.006 285.885);
|
||||
--muted: oklch(0.967 0.001 286.375);
|
||||
--muted-foreground: oklch(0.552 0.016 285.938);
|
||||
--accent: oklch(0.967 0.001 286.375);
|
||||
--accent-foreground: oklch(0.21 0.006 285.885);
|
||||
--warning: oklch(0.577 0.245 27.325);
|
||||
--warning-foreground: oklch(0.577 0.245 27.325);
|
||||
--secondary: oklch(0.97 0 0);
|
||||
--secondary-foreground: oklch(0.205 0 0);
|
||||
--muted: oklch(0.97 0 0);
|
||||
--muted-foreground: oklch(0.556 0 0);
|
||||
--accent: oklch(0.97 0 0);
|
||||
--accent-foreground: oklch(0.205 0 0);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--destructive-foreground: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.92 0.004 286.32);
|
||||
--input: oklch(0.92 0.004 286.32);
|
||||
--ring: oklch(0.705 0.015 286.067);
|
||||
--border: oklch(0.922 0 0);
|
||||
--input: oklch(0.922 0 0);
|
||||
--ring: oklch(0.708 0 0);
|
||||
--chart-1: oklch(0.646 0.222 41.116);
|
||||
--chart-2: oklch(0.6 0.118 184.704);
|
||||
--chart-3: oklch(0.398 0.07 227.392);
|
||||
--chart-4: oklch(0.828 0.189 84.429);
|
||||
--chart-5: oklch(0.769 0.188 70.08);
|
||||
--radius: 0.625rem;
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.141 0.005 285.823);
|
||||
--sidebar-primary: oklch(0.21 0.006 285.885);
|
||||
--sidebar-foreground: oklch(0.145 0 0);
|
||||
--sidebar-primary: oklch(0.205 0 0);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.967 0.001 286.375);
|
||||
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
|
||||
--sidebar-border: oklch(0.92 0.004 286.32);
|
||||
--sidebar-ring: oklch(0.705 0.015 286.067);
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.141 0.005 285.823);
|
||||
--sidebar-accent: oklch(0.97 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||
--sidebar-border: oklch(0.922 0 0);
|
||||
--sidebar-ring: oklch(0.708 0 0);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.141 0.005 285.823);
|
||||
--background: oklch(0.145 0 0);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.141 0.005 285.823);
|
||||
--card: oklch(0.205 0 0);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.141 0.005 285.823);
|
||||
--popover: oklch(0.205 0 0);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.985 0 0);
|
||||
--primary-foreground: oklch(0.21 0.006 285.885);
|
||||
--secondary: oklch(0.274 0.006 286.033);
|
||||
--primary: oklch(0.922 0 0);
|
||||
--primary-foreground: oklch(0.205 0 0);
|
||||
--secondary: oklch(0.269 0 0);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.274 0.006 286.033);
|
||||
--muted-foreground: oklch(0.705 0.015 286.067);
|
||||
--accent: oklch(0.274 0.006 286.033);
|
||||
--muted: oklch(0.269 0 0);
|
||||
--muted-foreground: oklch(0.708 0 0);
|
||||
--accent: oklch(0.269 0 0);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--warning: oklch(0.396 0.141 25.723);
|
||||
--warning-foreground: oklch(0.396 0.141 25.723);
|
||||
--destructive: oklch(0.396 0.141 25.723);
|
||||
--destructive-foreground: oklch(0.637 0.237 25.331);
|
||||
--border: oklch(0.274 0.006 286.033);
|
||||
--input: oklch(0.274 0.006 286.033);
|
||||
--ring: oklch(0.442 0.017 285.786);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.556 0 0);
|
||||
--chart-1: oklch(0.488 0.243 264.376);
|
||||
--chart-2: oklch(0.696 0.17 162.48);
|
||||
--chart-3: oklch(0.769 0.188 70.08);
|
||||
--chart-4: oklch(0.627 0.265 303.9);
|
||||
--chart-5: oklch(0.645 0.246 16.439);
|
||||
--sidebar: oklch(0.21 0.006 285.885);
|
||||
--sidebar: oklch(0.205 0 0);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.274 0.006 286.033);
|
||||
--sidebar-accent: oklch(0.269 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(0.274 0.006 286.033);
|
||||
--sidebar-ring: oklch(0.442 0.017 285.786);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.556 0 0);
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
|
|
|
|||
Loading…
Reference in a new issue