style: 🎨 Format code with Biome

This commit is contained in:
Jesse Wierzbinski 2025-12-09 22:32:22 +01:00
parent 7ff9d2302a
commit 3627ac0ef8
No known key found for this signature in database
296 changed files with 3257 additions and 2808 deletions

View file

@ -1,7 +1,7 @@
<template>
<Button variant="ghost" class="max-w-14 w-full" size="sm">
<component :is="icon" class="size-4" />
<slot />
<component :is="icon" class="size-4"/>
<slot/>
</Button>
</template>

View file

@ -1,20 +1,58 @@
<template>
<div class="flex flex-row w-full max-w-sm items-stretch justify-between">
<ActionButton :icon="Reply" @click="emit('reply')" :title="m.drab_tense_turtle_comfort()" :disabled="!authStore.isSignedIn">
<ActionButton
:icon="Reply"
@click="emit('reply')"
:title="m.drab_tense_turtle_comfort()"
:disabled="!authStore.isSignedIn"
>
{{ numberFormat(replyCount) }}
</ActionButton>
<ActionButton :icon="Heart" @click="liked ? unlike() : like()" :title="liked ? m.vexed_fluffy_clownfish_dance() : m.royal_close_samuel_scold()" :disabled="!authStore.isSignedIn" :class="liked && '*:fill-red-600 *:text-red-600'">
<ActionButton
:icon="Heart"
@click="liked ? unlike() : like()"
:title="liked ? m.vexed_fluffy_clownfish_dance() : m.royal_close_samuel_scold()"
:disabled="!authStore.isSignedIn"
:class="liked && '*:fill-red-600 *:text-red-600'"
>
{{ numberFormat(likeCount) }}
</ActionButton>
<ActionButton :icon="Repeat" @click="reblogged ? unreblog() : reblog()" :title="reblogged ? m.lime_neat_ox_stab() : m.aware_helpful_marlin_drop()" :disabled="!authStore.isSignedIn" :class="reblogged && '*:text-green-600'">
<ActionButton
:icon="Repeat"
@click="reblogged ? unreblog() : reblog()"
:title="reblogged ? m.lime_neat_ox_stab() : m.aware_helpful_marlin_drop()"
:disabled="!authStore.isSignedIn"
:class="reblogged && '*:text-green-600'"
>
{{ numberFormat(reblogCount) }}
</ActionButton>
<ActionButton :icon="Quote" @click="emit('quote')" :title="m.true_shy_jackal_drip()" :disabled="!authStore.isSignedIn" />
<ActionButton
:icon="Quote"
@click="emit('quote')"
:title="m.true_shy_jackal_drip()"
:disabled="!authStore.isSignedIn"
/>
<Picker @pick="react">
<ActionButton :icon="Smile" :title="m.bald_cool_kangaroo_jump()" :disabled="!authStore.isSignedIn" />
<ActionButton
:icon="Smile"
:title="m.bald_cool_kangaroo_jump()"
:disabled="!authStore.isSignedIn"
/>
</Picker>
<Menu :api-note-string="apiNoteString" :url="url" :remote-url="remoteUrl" :is-remote="isRemote" :author-id="authorId" @edit="emit('edit')" :note-id="noteId" @delete="emit('delete')">
<ActionButton :icon="Ellipsis" :title="m.busy_merry_cowfish_absorb()" />
<Menu
:api-note-string="apiNoteString"
:url="url"
:remote-url="remoteUrl"
:is-remote="isRemote"
:author-id="authorId"
@edit="emit('edit')"
:note-id="noteId"
@delete="emit('delete')"
>
<ActionButton
:icon="Ellipsis"
:title="m.busy_merry_cowfish_absorb()"
/>
</Menu>
</div>
</template>

View file

@ -1,8 +1,17 @@
<template>
<ImageAttachment v-if="attachment.type === 'image'" :attachment="attachment" />
<VideoAttachment v-else-if="attachment.type === 'video' || attachment.type === 'gifv'" :attachment="attachment" />
<AudioAttachment v-else-if="attachment.type === 'audio'" :attachment="attachment" />
<FileAttachment v-else :attachment="attachment" />
<ImageAttachment
v-if="attachment.type === 'image'"
:attachment="attachment"
/>
<VideoAttachment
v-else-if="attachment.type === 'video' || attachment.type === 'gifv'"
:attachment="attachment"
/>
<AudioAttachment
v-else-if="attachment.type === 'audio'"
:attachment="attachment"
/>
<FileAttachment v-else :attachment="attachment"/>
</template>
<script lang="ts" setup>

View file

@ -1,7 +1,13 @@
<template>
<!-- [&:has(>:last-child:nth-child(1))] means "when this element has 1 child" -->
<div class="grid gap-4 grid-cols-2 *:max-h-56 [&:has(>:last-child:nth-child(1))]:grid-cols-1 sm:[&:has(>:last-child:nth-child(1))>*]:max-h-72">
<Attachment v-for="attachment in attachments" :key="attachment.id" :attachment="attachment" />
<div
class="grid gap-4 grid-cols-2 *:max-h-56 [&:has(>:last-child:nth-child(1))]:grid-cols-1 sm:[&:has(>:last-child:nth-child(1))>*]:max-h-72"
>
<Attachment
v-for="attachment in attachments"
:key="attachment.id"
:attachment="attachment"
/>
</div>
</template>

View file

@ -1,16 +1,22 @@
<template>
<Dialog>
<Card class="w-full h-full overflow-hidden relative p-0 *:first:w-full *:first:h-full *:first:object-contain *:first:bg-muted/20">
<Card
class="w-full h-full overflow-hidden relative p-0 *:first:w-full *:first:h-full *:first:object-contain *:first:bg-muted/20"
>
<DialogTrigger v-if="lightbox" :as-child="true">
<slot />
<slot/>
</DialogTrigger>
<slot v-else />
<slot v-else/>
<!-- Alt text viewer -->
<Popover v-if="attachment.description">
<div class="absolute top-0 right-0 p-2">
<PopoverTrigger :as-child="true">
<Button variant="outline" size="icon" title="View alt text">
<Captions />
<Button
variant="outline"
size="icon"
title="View alt text"
>
<Captions/>
</Button>
</PopoverTrigger>
</div>
@ -19,26 +25,42 @@
</PopoverContent>
</Popover>
</Card>
<DialogContent :hide-close="true"
class="duration-200 bg-transparent border-none overflow-hidden !animate-none gap-6 w-screen h-screen !max-w-none">
<DialogContent
:hide-close="true"
class="duration-200 bg-transparent border-none overflow-hidden !animate-none gap-6 w-screen h-screen !max-w-none"
>
<div class="grid grid-rows-[auto_1fr_auto]">
<div class="flex flex-row gap-2 w-full">
<DialogTitle class="sr-only">{{ attachment.type }}</DialogTitle>
<Button as="a" :href="attachment?.url" target="_blank" :download="true" variant="outline" size="icon"
class="ml-auto">
<Download />
<DialogTitle class="sr-only">
{{ attachment.type }}
</DialogTitle>
<Button
as="a"
:href="attachment?.url"
target="_blank"
:download="true"
variant="outline"
size="icon"
class="ml-auto"
>
<Download/>
</Button>
<DialogClose :as-child="true">
<Button variant="outline" size="icon">
<X />
<X/>
</Button>
</DialogClose>
</div>
<div class="flex items-center justify-center overflow-hidden *:max-h-[80vh] *:max-w-[80vw] *:w-full *:h-full *:object-contain">
<slot />
<div
class="flex items-center justify-center overflow-hidden *:max-h-[80vh] *:max-w-[80vw] *:w-full *:h-full *:object-contain"
>
<slot/>
</div>
<DialogDescription class="flex items-center justify-center">
<Card v-if="attachment.description" class="max-w-md max-h-48 overflow-auto text-sm">
<Card
v-if="attachment.description"
class="max-w-md max-h-48 overflow-auto text-sm"
>
<p>{{ attachment.description }}</p>
</Card>
</DialogDescription>

View file

@ -1,13 +1,17 @@
<template>
<Base :attachment="attachment">
<audio :src="attachment.url" :alt="attachment.description ?? undefined" controls />
</Base>
<AttachmentBase :attachment="attachment">
<audio
:src="attachment.url"
:alt="attachment.description ?? undefined"
controls
/>
</AttachmentBase>
</template>
<script lang="ts" setup>
import type { Attachment } from "@versia/client/schemas";
import type { z } from "zod";
import Base from "./base.vue";
import AttachmentBase from "./attachment-base.vue";
const { attachment } = defineProps<{
attachment: z.infer<typeof Attachment>;

View file

@ -1,17 +1,19 @@
<template>
<Base :attachment="attachment" lightbox>
<div class="flex flex-col items-center justify-center min-h-48 text-sm gap-2">
<File class="size-12" />
<AttachmentBase :attachment="attachment" lightbox>
<div
class="flex flex-col items-center justify-center min-h-48 text-sm gap-2"
>
<File class="size-12"/>
<span>File attachment</span>
</div>
</Base>
</AttachmentBase>
</template>
<script lang="ts" setup>
import type { Attachment } from "@versia/client/schemas";
import { File } from "lucide-vue-next";
import type { z } from "zod";
import Base from "./base.vue";
import AttachmentBase from "./attachment-base.vue";
const { attachment } = defineProps<{
attachment: z.infer<typeof Attachment>;

View file

@ -1,13 +1,13 @@
<template>
<Base :attachment="attachment" lightbox>
<img :src="attachment.url" :alt="attachment.description ?? undefined" />
</Base>
<AttachmentBase :attachment="attachment" lightbox>
<img :src="attachment.url" :alt="attachment.description ?? undefined">
</AttachmentBase>
</template>
<script lang="ts" setup>
import type { Attachment } from "@versia/client/schemas";
import type { z } from "zod";
import Base from "./base.vue";
import AttachmentBase from "./attachment-base.vue";
const { attachment } = defineProps<{
attachment: z.infer<typeof Attachment>;

View file

@ -1,13 +1,19 @@
<template>
<Base :attachment="attachment">
<video :src="attachment.url" :alt="attachment.description ?? undefined" controls />
</Base>
<AttachmentBase :attachment="attachment">
<video
:src="attachment.url"
:alt="attachment.description ?? undefined"
controls
>
Your browser does not support the video tag.
</video>
</AttachmentBase>
</template>
<script lang="ts" setup>
import type { Attachment } from "@versia/client/schemas";
import type { z } from "zod";
import Base from "./base.vue";
import AttachmentBase from "./attachment-base.vue";
const { attachment } = defineProps<{
attachment: z.infer<typeof Attachment>;

View file

@ -3,10 +3,18 @@
<p class="text-sm leading-6 wrap-anywhere">
{{ contentWarning || m.sour_seemly_bird_hike() }}
</p>
<Button @click="hidden = !hidden" variant="outline" size="sm" class="col-span-2">
<Button
@click="hidden = !hidden"
variant="outline"
size="sm"
class="col-span-2"
>
{{ hidden ? m.bald_direct_turtle_win() :
m.known_flaky_cockroach_dash() }} {{ characterCount > 0 ? ` (${characterCount} characters` : "" }}{{
attachmentCount > 0 ? `${characterCount > 0 ? " · " : " ("}${attachmentCount} file(s)` : "" }}{{ (characterCount > 0 || attachmentCount > 0) ? ")" : "" }}
m.known_flaky_cockroach_dash() }}
{{ characterCount > 0 ? ` (${characterCount} characters` : "" }}
{{
attachmentCount > 0 ? `${characterCount > 0 ? " · " : " ("}${attachmentCount} file(s)` : "" }}
{{ (characterCount > 0 || attachmentCount > 0) ? ")" : "" }}
</Button>
</div>
</template>

View file

@ -1,14 +1,28 @@
<template>
<ContentWarning v-if="(sensitive || contentWarning) && preferences.show_content_warning" :content-warning="contentWarning" :character-count="characterCount ?? 0" :attachment-count="attachments.length" v-model="hidden" />
<ContentWarning
v-if="(sensitive || contentWarning) && preferences.show_content_warning"
:content-warning="contentWarning"
:character-count="characterCount ?? 0"
:attachment-count="attachments.length"
v-model="hidden"
/>
<OverflowGuard v-if="content" :character-count="characterCount" :class="(hidden && preferences.show_content_warning) && 'hidden'">
<OverflowGuard
v-if="content"
:character-count="characterCount"
:class="(hidden && preferences.show_content_warning) && 'hidden'"
>
<Prose v-html="content" v-render-emojis="emojis"></Prose>
</OverflowGuard>
<Attachments v-if="attachments.length > 0" :attachments="attachments" :class="(hidden && preferences.show_content_warning) && 'hidden'" />
<Attachments
v-if="attachments.length > 0"
:attachments="attachments"
:class="(hidden && preferences.show_content_warning) && 'hidden'"
/>
<div v-if="quote" class="mt-4 rounded border overflow-hidden">
<Note :note="quote" :hide-actions="true" :small-layout="true" />
<Note :note="quote" :hide-actions="true" :small-layout="true"/>
</div>
</template>

View file

@ -1,36 +1,61 @@
<template>
<div class="rounded grid grid-cols-[auto_1fr_auto] items-center gap-3">
<HoverCard v-model:open="popupOpen" @update:open="() => {
<HoverCard
v-model:open="popupOpen"
@update:open="() => {
if (!preferences.popup_avatar_hover) {
popupOpen = false;
}
}" :open-delay="2000">
}"
:open-delay="2000"
>
<HoverCardTrigger :as-child="true">
<NuxtLink :href="urlAsPath" :class="cn('relative size-12', smallLayout && 'size-8')">
<Avatar :class="cn('size-12 border border-card', smallLayout && 'size-8')" :src="author.avatar"
:name="author.display_name" />
<Avatar v-if="cornerAvatar" class="size-6 border absolute -bottom-1 -right-1" :src="cornerAvatar" />
<NuxtLink
:href="urlAsPath"
:class="cn('relative size-12', smallLayout && 'size-8')"
>
<Avatar
:class="cn('size-12 border border-card', smallLayout && 'size-8')"
:src="author.avatar"
:name="author.display_name"
/>
<Avatar
v-if="cornerAvatar"
class="size-6 border absolute -bottom-1 -right-1"
:src="cornerAvatar"
/>
</NuxtLink>
</HoverCardTrigger>
<HoverCardContent class="w-96">
<SmallCard :account="author" />
<SmallCard :account="author"/>
</HoverCardContent>
</HoverCard>
<Col
:class="smallLayout && 'text-sm'">
<Text class="font-semibold" v-render-emojis="author.emojis">{{
<Column :class="smallLayout && 'text-sm'">
<Text class="font-semibold" v-render-emojis="author.emojis">
{{
author.display_name
}}</Text>
}}
</Text>
<div class="-mt-1">
<Address as="span" :username="username" :domain="instance" />
<Address as="span" :username="username" :domain="instance"/>
&middot;
<Text as="span" muted class="ml-auto tracking-normal" :title="fullTime">{{ timeAgo }}</Text>
<Text
as="span"
muted
class="ml-auto tracking-normal"
:title="fullTime"
>
{{ timeAgo }}
</Text>
</div>
</Col>
</Column>
<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" />
<NuxtLink
:href="noteUrlAsPath"
class="text-xs text-muted-foreground"
:title="visibilities[visibility].text"
>
<component :is="visibilities[visibility].icon" class="size-4"/>
</NuxtLink>
</div>
</div>
@ -49,7 +74,7 @@ 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 Column from "../typography/layout/col.vue";
import Text from "../typography/text.vue";
import {
HoverCard,

View file

@ -79,57 +79,71 @@ const _delete = async () => {
<template>
<DropdownMenu>
<DropdownMenuTrigger as-child>
<slot />
<slot/>
</DropdownMenuTrigger>
<DropdownMenuContent class="min-w-56">
<DropdownMenuGroup>
<DropdownMenuItem v-if="authorIsMe" as="button" @click="emit('edit')">
<Pencil />
<DropdownMenuItem
v-if="authorIsMe"
as="button"
@click="emit('edit')"
>
<Pencil/>
{{ m.front_lime_grizzly_persist() }}
</DropdownMenuItem>
<DropdownMenuItem as="button" @click="copyText(apiNoteString)">
<Code />
<Code/>
{{ m.yummy_moving_scallop_sail() }}
</DropdownMenuItem>
<DropdownMenuItem as="button" @click="copyText(noteId)">
<Hash />
<Hash/>
{{ m.sunny_zany_jellyfish_pop() }}
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuSeparator/>
<DropdownMenuGroup>
<DropdownMenuItem as="button" @click="copyText(url)">
<Link />
<Link/>
{{ m.ago_new_pelican_drip() }}
</DropdownMenuItem>
<DropdownMenuItem as="button" v-if="isRemote && remoteUrl" @click="copyText(remoteUrl)">
<Link />
<DropdownMenuItem
as="button"
v-if="isRemote && remoteUrl"
@click="copyText(remoteUrl)"
>
<Link/>
{{ m.solid_witty_zebra_walk() }}
</DropdownMenuItem>
<DropdownMenuItem as="a" v-if="isRemote" target="_blank" rel="noopener noreferrer" :href="remoteUrl">
<ExternalLink />
<DropdownMenuItem
as="a"
v-if="isRemote"
target="_blank"
rel="noopener noreferrer"
:href="remoteUrl"
>
<ExternalLink/>
{{ m.active_trite_lark_inspire() }}
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator v-if="authorIsMe" />
<DropdownMenuSeparator v-if="authorIsMe"/>
<DropdownMenuGroup v-if="authorIsMe">
<DropdownMenuItem as="button" :disabled="true">
<Delete />
<Delete/>
{{ m.real_green_clownfish_pet() }}
</DropdownMenuItem>
<DropdownMenuItem as="button" @click="_delete">
<Trash />
<Trash/>
{{ m.tense_quick_cod_favor() }}
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator v-if="authStore.isSignedIn && !authorIsMe" />
<DropdownMenuSeparator v-if="authStore.isSignedIn && !authorIsMe"/>
<DropdownMenuGroup v-if="authStore.isSignedIn && !authorIsMe">
<DropdownMenuItem as="button" :disabled="true">
<Flag />
<Flag/>
{{ m.great_few_jaguar_rise() }}
</DropdownMenuItem>
<DropdownMenuItem as="button" @click="blockUser(authorId)">
<Ban />
<Ban/>
{{ m.misty_soft_sparrow_vent() }}
</DropdownMenuItem>
</DropdownMenuGroup>

View file

@ -50,7 +50,12 @@
:sensitive="noteToUse.sensitive"
:content-warning="noteToUse.spoiler_text"
/>
<Reactions v-if="noteToUse.reactions && noteToUse.reactions.length > 0" :reactions="noteToUse.reactions" :emojis="noteToUse.emojis" :status-id="noteToUse.id" />
<Reactions
v-if="noteToUse.reactions && noteToUse.reactions.length > 0"
:reactions="noteToUse.reactions"
:emojis="noteToUse.emojis"
:status-id="noteToUse.id"
/>
</CardContent>
<CardFooter v-if="!hideActions">
<Actions

View file

@ -1,18 +1,29 @@
<template>
<div ref="container" class="overflow-y-hidden relative duration-200" :style="{
<div
ref="container"
class="overflow-y-hidden relative duration-200"
:style="{
maxHeight: collapsed ? '18rem' : `${container?.scrollHeight}px`,
}">
<slot />
<div v-if="isOverflowing && collapsed"
class="absolute inset-x-0 bottom-0 h-36 bg-gradient-to-t from-black/5 to-transparent rounded-b"></div>
<Button v-if="isOverflowing" @click="collapsed = !collapsed"
class="absolute bottom-2 right-1/2 translate-x-1/2">{{
}"
>
<slot/>
<div
v-if="isOverflowing && collapsed"
class="absolute inset-x-0 bottom-0 h-36 bg-gradient-to-t from-black/5 to-transparent rounded-b"
></div>
<Button
v-if="isOverflowing"
@click="collapsed = !collapsed"
class="absolute bottom-2 right-1/2 translate-x-1/2"
>
{{
collapsed
? `${m.lazy_honest_mammoth_bump()}${formattedCharacterCount ? `${m.dark_spare_goldfish_charm({
count: formattedCharacterCount,
})}` : ""}`
: m.that_misty_mule_arrive()
}}</Button>
}}
</Button>
</div>
</template>

View file

@ -1,9 +1,11 @@
<template>
<div :class="[
<div
:class="[
'prose prose-sm block relative dark:prose-invert duration-200 !max-w-full break-words prose-a:no-underline hover:prose-a:underline',
$style.content,
]">
<slot />
]"
>
<slot/>
</div>
</template>

View file

@ -1,6 +1,12 @@
<template>
<div class="flex flex-row gap-2 flex-wrap">
<Reaction v-for="reaction in reactions" :key="reaction.name" :reaction="reaction" :emoji="emojis.find(e => `:${e.shortcode}:` === reaction.name)" :status-id="statusId" />
<Reaction
v-for="reaction in reactions"
:key="reaction.name"
:reaction="reaction"
:emoji="emojis.find(e => `:${e.shortcode}:` === reaction.name)"
:status-id="statusId"
/>
</div>
</template>

View file

@ -1,8 +1,6 @@
<template>
<div class="sticky top-2 z-10 flex items-center justify-center p-2">
<Badge variant="secondary">
{{ categoryName }}
</Badge>
<Badge variant="secondary">{{ categoryName }}</Badge>
</div>
</template>

View file

@ -1,8 +1,17 @@
<template>
<div class="p-2 text-sm font-semibold border-0 rounded-none text-center flex flex-row items-center gap-2 truncate">
<img v-if="(emoji as InferredEmoji)?.url" :src="(emoji as InferredEmoji)?.url"
:alt="(emoji as InferredEmoji)?.shortcode" class="h-8 align-middle inline not-prose" />
<span v-else-if="(emoji as UnicodeEmoji)?.unicode" class="text-2xl align-middle inline not-prose">
<div
class="p-2 text-sm font-semibold border-0 rounded-none text-center flex flex-row items-center gap-2 truncate"
>
<img
v-if="(emoji as InferredEmoji)?.url"
:src="(emoji as InferredEmoji)?.url"
:alt="(emoji as InferredEmoji)?.shortcode"
class="h-8 align-middle inline not-prose"
>
<span
v-else-if="(emoji as UnicodeEmoji)?.unicode"
class="text-2xl align-middle inline not-prose"
>
{{ (emoji as UnicodeEmoji)?.unicode }}
</span>
{{ (emoji as InferredEmoji)?.shortcode || (emoji as UnicodeEmoji)?.shortcode }}

View file

@ -1,9 +1,22 @@
<template>
<Button @focus="() => emit('select', emoji)" @mouseenter="() => emit('select', emoji)" @click="() => emit('pick', emoji)" size="icon" variant="ghost"
class="size-12">
<img v-if="(emoji as InferredEmoji).url" :src="(emoji as InferredEmoji).url"
:alt="(emoji as InferredEmoji).shortcode" class="h-8 align-middle inline not-prose" />
<span v-else-if="(emoji as UnicodeEmoji).unicode" class="text-2xl align-middle inline not-prose">
<Button
@focus="() => emit('select', emoji)"
@mouseenter="() => emit('select', emoji)"
@click="() => emit('pick', emoji)"
size="icon"
variant="ghost"
class="size-12"
>
<img
v-if="(emoji as InferredEmoji).url"
:src="(emoji as InferredEmoji).url"
:alt="(emoji as InferredEmoji).shortcode"
class="h-8 align-middle inline not-prose"
>
<span
v-else-if="(emoji as UnicodeEmoji).unicode"
class="text-2xl align-middle inline not-prose"
>
{{ (emoji as UnicodeEmoji).unicode }}
</span>
</Button>

View file

@ -1,31 +1,57 @@
<template>
<Popover v-model:open="open">
<PopoverTrigger as-child>
<slot />
<slot/>
</PopoverTrigger>
<PopoverContent class="p-0 w-fit">
<div class="grid-cols-[minmax(0,1fr)_auto] gap-0 grid divide-x *:h-112 *:overflow-y-auto"
orientation="vertical">
<div class="grid grid-rows-[auto_minmax(0,1fr)_auto] gap-0" ref="emojiContainer">
<div
class="grid-cols-[minmax(0,1fr)_auto] gap-0 grid divide-x *:h-112 *:overflow-y-auto"
orientation="vertical"
>
<div
class="grid grid-rows-[auto_minmax(0,1fr)_auto] gap-0"
ref="emojiContainer"
>
<div class="p-2">
<Input placeholder="Search" v-model="filter" />
<Input placeholder="Search" v-model="filter"/>
</div>
<VList :data="virtualizedItems" #default="{ item }" class="relative" :style="{
<VList
:data="virtualizedItems"
#default="{ item }"
class="relative"
:style="{
width: `calc(var(--spacing) * ((12 * ${EMOJI_PER_ROW}) + (${EMOJI_PER_ROW} - 1)) + var(--spacing) * 4)`,
}">
<CategoryHeader :key="item.headerId" v-if="item.type === 'header'" :category-name="item.name" />
<div v-else-if="item.type === 'emoji-row'" :key="item.rowId" class="flex gap-1 p-2">
<Emoji v-for="emoji in item.emojis" :key="getEmojiKey(emoji)" :emoji="emoji"
@select="(e) => selectedEmoji = e" @pick="e => {
}"
>
<CategoryHeader
:key="item.headerId"
v-if="item.type === 'header'"
:category-name="item.name"
/>
<div
v-else-if="item.type === 'emoji-row'"
:key="item.rowId"
class="flex gap-1 p-2"
>
<Emoji
v-for="emoji in item.emojis"
:key="getEmojiKey(emoji)"
:emoji="emoji"
@select="(e) => selectedEmoji = e"
@pick="e => {
emit('pick', e); open = false;
}" />
}"
/>
</div>
</VList>
<EmojiDisplay :emoji="selectedEmoji" :style="{
<EmojiDisplay
:emoji="selectedEmoji"
:style="{
width: `calc(var(--spacing) * ((12 * ${EMOJI_PER_ROW}) + (${EMOJI_PER_ROW} - 1)) + var(--spacing) * 4)`,
}" />
}"
/>
</div>
<Sidebar :categories="categories" @select="scrollToCategory" />
<Sidebar :categories="categories" @select="scrollToCategory"/>
</div>
</PopoverContent>
</Popover>

View file

@ -1,8 +1,23 @@
<template>
<div class="grid gap-1 bg-transparent p-2">
<Button v-for="category in categories" :key="category.name" size="icon" variant="ghost" @click="() => emit('select', category)">
<component v-if="category.groupId" :is="emojiGroupIconMap[category.groupId]" class="size-6 text-primary" />
<img v-else-if="category.src" :src="category.src" class="size-6 align-middle inline not-prose" role="presentation" />
<Button
v-for="category in categories"
:key="category.name"
size="icon"
variant="ghost"
@click="() => emit('select', category)"
>
<component
v-if="category.groupId"
:is="emojiGroupIconMap[category.groupId]"
class="size-6 text-primary"
/>
<img
v-else-if="category.src"
:src="category.src"
class="size-6 align-middle inline not-prose"
role="presentation"
>
</Button>
</div>
</template>

View file

@ -1,23 +1,38 @@
<template>
<HoverCard @update:open="(open) => open && accounts === null && refreshReactions()">
<HoverCard
@update:open="(open) => open && accounts === null && refreshReactions()"
>
<HoverCardTrigger as-child>
<Button @click="reaction.me ? !reaction.remote && unreact() : !reaction.remote && react()" :variant="reaction.me ? 'secondary' : reaction.remote ? 'ghost' : 'outline'" size="sm" class="gap-2">
<img v-if="emoji" :src="emoji.url" :alt="emoji.shortcode"
class="h-[1lh] align-middle inline not-prose" />
<span v-else>
{{ reaction.name }}
</span>
<Button
@click="reaction.me ? !reaction.remote && unreact() : !reaction.remote && react()"
:variant="reaction.me ? 'secondary' : reaction.remote ? 'ghost' : 'outline'"
size="sm"
class="gap-2"
>
<img
v-if="emoji"
:src="emoji.url"
:alt="emoji.shortcode"
class="h-[1lh] align-middle inline not-prose"
>
<span v-else> {{ reaction.name }}</span>
{{ formatNumber(reaction.count) }}
</Button>
</HoverCardTrigger>
<HoverCardContent class="p-3">
<Spinner v-if="accounts === null" class="border-0" />
<Spinner v-if="accounts === null" class="border-0"/>
<ul v-else class="flex flex-col gap-4">
<li
v-for="account in accounts">
<NuxtLink :to="`/@${account.acct}`" class="flex items-center gap-2">
<Avatar class="size-6" :key="account.id" :src="account.avatar"
:name="account.display_name || account.username" />
<li v-for="account in accounts">
<NuxtLink
:to="`/@${account.acct}`"
class="flex items-center gap-2"
>
<Avatar
class="size-6"
:key="account.id"
:src="account.avatar"
:name="account.display_name || account.username"
/>
<span class="text-sm font-semibold line-clamp-1">
{{ account.display_name || account.username }}
</span>

View file

@ -1,9 +1,13 @@
<template>
<NuxtLink :href="urlAsPath">
<Card class="flex-row px-2 py-1 items-center gap-2 hover:bg-muted duration-100 text-sm">
<Repeat class="size-4 text-primary" />
<Avatar class="size-6 border" :src="avatar" :name="displayName" />
<span class="font-semibold" v-render-emojis="emojis">{{ displayName }}</span>
<Card
class="flex-row px-2 py-1 items-center gap-2 hover:bg-muted duration-100 text-sm"
>
<Repeat class="size-4 text-primary"/>
<Avatar class="size-6 border" :src="avatar" :name="displayName"/>
<span class="font-semibold" v-render-emojis="emojis"
>{{ displayName }}</span
>
{{ m.large_vivid_horse_catch() }}
</Card>
</NuxtLink>