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,8 +1,6 @@
<template> <template>
<TooltipProvider> <TooltipProvider>
<Component is="style"> <Component is="style">{{ preferences.custom_css }}</Component>
{{ preferences.custom_css }}
</Component>
<NuxtPwaAssets/> <NuxtPwaAssets/>
<NuxtLayout> <NuxtLayout>
<NuxtPage/> <NuxtPage/>
@ -91,7 +89,10 @@ body {
html.theme-changing * { html.theme-changing * {
/* Stroke and fill aren't animatable */ /* Stroke and fill aren't animatable */
transition: background-color 1s ease, border 1s ease, color 1s ease, transition:
background-color 1s ease,
border 1s ease,
color 1s ease,
box-shadow 1s ease !important; box-shadow 1s ease !important;
} }

View file

@ -3,50 +3,76 @@
<Note :note="relation.note" :hide-actions="true" :small-layout="true"/> <Note :note="relation.note" :hide-actions="true" :small-layout="true"/>
</div> </div>
<InputGroup class="p-1"> <InputGroup class="p-1">
<InputGroupAddon v-if="store.sensitive" align="block-start" class="pt-3"> <InputGroupAddon
<Input v-model:model-value="store.contentWarning" placeholder="Put your content warning here" /> v-if="store.sensitive"
align="block-start"
class="pt-3"
>
<Input
v-model:model-value="store.contentWarning"
placeholder="Put your content warning here"
/>
</InputGroupAddon> </InputGroupAddon>
<EditorContent data-slot="input-group-control" @paste-files="uploadFiles" v-model:content="store.content" <EditorContent
v-model:raw-content="store.rawContent" :placeholder="getRandomSplash()" data-slot="input-group-control"
@paste-files="uploadFiles"
v-model:content="store.content"
v-model:raw-content="store.rawContent"
:placeholder="getRandomSplash()"
class=" placeholder:text-muted-foreground flex field-sizing-content min-h-58 w-full px-4 text-base disabled:opacity-50 md:text-sm flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none" class=" placeholder:text-muted-foreground flex field-sizing-content min-h-58 w-full px-4 text-base disabled:opacity-50 md:text-sm flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none"
:disabled="store.sending" :mode="store.contentType === 'text/html' ? 'rich' : 'plain'" /> :disabled="store.sending"
:mode="store.contentType === 'text/html' ? 'rich' : 'plain'"
/>
<InputGroupAddon v-if="store.files.length > 0" align="block-end" class="overflow-x-auto *:shrink-0"> <InputGroupAddon
v-if="store.files.length > 0"
align="block-end"
class="overflow-x-auto *:shrink-0"
>
<Files v-model:files="store.files" :composer-key="composerKey"/> <Files v-model:files="store.files" :composer-key="composerKey"/>
</InputGroupAddon> </InputGroupAddon>
<InputGroupAddon align="block-end"> <InputGroupAddon align="block-end">
<Select v-model:model-value="store.contentType"> <Select v-model:model-value="store.contentType">
<SelectTrigger as-child disable-default-classes disable-select-icon> <SelectTrigger
as-child
disable-default-classes
disable-select-icon
>
<InputGroupButton variant="ghost" size="icon-sm"> <InputGroupButton variant="ghost" size="icon-sm">
<LetterText v-if="store.contentType === 'text/html'"/> <LetterText v-if="store.contentType === 'text/html'"/>
<Type v-else/> <Type v-else/>
</InputGroupButton> </InputGroupButton>
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="text/plain"> <SelectItem value="text/plain">Plain text</SelectItem>
Plain text <SelectItem value="text/html">Rich text</SelectItem>
</SelectItem>
<SelectItem value="text/html">
Rich text
</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
<VisibilityPicker v-model:visibility="store.visibility"> <VisibilityPicker v-model:visibility="store.visibility">
<InputGroupButton variant="ghost" size="icon-sm" :disabled="store.relation?.type === 'edit'"> <InputGroupButton
variant="ghost"
size="icon-sm"
:disabled="store.relation?.type === 'edit'"
>
<component :is="visibilities[store.visibility].icon"/> <component :is="visibilities[store.visibility].icon"/>
</InputGroupButton> </InputGroupButton>
</VisibilityPicker> </VisibilityPicker>
<InputGroupButton variant="ghost" size="icon-sm" @click="fileInput?.click()"> <InputGroupButton
variant="ghost"
size="icon-sm"
@click="fileInput?.click()"
>
<FilePlus2/> <FilePlus2/>
</InputGroupButton> </InputGroupButton>
<Toggle size="sm" v-model="store.sensitive"> <Toggle size="sm" v-model="store.sensitive">
<TriangleAlert/> <TriangleAlert/>
</Toggle> </Toggle>
<InputGroupText :class="['ml-auto', charactersLeft < 0 && 'text-destructive']"> <InputGroupText
:class="['ml-auto', charactersLeft < 0 && 'text-destructive']"
>
{{ charactersLeft.toLocaleString(getLocale(), { {{ charactersLeft.toLocaleString(getLocale(), {
maximumFractionDigits: 2, maximumFractionDigits: 2,
notation: 'compact', notation: 'compact',
@ -54,8 +80,12 @@
}) }} }) }}
</InputGroupText> </InputGroupText>
<Separator orientation="vertical" class="h-4!"/> <Separator orientation="vertical" class="h-4!"/>
<InputGroupButton variant="default" size="icon-sm" :disabled="store.sending || !store.canSend" <InputGroupButton
@click="send"> variant="default"
size="icon-sm"
:disabled="store.sending || !store.canSend"
@click="send"
>
<Spinner v-if="store.sending"/> <Spinner v-if="store.sending"/>
<ArrowUp v-else/> <ArrowUp v-else/>
<span class="sr-only">Send</span> <span class="sr-only">Send</span>
@ -63,7 +93,13 @@
</InputGroupAddon> </InputGroupAddon>
</InputGroup> </InputGroup>
<input type="file" ref="fileInput" @change="uploadFileFromEvent" class="hidden" multiple /> <input
type="file"
ref="fileInput"
@change="uploadFileFromEvent"
class="hidden"
multiple
>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View file

@ -5,18 +5,29 @@
:disabled="file.uploading || file.updating" :disabled="file.uploading || file.updating"
class="block bg-card text-card-foreground shadow-sm h-28 overflow-hidden rounded relative min-w-28 *:disabled:opacity-50" class="block bg-card text-card-foreground shadow-sm h-28 overflow-hidden rounded relative min-w-28 *:disabled:opacity-50"
> >
<img v-if="file.file?.type.startsWith('image/')" :src="createObjectURL(file.file)" class="object-contain h-28 w-full" :alt="file.alt" /> <img
v-if="file.file?.type.startsWith('image/')"
:src="createObjectURL(file.file)"
class="object-contain h-28 w-full"
:alt="file.alt"
>
<FileIcon v-else class="size-6 m-auto text-muted-foreground"/> <FileIcon v-else class="size-6 m-auto text-muted-foreground"/>
<Badge <Badge
v-if="file.file && !(file.uploading || file.updating)" v-if="file.file && !(file.uploading || file.updating)"
class="absolute bottom-1 right-1" class="absolute bottom-1 right-1"
variant="default" variant="default"
>{{ formatBytes(file.file.size) }}</Badge
> >
<Spinner v-else-if="file.file" class="absolute bottom-1 right-1 size-8 p-1.5" /> {{ formatBytes(file.file.size) }}
</Badge>
<Spinner
v-else-if="file.file"
class="absolute bottom-1 right-1 size-8 p-1.5"
/>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent class="min-w-48"> <DropdownMenuContent class="min-w-48">
<DropdownMenuLabel v-if="file.file">{{ file.file.name }}</DropdownMenuLabel> <DropdownMenuLabel v-if="file.file">
{{ file.file.name }}
</DropdownMenuLabel>
<DropdownMenuSeparator/> <DropdownMenuSeparator/>
<DropdownMenuItem @click="editCaption"> <DropdownMenuItem @click="editCaption">
<Captions/> <Captions/>

View file

@ -1,6 +1,12 @@
<template> <template>
<FilePreview v-for="(file, index) in files" :key="file.apiId" :file="file" @update:file="files[index] = $event" :composer-key="composerKey" <FilePreview
@remove="files.splice(index, 1)" /> v-for="(file, index) in files"
:key="file.apiId"
:file="file"
@update:file="files[index] = $event"
:composer-key="composerKey"
@remove="files.splice(index, 1)"
/>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View file

@ -4,8 +4,15 @@
<slot/> <slot/>
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem v-for="(v, k) in visibilities" :key="k" @click="visibility = k" :value="k"> <SelectItem
<div class="flex flex-row gap-3 items-center w-full justify-between"> v-for="(v, k) in visibilities"
:key="k"
@click="visibility = k"
:value="k"
>
<div
class="flex flex-row gap-3 items-center w-full justify-between"
>
<component :is="v.icon" class="size-4"/> <component :is="v.icon" class="size-4"/>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<span class="font-semibold">{{ v.name }}</span> <span class="font-semibold">{{ v.name }}</span>

View file

@ -69,7 +69,8 @@ watch(active, (value) => {
<template> <template>
<BubbleMenu :editor="editor"> <BubbleMenu :editor="editor">
<ToggleGroup type="multiple" <ToggleGroup
type="multiple"
v-model="active" v-model="active"
class="bg-popover rounded-md" class="bg-popover rounded-md"
> >

View file

@ -1,8 +1,6 @@
<template> <template>
<BubbleMenu :editor="editor"/> <BubbleMenu :editor="editor"/>
<EditorContent :editor="editor" <EditorContent :editor="editor" v-bind="$attrs" :class="$style.content"/>
v-bind="$attrs"
:class="$style.content" />
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View file

@ -1,10 +1,23 @@
<template> <template>
<Command class="rounded border shadow-md min-w-[200px] h-fit not-prose" :selected-value="emojis[selectedIndex]?.id"> <Command
class="rounded border shadow-md min-w-[200px] h-fit not-prose"
:selected-value="emojis[selectedIndex]?.id"
>
<CommandList> <CommandList>
<CommandEmpty>No results found.</CommandEmpty> <CommandEmpty>No results found.</CommandEmpty>
<CommandGroup class="emojis-group" heading="Emojis"> <CommandGroup class="emojis-group" heading="Emojis">
<CommandItem :value="emoji.id" v-for="emoji, index in emojis" :key="emoji.id" @click="selectItem(index)" class="scroll-m-10"> <CommandItem
<img class="h-[1lh] align-middle inline hover:scale-110 transition-transform duration-75 ease-in-out" :src="emoji.url" :title="emoji.shortcode" /> :value="emoji.id"
v-for="emoji, index in emojis"
:key="emoji.id"
@click="selectItem(index)"
class="scroll-m-10"
>
<img
class="h-[1lh] align-middle inline hover:scale-110 transition-transform duration-75 ease-in-out"
:src="emoji.url"
:title="emoji.shortcode"
>
<span>{{ emoji.shortcode }}</span> <span>{{ emoji.shortcode }}</span>
</CommandItem> </CommandItem>
</CommandGroup> </CommandGroup>

View file

@ -1,11 +1,26 @@
<template> <template>
<Command class="rounded border shadow-md min-w-[200px] h-fit not-prose" :selected-value="items[selectedIndex]?.key"> <Command
class="rounded border shadow-md min-w-[200px] h-fit not-prose"
:selected-value="items[selectedIndex]?.key"
>
<CommandList> <CommandList>
<CommandEmpty>No results found.</CommandEmpty> <CommandEmpty>No results found.</CommandEmpty>
<CommandGroup class="mentions-group" heading="Users"> <CommandGroup class="mentions-group" heading="Users">
<CommandItem :value="user.key" v-for="user, index in items" :key="user.key" @click="selectItem(index)" class="scroll-m-10"> <CommandItem
<Avatar class="size-4" :src="user.value.avatar" :name="user.value.display_name" /> :value="user.key"
<span v-render-emojis="user.value.emojis">{{ user.value.display_name }}</span> v-for="user, index in items"
:key="user.key"
@click="selectItem(index)"
class="scroll-m-10"
>
<Avatar
class="size-4"
:src="user.value.avatar"
:name="user.value.display_name"
/>
<span v-render-emojis="user.value.emojis"
>{{ user.value.display_name }}</span
>
</CommandItem> </CommandItem>
</CommandGroup> </CommandGroup>
</CommandList> </CommandList>

View file

@ -1,9 +1,7 @@
<template> <template>
<Alert> <Alert>
<AlertTitle>{{ m.fine_arable_lemming_fold() }}</AlertTitle> <AlertTitle>{{ m.fine_arable_lemming_fold() }}</AlertTitle>
<AlertDescription> <AlertDescription>{{ m.petty_honest_fish_stir() }}</AlertDescription>
{{ m.petty_honest_fish_stir() }}
</AlertDescription>
</Alert> </Alert>
</template> </template>

View file

@ -1,9 +1,7 @@
<template> <template>
<Alert> <Alert>
<AlertTitle>{{ m.empty_awful_lark_dart() }}</AlertTitle> <AlertTitle>{{ m.empty_awful_lark_dart() }}</AlertTitle>
<AlertDescription> <AlertDescription>{{ m.clean_even_mayfly_tap() }}</AlertDescription>
{{ m.clean_even_mayfly_tap() }}
</AlertDescription>
</Alert> </Alert>
</template> </template>

View file

@ -1,9 +1,7 @@
<template> <template>
<Alert> <Alert>
<AlertTitle>{{ m.steep_suave_fish_snap() }}</AlertTitle> <AlertTitle>{{ m.steep_suave_fish_snap() }}</AlertTitle>
<AlertDescription> <AlertDescription>{{ m.muddy_bland_shark_accept() }}</AlertDescription>
{{ m.muddy_bland_shark_accept() }}
</AlertDescription>
</Alert> </Alert>
</template> </template>

View file

@ -3,23 +3,33 @@ import SquarePattern from "../graphics/square-pattern.vue";
</script> </script>
<template> <template>
<div class="grid min-h-screen place-items-center px-6 py-24 sm:py-32 lg:px-8 fixed inset-0 z-[1000000] bg-dark-900"> <div
class="grid min-h-screen place-items-center px-6 py-24 sm:py-32 lg:px-8 fixed inset-0 z-[1000000] bg-dark-900"
>
<SquarePattern/> <SquarePattern/>
<div class="prose prose-invert max-w-lg"> <div class="prose prose-invert max-w-lg">
<h1 class="mt-4 text-3xl font-bold tracking-tight text-gray-100 sm:text-5xl">JavaScript is disabled <h1
class="mt-4 text-3xl font-bold tracking-tight text-gray-100 sm:text-5xl"
>
JavaScript is disabled
</h1> </h1>
<p class="mt-6 text-base leading-7 text-gray-400"> <p class="mt-6 text-base leading-7 text-gray-400">
This website requires JavaScript to function properly. Please enable JavaScript in your browser This website requires JavaScript to function properly. Please
settings. enable JavaScript in your browser settings.
</p> </p>
<p class="mt-6 text-base leading-7 text-gray-400"> <p class="mt-6 text-base leading-7 text-gray-400">
If you are using a browser that does not support JavaScript, please consider using a modern browser If you are using a browser that does not support JavaScript,
like <a href="https://www.mozilla.org/firefox/new/" class="underline">Firefox</a> or <a please consider using a modern browser like
href="https://www.google.com/chrome/" class="underline">Chrome</a>. <a href="https://www.mozilla.org/firefox/new/" class="underline"
>Firefox</a
>or <a href="https://www.google.com/chrome/" class="underline"
>Chrome</a
>.
</p> </p>
<p class="mt-6 text-base leading-7 text-gray-400"> <p class="mt-6 text-base leading-7 text-gray-400">
This application does not track you, collect user data, use cookies of any kind or send requests to This application does not track you, collect user data, use
servers outside of your account's instance. cookies of any kind or send requests to servers outside of your
account's instance.
</p> </p>
</div> </div>
</div> </div>

View file

@ -1,11 +1,16 @@
<template> <template>
<Card> <Card>
<FormItem class="grid grid-cols-[minmax(0,1fr)_auto] items-center gap-2"> <FormItem
class="grid grid-cols-[minmax(0,1fr)_auto] items-center gap-2"
>
<CardHeader class="flex flex-col gap-1.5 p-0"> <CardHeader class="flex flex-col gap-1.5 p-0">
<FormLabel class="font-semibold tracking-tight" :as="CardTitle"> <FormLabel class="font-semibold tracking-tight" :as="CardTitle">
{{ title }} {{ title }}
</FormLabel> </FormLabel>
<FormDescription class="text-xs leading-none" v-if="description"> <FormDescription
class="text-xs leading-none"
v-if="description"
>
{{ description }} {{ description }}
</FormDescription> </FormDescription>
</CardHeader> </CardHeader>

View file

@ -1,14 +1,10 @@
<template> <template>
<FormItem> <FormItem>
<FormLabel> <FormLabel>{{ title }}</FormLabel>
{{ title }}
</FormLabel>
<FormControl> <FormControl>
<slot/> <slot/>
</FormControl> </FormControl>
<FormDescription v-if="description"> <FormDescription v-if="description">{{ description }}</FormDescription>
{{ description }}
</FormDescription>
<FormMessage/> <FormMessage/>
</FormItem> </FormItem>
</template> </template>

View file

@ -1,19 +1,34 @@
<template> <template>
<svg class="absolute inset-x-0 top-0 h-full w-full stroke-primary/[0.07] [mask-image:radial-gradient(100%_100%_at_top_right,var(--primary-foreground),transparent)] pointer-events-none" <svg
aria-hidden="true"> class="absolute inset-x-0 top-0 h-full w-full stroke-primary/[0.07] [mask-image:radial-gradient(100%_100%_at_top_right,var(--primary-foreground),transparent)] pointer-events-none"
aria-hidden="true"
>
<defs> <defs>
<pattern id="983e3e4c-de6d-4c3f-8d64-b9761d1534cc" width="200" height="200" x="50%" y="-1" <pattern
patternUnits="userSpaceOnUse"> id="983e3e4c-de6d-4c3f-8d64-b9761d1534cc"
width="200"
height="200"
x="50%"
y="-1"
patternUnits="userSpaceOnUse"
>
<path d="M.5 200V.5H200" fill="none"></path> <path d="M.5 200V.5H200" fill="none"></path>
</pattern> </pattern>
</defs><svg x="50%" y="-1" class="overflow-visible fill-primary/[0.03]"> </defs>
<path d="M-200 0h201v201h-201Z M600 0h201v201h-201Z M-400 600h201v201h-201Z M200 800h201v201h-201Z" <svg x="50%" y="-1" class="overflow-visible fill-primary/[0.03]">
stroke-width="0"></path> <path
d="M-200 0h201v201h-201Z M600 0h201v201h-201Z M-400 600h201v201h-201Z M200 800h201v201h-201Z"
stroke-width="0"
></path>
</svg> </svg>
<rect width="100%" height="100%" stroke-width="0" fill="url(#983e3e4c-de6d-4c3f-8d64-b9761d1534cc)"></rect> <rect
width="100%"
height="100%"
stroke-width="0"
fill="url(#983e3e4c-de6d-4c3f-8d64-b9761d1534cc)"
></rect>
</svg> </svg>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
</script> </script>

View file

@ -1,8 +1,11 @@
<template> <template>
<Card class="grid grid-cols-[auto_1fr] gap-2"> <Card class="grid grid-cols-[auto_1fr] gap-2">
<Avatar :src="instance.thumbnail?.url ?? <Avatar
:src="instance.thumbnail?.url ??
'https://cdn.versia.pub/branding/icon.svg' 'https://cdn.versia.pub/branding/icon.svg'
" :name="instance.title" /> "
:name="instance.title"
/>
<div class="grid text-sm leading-tight *:line-clamp-1"> <div class="grid text-sm leading-tight *:line-clamp-1">
<span class="truncate font-semibold"> <span class="truncate font-semibold">
{{ {{
@ -14,7 +17,6 @@
instance.versia_version || instance.version instance.versia_version || instance.version
}} }}
</span> </span>
</div> </div>
<h1 class="line-clamp-1 text-sm font-semibold col-span-2"> <h1 class="line-clamp-1 text-sm font-semibold col-span-2">
{{ {{

View file

@ -40,17 +40,35 @@ const inputValue = ref<string>("");
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div v-if="modalOptions.inputType === 'text'" class="grid gap-4 py-4"> <div
v-if="modalOptions.inputType === 'text'"
class="grid gap-4 py-4"
>
<div class="grid grid-cols-4 items-center gap-4"> <div class="grid grid-cols-4 items-center gap-4">
<Label for="confirmInput" class="text-right">{{ m.mean_mean_badger_inspire() }}</Label> <Label for="confirmInput" class="text-right">
<Input id="confirmInput" v-model="inputValue" class="col-span-3" /> {{ m.mean_mean_badger_inspire() }}
</Label>
<Input
id="confirmInput"
v-model="inputValue"
class="col-span-3"
/>
</div> </div>
</div> </div>
<div v-else-if="modalOptions.inputType === 'textarea'" class="grid gap-4 py-4"> <div
v-else-if="modalOptions.inputType === 'textarea'"
class="grid gap-4 py-4"
>
<div class="grid grid-cols-4 items-center gap-4"> <div class="grid grid-cols-4 items-center gap-4">
<Label for="confirmTextarea" class="text-right">{{ m.mean_mean_badger_inspire() }}</Label> <Label for="confirmTextarea" class="text-right">
<Textarea id="confirmTextarea" v-model="inputValue" class="col-span-3" /> {{ m.mean_mean_badger_inspire() }}
</Label>
<Textarea
id="confirmTextarea"
v-model="inputValue"
class="col-span-3"
/>
</div> </div>
</div> </div>
@ -58,10 +76,12 @@ const inputValue = ref<string>("");
<Button variant="outline" @click="() => emit('cancel')"> <Button variant="outline" @click="() => emit('cancel')">
{{ modalOptions.cancelText }} {{ modalOptions.cancelText }}
</Button> </Button>
<Button @click="() => emit('confirm', { <Button
@click="() => emit('confirm', {
confirmed: true, confirmed: true,
value: inputValue, value: inputValue,
})"> })"
>
{{ modalOptions.confirmText }} {{ modalOptions.confirmText }}
</Button> </Button>
</DialogFooter> </DialogFooter>

View file

@ -75,7 +75,11 @@ const isValid = ref(false);
</script> </script>
<template> <template>
<AlertDialog :key="String(isOpen)" :open="isOpen" @update:open="isOpen = false"> <AlertDialog
:key="String(isOpen)"
:open="isOpen"
@update:open="isOpen = false"
>
<AlertDialogContent class="sm:max-w-[425px] flex flex-col"> <AlertDialogContent class="sm:max-w-[425px] flex flex-col">
<AlertDialogHeader> <AlertDialogHeader>
<AlertDialogTitle>{{ modalOptions.title }}</AlertDialogTitle> <AlertDialogTitle>{{ modalOptions.title }}</AlertDialogTitle>
@ -84,11 +88,23 @@ const isValid = ref(false);
</AlertDialogDescription> </AlertDialogDescription>
</AlertDialogHeader> </AlertDialogHeader>
<Input v-if="modalOptions.inputType === 'text'" v-model="inputValue" /> <Input
v-if="modalOptions.inputType === 'text'"
v-model="inputValue"
/>
<UrlInput v-if="modalOptions.inputType === 'url'" v-model="inputValue" placeholder="google.com" v-model:is-valid="isValid" /> <UrlInput
v-if="modalOptions.inputType === 'url'"
v-model="inputValue"
placeholder="google.com"
v-model:is-valid="isValid"
/>
<Textarea v-else-if="modalOptions.inputType === 'textarea'" v-model="inputValue" rows="6" /> <Textarea
v-else-if="modalOptions.inputType === 'textarea'"
v-model="inputValue"
rows="6"
/>
<AlertDialogFooter class="w-full"> <AlertDialogFooter class="w-full">
<AlertDialogCancel :as-child="true"> <AlertDialogCancel :as-child="true">
@ -97,7 +113,10 @@ const isValid = ref(false);
</Button> </Button>
</AlertDialogCancel> </AlertDialogCancel>
<AlertDialogAction :as-child="true"> <AlertDialogAction :as-child="true">
<Button @click="handleConfirm" :disabled="!isValid && modalOptions.inputType === 'url'"> <Button
@click="handleConfirm"
:disabled="!isValid && modalOptions.inputType === 'url'"
>
{{ modalOptions.confirmText }} {{ modalOptions.confirmText }}
</Button> </Button>
</AlertDialogAction> </AlertDialogAction>

View file

@ -1,9 +1,15 @@
<template> <template>
<Tabs v-model:model-value="current"> <Tabs v-model:model-value="current">
<TabsList> <TabsList>
<TabsTrigger v-for="timeline in timelines.filter( <TabsTrigger
v-for="timeline in timelines.filter(
i => i.requiresLogin ? authStore.isSignedIn : true, i => i.requiresLogin ? authStore.isSignedIn : true,
)" :key="timeline.value" :value="timeline.value" :as="NuxtLink" :href="timeline.url"> )"
:key="timeline.value"
:value="timeline.value"
:as="NuxtLink"
:href="timeline.url"
>
{{ timeline.name }} {{ timeline.name }}
</TabsTrigger> </TabsTrigger>
</TabsList> </TabsList>

View file

@ -1,20 +1,58 @@
<template> <template>
<div class="flex flex-row w-full max-w-sm items-stretch justify-between"> <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) }} {{ numberFormat(replyCount) }}
</ActionButton> </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) }} {{ numberFormat(likeCount) }}
</ActionButton> </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) }} {{ numberFormat(reblogCount) }}
</ActionButton> </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"> <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> </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')"> <Menu
<ActionButton :icon="Ellipsis" :title="m.busy_merry_cowfish_absorb()" /> :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> </Menu>
</div> </div>
</template> </template>

View file

@ -1,7 +1,16 @@
<template> <template>
<ImageAttachment v-if="attachment.type === 'image'" :attachment="attachment" /> <ImageAttachment
<VideoAttachment v-else-if="attachment.type === 'video' || attachment.type === 'gifv'" :attachment="attachment" /> v-if="attachment.type === 'image'"
<AudioAttachment v-else-if="attachment.type === 'audio'" :attachment="attachment" /> :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"/> <FileAttachment v-else :attachment="attachment"/>
</template> </template>

View file

@ -1,7 +1,13 @@
<template> <template>
<!-- [&:has(>:last-child:nth-child(1))] means "when this element has 1 child" --> <!-- [&: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"> <div
<Attachment v-for="attachment in attachments" :key="attachment.id" :attachment="attachment" /> 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> </div>
</template> </template>

View file

@ -1,6 +1,8 @@
<template> <template>
<Dialog> <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"> <DialogTrigger v-if="lightbox" :as-child="true">
<slot/> <slot/>
</DialogTrigger> </DialogTrigger>
@ -9,7 +11,11 @@
<Popover v-if="attachment.description"> <Popover v-if="attachment.description">
<div class="absolute top-0 right-0 p-2"> <div class="absolute top-0 right-0 p-2">
<PopoverTrigger :as-child="true"> <PopoverTrigger :as-child="true">
<Button variant="outline" size="icon" title="View alt text"> <Button
variant="outline"
size="icon"
title="View alt text"
>
<Captions/> <Captions/>
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
@ -19,13 +25,24 @@
</PopoverContent> </PopoverContent>
</Popover> </Popover>
</Card> </Card>
<DialogContent :hide-close="true" <DialogContent
class="duration-200 bg-transparent border-none overflow-hidden !animate-none gap-6 w-screen h-screen !max-w-none"> :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="grid grid-rows-[auto_1fr_auto]">
<div class="flex flex-row gap-2 w-full"> <div class="flex flex-row gap-2 w-full">
<DialogTitle class="sr-only">{{ attachment.type }}</DialogTitle> <DialogTitle class="sr-only">
<Button as="a" :href="attachment?.url" target="_blank" :download="true" variant="outline" size="icon" {{ attachment.type }}
class="ml-auto"> </DialogTitle>
<Button
as="a"
:href="attachment?.url"
target="_blank"
:download="true"
variant="outline"
size="icon"
class="ml-auto"
>
<Download/> <Download/>
</Button> </Button>
<DialogClose :as-child="true"> <DialogClose :as-child="true">
@ -34,11 +51,16 @@
</Button> </Button>
</DialogClose> </DialogClose>
</div> </div>
<div class="flex items-center justify-center overflow-hidden *:max-h-[80vh] *:max-w-[80vw] *:w-full *:h-full *:object-contain"> <div
class="flex items-center justify-center overflow-hidden *:max-h-[80vh] *:max-w-[80vw] *:w-full *:h-full *:object-contain"
>
<slot/> <slot/>
</div> </div>
<DialogDescription class="flex items-center justify-center"> <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> <p>{{ attachment.description }}</p>
</Card> </Card>
</DialogDescription> </DialogDescription>

View file

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

View file

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

View file

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

View file

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

View file

@ -3,10 +3,18 @@
<p class="text-sm leading-6 wrap-anywhere"> <p class="text-sm leading-6 wrap-anywhere">
{{ contentWarning || m.sour_seemly_bird_hike() }} {{ contentWarning || m.sour_seemly_bird_hike() }}
</p> </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() : {{ hidden ? m.bald_direct_turtle_win() :
m.known_flaky_cockroach_dash() }} {{ characterCount > 0 ? ` (${characterCount} characters` : "" }}{{ m.known_flaky_cockroach_dash() }}
attachmentCount > 0 ? `${characterCount > 0 ? " · " : " ("}${attachmentCount} file(s)` : "" }}{{ (characterCount > 0 || attachmentCount > 0) ? ")" : "" }} {{ characterCount > 0 ? ` (${characterCount} characters` : "" }}
{{
attachmentCount > 0 ? `${characterCount > 0 ? " · " : " ("}${attachmentCount} file(s)` : "" }}
{{ (characterCount > 0 || attachmentCount > 0) ? ")" : "" }}
</Button> </Button>
</div> </div>
</template> </template>

View file

@ -1,11 +1,25 @@
<template> <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> <Prose v-html="content" v-render-emojis="emojis"></Prose>
</OverflowGuard> </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"> <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"/>

View file

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

View file

@ -83,7 +83,11 @@ const _delete = async () => {
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent class="min-w-56"> <DropdownMenuContent class="min-w-56">
<DropdownMenuGroup> <DropdownMenuGroup>
<DropdownMenuItem v-if="authorIsMe" as="button" @click="emit('edit')"> <DropdownMenuItem
v-if="authorIsMe"
as="button"
@click="emit('edit')"
>
<Pencil/> <Pencil/>
{{ m.front_lime_grizzly_persist() }} {{ m.front_lime_grizzly_persist() }}
</DropdownMenuItem> </DropdownMenuItem>
@ -102,11 +106,21 @@ const _delete = async () => {
<Link/> <Link/>
{{ m.ago_new_pelican_drip() }} {{ m.ago_new_pelican_drip() }}
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem as="button" v-if="isRemote && remoteUrl" @click="copyText(remoteUrl)"> <DropdownMenuItem
as="button"
v-if="isRemote && remoteUrl"
@click="copyText(remoteUrl)"
>
<Link/> <Link/>
{{ m.solid_witty_zebra_walk() }} {{ m.solid_witty_zebra_walk() }}
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem as="a" v-if="isRemote" target="_blank" rel="noopener noreferrer" :href="remoteUrl"> <DropdownMenuItem
as="a"
v-if="isRemote"
target="_blank"
rel="noopener noreferrer"
:href="remoteUrl"
>
<ExternalLink/> <ExternalLink/>
{{ m.active_trite_lark_inspire() }} {{ m.active_trite_lark_inspire() }}
</DropdownMenuItem> </DropdownMenuItem>

View file

@ -50,7 +50,12 @@
:sensitive="noteToUse.sensitive" :sensitive="noteToUse.sensitive"
:content-warning="noteToUse.spoiler_text" :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> </CardContent>
<CardFooter v-if="!hideActions"> <CardFooter v-if="!hideActions">
<Actions <Actions

View file

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

View file

@ -1,8 +1,10 @@
<template> <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', '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, $style.content,
]"> ]"
>
<slot/> <slot/>
</div> </div>
</template> </template>

View file

@ -1,6 +1,12 @@
<template> <template>
<div class="flex flex-row gap-2 flex-wrap"> <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> </div>
</template> </template>

View file

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

View file

@ -1,8 +1,17 @@
<template> <template>
<div class="p-2 text-sm font-semibold border-0 rounded-none text-center flex flex-row items-center gap-2 truncate"> <div
<img v-if="(emoji as InferredEmoji)?.url" :src="(emoji as InferredEmoji)?.url" class="p-2 text-sm font-semibold border-0 rounded-none text-center flex flex-row items-center gap-2 truncate"
: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"> <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 }} {{ (emoji as UnicodeEmoji)?.unicode }}
</span> </span>
{{ (emoji as InferredEmoji)?.shortcode || (emoji as UnicodeEmoji)?.shortcode }} {{ (emoji as InferredEmoji)?.shortcode || (emoji as UnicodeEmoji)?.shortcode }}

View file

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

View file

@ -4,26 +4,52 @@
<slot/> <slot/>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent class="p-0 w-fit"> <PopoverContent class="p-0 w-fit">
<div class="grid-cols-[minmax(0,1fr)_auto] gap-0 grid divide-x *:h-112 *:overflow-y-auto" <div
orientation="vertical"> class="grid-cols-[minmax(0,1fr)_auto] gap-0 grid divide-x *:h-112 *:overflow-y-auto"
<div class="grid grid-rows-[auto_minmax(0,1fr)_auto] gap-0" ref="emojiContainer"> orientation="vertical"
>
<div
class="grid grid-rows-[auto_minmax(0,1fr)_auto] gap-0"
ref="emojiContainer"
>
<div class="p-2"> <div class="p-2">
<Input placeholder="Search" v-model="filter"/> <Input placeholder="Search" v-model="filter"/>
</div> </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)`, 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"> <CategoryHeader
<Emoji v-for="emoji in item.emojis" :key="getEmojiKey(emoji)" :emoji="emoji" :key="item.headerId"
@select="(e) => selectedEmoji = e" @pick="e => { 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; emit('pick', e); open = false;
}" /> }"
/>
</div> </div>
</VList> </VList>
<EmojiDisplay :emoji="selectedEmoji" :style="{ <EmojiDisplay
:emoji="selectedEmoji"
:style="{
width: `calc(var(--spacing) * ((12 * ${EMOJI_PER_ROW}) + (${EMOJI_PER_ROW} - 1)) + var(--spacing) * 4)`, width: `calc(var(--spacing) * ((12 * ${EMOJI_PER_ROW}) + (${EMOJI_PER_ROW} - 1)) + var(--spacing) * 4)`,
}" /> }"
/>
</div> </div>
<Sidebar :categories="categories" @select="scrollToCategory"/> <Sidebar :categories="categories" @select="scrollToCategory"/>
</div> </div>

View file

@ -1,8 +1,23 @@
<template> <template>
<div class="grid gap-1 bg-transparent p-2"> <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)"> <Button
<component v-if="category.groupId" :is="emojiGroupIconMap[category.groupId]" class="size-6 text-primary" /> v-for="category in categories"
<img v-else-if="category.src" :src="category.src" class="size-6 align-middle inline not-prose" role="presentation" /> :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> </Button>
</div> </div>
</template> </template>

View file

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

View file

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

View file

@ -1,12 +1,25 @@
<template> <template>
<div v-if="relationship?.requested_by !== false" class="flex flex-row items-center gap-3 p-4"> <div
v-if="relationship?.requested_by !== false"
class="flex flex-row items-center gap-3 p-4"
>
<NuxtLink :href="followerUrl" class="relative size-10"> <NuxtLink :href="followerUrl" class="relative size-10">
<Avatar class="size-10 border border-border" :src="follower.avatar" :name="follower.display_name" /> <Avatar
class="size-10 border border-border"
:src="follower.avatar"
:name="follower.display_name"
/>
</NuxtLink> </NuxtLink>
<div class="flex flex-col gap-0.5 justify-center flex-1 text-left leading-tight text-sm"> <div
<span class="truncate font-semibold" v-render-emojis="follower.emojis">{{ class="flex flex-col gap-0.5 justify-center flex-1 text-left leading-tight text-sm"
>
<span
class="truncate font-semibold"
v-render-emojis="follower.emojis"
>{{
follower.display_name follower.display_name
}}</span> }}</span
>
<span class="truncate tracking-tight"> <span class="truncate tracking-tight">
<Address :username="username" :domain="domain"/> <Address :username="username" :domain="domain"/>
</span> </span>
@ -15,14 +28,27 @@
<div v-if="loading" class="flex p-2 items-center justify-center h-12"> <div v-if="loading" class="flex p-2 items-center justify-center h-12">
<Loader class="size-4 animate-spin"/> <Loader class="size-4 animate-spin"/>
</div> </div>
<div v-else-if="relationship?.requested_by === false" class="flex p-2 items-center justify-center h-12"> <div
v-else-if="relationship?.requested_by === false"
class="flex p-2 items-center justify-center h-12"
>
<Check class="size-4"/> <Check class="size-4"/>
</div> </div>
<div v-else class="grid grid-cols-2 p-2 gap-2"> <div v-else class="grid grid-cols-2 p-2 gap-2">
<Button variant="secondary" size="sm" @click="accept" :title="m.slow_these_kestrel_sail()"> <Button
variant="secondary"
size="sm"
@click="accept"
:title="m.slow_these_kestrel_sail()"
>
<Check/> <Check/>
</Button> </Button>
<Button variant="ghost" size="sm" @click="reject" :title="m.weary_steep_yak_embrace()"> <Button
variant="ghost"
size="sm"
@click="reject"
:title="m.weary_steep_yak_embrace()"
>
<X/> <X/>
</Button> </Button>
</div> </div>

View file

@ -1,17 +1,43 @@
<template> <template>
<section class="space-y-2"> <section class="space-y-2">
<CardTitle class="text-xs"> <CardTitle class="text-xs">{{ name }}</CardTitle>
{{ name }}
</CardTitle>
<Card class="p-0 gap-0"> <Card class="p-0 gap-0">
<div v-for="preference of preferences" :key="preference"> <div v-for="preference of preferences" :key="preference">
<TextPreferenceVue v-if="(prefs[preference] instanceof TextPreference)" :pref="(prefs[preference] as TextPreference)" :name="preference" /> <TextPreferenceVue
<BooleanPreferenceVue v-else-if="(prefs[preference] instanceof BooleanPreference)" :pref="(prefs[preference] as BooleanPreference)" :name="preference" /> v-if="(prefs[preference] instanceof TextPreference)"
<SelectPreferenceVue v-else-if="(prefs[preference] instanceof SelectPreference)" :pref="(prefs[preference] as SelectPreference<string>)" :name="preference" /> :pref="(prefs[preference] as TextPreference)"
<NumberPreferenceVue v-else-if="(prefs[preference] instanceof NumberPreference)" :pref="(prefs[preference] as NumberPreference)" :name="preference" /> :name="preference"
<MultiSelectPreferenceVue v-else-if="(prefs[preference] instanceof MultiSelectPreference)" :pref="(prefs[preference] as MultiSelectPreference<string>)" :name="preference" /> />
<CodePreferenceVue v-else-if="(prefs[preference] instanceof CodePreference)" :pref="(prefs[preference] as CodePreference)" :name="preference" /> <BooleanPreferenceVue
<UrlPreferenceVue v-else-if="(prefs[preference] instanceof UrlPreference)" :pref="(prefs[preference] as UrlPreference)" :name="preference" /> v-else-if="(prefs[preference] instanceof BooleanPreference)"
:pref="(prefs[preference] as BooleanPreference)"
:name="preference"
/>
<SelectPreferenceVue
v-else-if="(prefs[preference] instanceof SelectPreference)"
:pref="(prefs[preference] as SelectPreference<string>)"
:name="preference"
/>
<NumberPreferenceVue
v-else-if="(prefs[preference] instanceof NumberPreference)"
:pref="(prefs[preference] as NumberPreference)"
:name="preference"
/>
<MultiSelectPreferenceVue
v-else-if="(prefs[preference] instanceof MultiSelectPreference)"
:pref="(prefs[preference] as MultiSelectPreference<string>)"
:name="preference"
/>
<CodePreferenceVue
v-else-if="(prefs[preference] instanceof CodePreference)"
:pref="(prefs[preference] as CodePreference)"
:name="preference"
/>
<UrlPreferenceVue
v-else-if="(prefs[preference] instanceof UrlPreference)"
:pref="(prefs[preference] as UrlPreference)"
:name="preference"
/>
</div> </div>
</Card> </Card>
</section> </section>

View file

@ -1,11 +1,15 @@
<template> <template>
<Card class="grid gap-3 text-sm"> <Card class="grid gap-3 text-sm">
<dl class="grid gap-3"> <dl class="grid gap-3">
<div v-for="[key, value] of data" :key="key" class="flex flex-row items-baseline justify-between gap-4 truncate"> <div
<dt class="text-muted-foreground"> v-for="[key, value] of data"
{{ key }} :key="key"
</dt> class="flex flex-row items-baseline justify-between gap-4 truncate"
<dd class="font-mono" v-if="typeof value === 'string'">{{ value }}</dd> >
<dt class="text-muted-foreground">{{ key }}</dt>
<dd class="font-mono" v-if="typeof value === 'string'">
{{ value }}
</dd>
<dd class="font-mono" v-else> <dd class="font-mono" v-else>
<component :is="value"/> <component :is="value"/>
</dd> </dd>

View file

@ -77,31 +77,55 @@ useListen("preferences:open", () => {
<template> <template>
<Dialog v-model:open="open" v-if="authStore.isSignedIn"> <Dialog v-model:open="open" v-if="authStore.isSignedIn">
<DialogContent class="md:max-w-5xl w-full h-full p-0 md:max-h-[70dvh] overflow-hidden"> <DialogContent
<Tabs class="md:grid-cols-[auto_minmax(0,1fr)] !grid gap-2 *:p-4 overflow-hidden *:overflow-y-auto *:h-full" orientation="vertical" class="md:max-w-5xl w-full h-full p-0 md:max-h-[70dvh] overflow-hidden"
:default-value="pages[0]"> >
<DialogHeader class="gap-6 grid grid-rows-[auto_minmax(0,1fr)] border-b md:border-b-0 md:border-r min-w-60 text-left"> <Tabs
<div class="grid gap-3 items-center grid-cols-[auto_minmax(0,1fr)]"> class="md:grid-cols-[auto_minmax(0,1fr)] !grid gap-2 *:p-4 overflow-hidden *:overflow-y-auto *:h-full"
<Avatar :name="authStore.account!.display_name || authStore.account!.username" orientation="vertical"
:src="authStore.account!.avatar" /> :default-value="pages[0]"
>
<DialogHeader
class="gap-6 grid grid-rows-[auto_minmax(0,1fr)] border-b md:border-b-0 md:border-r min-w-60 text-left"
>
<div
class="grid gap-3 items-center grid-cols-[auto_minmax(0,1fr)]"
>
<Avatar
:name="authStore.account!.display_name || authStore.account!.username"
:src="authStore.account!.avatar"
/>
<DialogTitle>Preferences</DialogTitle> <DialogTitle>Preferences</DialogTitle>
</div> </div>
<DialogDescription class="sr-only"> <DialogDescription class="sr-only">
Make changes to your preferences here. Make changes to your preferences here.
</DialogDescription> </DialogDescription>
<TabsList class="md:grid md:grid-cols-1 w-full h-fit *:justify-start !justify-start"> <TabsList
<TabsTrigger v-for="page in pages" :key="page" :value="page"> class="md:grid md:grid-cols-1 w-full h-fit *:justify-start !justify-start"
>
<TabsTrigger
v-for="page in pages"
:key="page"
:value="page"
>
<component :is="icons[page]" class="size-4 mr-2"/> <component :is="icons[page]" class="size-4 mr-2"/>
{{ page }} {{ page }}
</TabsTrigger> </TabsTrigger>
</TabsList> </TabsList>
</DialogHeader> </DialogHeader>
<TabsContent v-for="page in pages.filter(p => !extraPages.includes(p))" :key="page" :value="page" <TabsContent
as-child> v-for="page in pages.filter(p => !extraPages.includes(p))"
:key="page"
:value="page"
as-child
>
<Page :title="page"> <Page :title="page">
<Category v-for="category in categories[page]" :key="category" <Category
v-for="category in categories[page]"
:key="category"
:preferences="Object.entries(preferences).filter(([, p]) => p.options.category === `${page}/${category}`).map(([k,]) => k as keyof typeof preferences)" :preferences="Object.entries(preferences).filter(([, p]) => p.options.category === `${page}/${category}`).map(([k,]) => k as keyof typeof preferences)"
:name="category" /> :name="category"
/>
</Page> </Page>
</TabsContent> </TabsContent>
<TabsContent value="Emojis" as-child> <TabsContent value="Emojis" as-child>
@ -130,21 +154,49 @@ useListen("preferences:open", () => {
</section> </section>
<Separator/> <Separator/>
<section class="space-y-2"> <section class="space-y-2">
<h3 class="text-lg font-semibold tracking-tight">Developers</h3> <h3 class="text-lg font-semibold tracking-tight">
<div class="grid lg:grid-cols-3 md:grid-cols-2 grid-cols-1 gap-4"> Developers
<TinyCard v-if="author1" :account="author1" domain="vs.cpluspatch.com" /> </h3>
<TinyCard v-if="author2" :account="author2" domain="social.lysand.org" /> <div
<TinyCard v-if="author3" :account="author3" domain="social.lysand.org" /> class="grid lg:grid-cols-3 md:grid-cols-2 grid-cols-1 gap-4"
<TinyCard v-if="author4" :account="author4" domain="v.everypizza.im" /> >
<TinyCard
v-if="author1"
:account="author1"
domain="vs.cpluspatch.com"
/>
<TinyCard
v-if="author2"
:account="author2"
domain="social.lysand.org"
/>
<TinyCard
v-if="author3"
:account="author3"
domain="social.lysand.org"
/>
<TinyCard
v-if="author4"
:account="author4"
domain="v.everypizza.im"
/>
</div> </div>
</section> </section>
<Separator/> <Separator/>
<section class="space-y-2"> <section class="space-y-2">
<h3 class="text-lg font-semibold tracking-tight">Dependencies</h3> <h3 class="text-lg font-semibold tracking-tight">
<ul class="grid lg:grid-cols-2 gap-2 grid-cols-1 items-center justify-center list-disc ml-6"> Dependencies
<li v-for="[dep, version] in Object.entries(pkg.dependencies)" :key="dep"> </h3>
<ul
class="grid lg:grid-cols-2 gap-2 grid-cols-1 items-center justify-center list-disc ml-6"
>
<li
v-for="[dep, version] in Object.entries(pkg.dependencies)"
:key="dep"
>
<code <code
class="rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-xs font-semibold"> class="rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-xs font-semibold"
>
{{ dep }}@{{ version }} {{ dep }}@{{ version }}
</code> </code>
</li> </li>

View file

@ -1,7 +1,12 @@
<template> <template>
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger as-child> <DropdownMenuTrigger as-child>
<Button variant="ghost" size="icon" title="Open menu" class="size-8 p-0"> <Button
variant="ghost"
size="icon"
title="Open menu"
class="size-8 p-0"
>
<MoreHorizontal class="size-4"/> <MoreHorizontal class="size-4"/>
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>

View file

@ -282,9 +282,12 @@ const table = useVueTable({
<template> <template>
<div class="w-full"> <div class="w-full">
<div class="flex gap-2 items-center py-4"> <div class="flex gap-2 items-center py-4">
<Input class="max-w-52 mr-auto" placeholder="Filter emojis..." <Input
class="max-w-52 mr-auto"
placeholder="Filter emojis..."
:model-value="(table.getColumn('shortcode')?.getFilterValue() as string)" :model-value="(table.getColumn('shortcode')?.getFilterValue() as string)"
@update:model-value="table.getColumn('shortcode')?.setFilterValue($event)" /> @update:model-value="table.getColumn('shortcode')?.setFilterValue($event)"
/>
<Uploader v-if="props.canUpload"> <Uploader v-if="props.canUpload">
<Button variant="outline" size="icon" title="Upload emoji"> <Button variant="outline" size="icon" title="Upload emoji">
<Plus class="size-4"/> <Plus class="size-4"/>
@ -299,10 +302,14 @@ const table = useVueTable({
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="end"> <DropdownMenuContent align="end">
<DropdownMenuCheckboxItem <DropdownMenuCheckboxItem
v-for="column in table.getAllColumns().filter((column) => column.getCanHide())" :key="column.id" v-for="column in table.getAllColumns().filter((column) => column.getCanHide())"
class="capitalize" :model-value="column.getIsVisible()" @update:model-value="(value) => { :key="column.id"
class="capitalize"
:model-value="column.getIsVisible()"
@update:model-value="(value) => {
column.toggleVisibility(!!value) column.toggleVisibility(!!value)
}"> }"
>
{{ column.id }} {{ column.id }}
</DropdownMenuCheckboxItem> </DropdownMenuCheckboxItem>
</DropdownMenuContent> </DropdownMenuContent>
@ -311,19 +318,40 @@ const table = useVueTable({
<div class="rounded-md border"> <div class="rounded-md border">
<Table> <Table>
<TableHeader> <TableHeader>
<TableRow v-for="headerGroup in table.getHeaderGroups()" :key="headerGroup.id"> <TableRow
<TableHead v-for="header in headerGroup.headers" :key="header.id" class=""> v-for="headerGroup in table.getHeaderGroups()"
<FlexRender v-if="!header.isPlaceholder" :render="header.column.columnDef.header" :key="headerGroup.id"
:props="header.getContext()" /> >
<TableHead
v-for="header in headerGroup.headers"
:key="header.id"
class=""
>
<FlexRender
v-if="!header.isPlaceholder"
:render="header.column.columnDef.header"
:props="header.getContext()"
/>
</TableHead> </TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
<template v-if="table.getRowModel().rows?.length"> <template v-if="table.getRowModel().rows?.length">
<template v-for="row in table.getRowModel().rows" :key="row.id"> <template
<TableRow :data-state="row.getIsSelected() && 'selected'"> v-for="row in table.getRowModel().rows"
<TableCell v-for="cell in row.getVisibleCells()" :key="cell.id"> :key="row.id"
<FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" /> >
<TableRow
:data-state="row.getIsSelected() && 'selected'"
>
<TableCell
v-for="cell in row.getVisibleCells()"
:key="cell.id"
>
<FlexRender
:render="cell.column.columnDef.cell"
:props="cell.getContext()"
/>
</TableCell> </TableCell>
</TableRow> </TableRow>
<TableRow v-if="row.getIsExpanded()"> <TableRow v-if="row.getIsExpanded()">
@ -335,7 +363,10 @@ const table = useVueTable({
</template> </template>
<TableRow v-else> <TableRow v-else>
<TableCell :colspan="columns.length" class="h-24 text-center"> <TableCell
:colspan="columns.length"
class="h-24 text-center"
>
No results. No results.
</TableCell> </TableCell>
</TableRow> </TableRow>
@ -349,11 +380,20 @@ const table = useVueTable({
{{ table.getFilteredRowModel().rows.length }}row(s) selected. {{ table.getFilteredRowModel().rows.length }}row(s) selected.
</div> </div>
<div class="space-x-2"> <div class="space-x-2">
<Button variant="outline" size="sm" :disabled="!table.getCanPreviousPage()" <Button
@click="table.previousPage()"> variant="outline"
size="sm"
:disabled="!table.getCanPreviousPage()"
@click="table.previousPage()"
>
Previous Previous
</Button> </Button>
<Button variant="outline" size="sm" :disabled="!table.getCanNextPage()" @click="table.nextPage()"> <Button
variant="outline"
size="sm"
:disabled="!table.getCanNextPage()"
@click="table.nextPage()"
>
Next Next
</Button> </Button>
</div> </div>

View file

@ -4,9 +4,7 @@
<slot/> <slot/>
</DialogTrigger> </DialogTrigger>
<DialogContent> <DialogContent>
<DialogTitle> <DialogTitle>{{ m.whole_icy_puffin_smile() }}</DialogTitle>
{{ m.whole_icy_puffin_smile() }}
</DialogTitle>
<DialogDescription class="sr-only"> <DialogDescription class="sr-only">
{{ m.frail_great_marten_pet() }} {{ m.frail_great_marten_pet() }}
</DialogDescription> </DialogDescription>
@ -20,28 +18,28 @@
class="h-full object-cover" class="h-full object-cover"
:src="createObjectURL(values.image as File)" :src="createObjectURL(values.image as File)"
:alt="values.alt" :alt="values.alt"
/> >
</div> </div>
<div class="bg-zinc-700"> <div class="bg-zinc-700">
<img <img
class="h-full object-cover" class="h-full object-cover"
:src="createObjectURL(values.image as File)" :src="createObjectURL(values.image as File)"
:alt="values.alt" :alt="values.alt"
/> >
</div> </div>
<div class="bg-zinc-400"> <div class="bg-zinc-400">
<img <img
class="h-full object-cover" class="h-full object-cover"
:src="createObjectURL(values.image as File)" :src="createObjectURL(values.image as File)"
:alt="values.alt" :alt="values.alt"
/> >
</div> </div>
<div class="bg-foreground"> <div class="bg-foreground">
<img <img
class="h-full object-cover" class="h-full object-cover"
:src="createObjectURL(values.image as File)" :src="createObjectURL(values.image as File)"
:alt="values.alt" :alt="values.alt"
/> >
</div> </div>
</div> </div>
@ -74,9 +72,7 @@
<FormField v-slot="{ componentField }" name="shortcode"> <FormField v-slot="{ componentField }" name="shortcode">
<FormItem> <FormItem>
<FormLabel> <FormLabel>{{ m.happy_mild_fox_gleam() }}</FormLabel>
{{ m.happy_mild_fox_gleam() }}
</FormLabel>
<FormControl> <FormControl>
<Input <Input
v-bind="componentField" v-bind="componentField"
@ -130,7 +126,10 @@
name="global" name="global"
as-child as-child
> >
<FormSwitch :title="m.pink_sharp_carp_work()" :description="m.dark_pretty_hyena_link()"> <FormSwitch
:title="m.pink_sharp_carp_work()"
:description="m.dark_pretty_hyena_link()"
>
<Switch <Switch
:model-value="value" :model-value="value"
@update:model-value="handleChange" @update:model-value="handleChange"

View file

@ -1,8 +1,6 @@
<template> <template>
<section class="gap-4 flex flex-col"> <section class="gap-4 flex flex-col">
<h2 class="text-xl font-bold tracking-tight"> <h2 class="text-xl font-bold tracking-tight">{{ title }}</h2>
{{ title }}
</h2>
<slot/> <slot/>
</section> </section>

View file

@ -1,10 +1,20 @@
<template> <template>
<form class="grid gap-6" @submit="save"> <form class="grid gap-6" @submit="save">
<Transition name="slide-up"> <Transition name="slide-up">
<Alert v-if="dirty" class="absolute bottom-2 z-10 inset-x-2 w-[calc(100%-1rem)] grid-cols-[calc(var(--spacing)*4)_1fr_auto]!"> <Alert
v-if="dirty"
class="absolute bottom-2 z-10 inset-x-2 w-[calc(100%-1rem)] grid-cols-[calc(var(--spacing)*4)_1fr_auto]!"
>
<SaveOff class="size-4"/> <SaveOff class="size-4"/>
<AlertTitle>Unsaved changes</AlertTitle> <AlertTitle>Unsaved changes</AlertTitle>
<Button variant="secondary" class="w-full row-span-2" type="button" :disabled="submitting">Apply</Button> <Button
variant="secondary"
class="w-full row-span-2"
type="button"
:disabled="submitting"
>
Apply
</Button>
<AlertDescription> <AlertDescription>
Click "apply" to save your changes. Click "apply" to save your changes.
</AlertDescription> </AlertDescription>
@ -12,55 +22,101 @@
</Transition> </Transition>
<FormField v-slot="{ handleChange, handleBlur }" name="banner"> <FormField v-slot="{ handleChange, handleBlur }" name="banner">
<TextInput :title="m.bright_late_osprey_renew()" :description="m.great_level_lamb_sway()"> <TextInput
<Input type="file" accept="image/*" @change="handleChange" @blur="handleBlur" /> :title="m.bright_late_osprey_renew()"
:description="m.great_level_lamb_sway()"
>
<Input
type="file"
accept="image/*"
@change="handleChange"
@blur="handleBlur"
/>
</TextInput> </TextInput>
</FormField> </FormField>
<FormField v-slot="{ setValue }" name="avatar"> <FormField v-slot="{ setValue }" name="avatar">
<TextInput :title="m.safe_icy_bulldog_quell()"> <TextInput :title="m.safe_icy_bulldog_quell()">
<ImageUploader v-model:image="authStore.account!.avatar" @submit-file="(file) => setValue(file)" <ImageUploader
@submit-url="(url) => setValue(url)" /> v-model:image="authStore.account!.avatar"
@submit-file="(file) => setValue(file)"
@submit-url="(url) => setValue(url)"
/>
</TextInput> </TextInput>
</FormField> </FormField>
<FormField v-slot="{ componentField }" name="name"> <FormField v-slot="{ componentField }" name="name">
<TextInput :title="m.mild_known_mallard_jolt()" :description="m.lime_dry_skunk_loop()"> <TextInput
:title="m.mild_known_mallard_jolt()"
:description="m.lime_dry_skunk_loop()"
>
<Input v-bind="componentField"/> <Input v-bind="componentField"/>
</TextInput> </TextInput>
</FormField> </FormField>
<FormField v-slot="{ componentField }" name="username"> <FormField v-slot="{ componentField }" name="username">
<TextInput :title="m.neat_silly_dog_prosper()" :description="m.petty_plane_tadpole_earn()"> <TextInput
:title="m.neat_silly_dog_prosper()"
:description="m.petty_plane_tadpole_earn()"
>
<Input v-bind="componentField"/> <Input v-bind="componentField"/>
</TextInput> </TextInput>
</FormField> </FormField>
<FormField v-slot="{ componentField }" name="bio"> <FormField v-slot="{ componentField }" name="bio">
<TextInput :title="m.next_caring_ladybug_hack()" :description="m.stale_just_anaconda_earn()"> <TextInput
:title="m.next_caring_ladybug_hack()"
:description="m.stale_just_anaconda_earn()"
>
<Textarea rows="10" v-bind="componentField"/> <Textarea rows="10" v-bind="componentField"/>
</TextInput> </TextInput>
</FormField> </FormField>
<FormField v-slot="{ value, handleChange }" name="fields"> <FormField v-slot="{ value, handleChange }" name="fields">
<Fields :title="m.aqua_mealy_toucan_pride()" :value="value" @update:value="handleChange" /> <Fields
:title="m.aqua_mealy_toucan_pride()"
:value="value"
@update:value="handleChange"
/>
</FormField> </FormField>
<FormField v-slot="{ value, handleChange }" name="bot" as-child> <FormField v-slot="{ value, handleChange }" name="bot" as-child>
<SwitchInput :title="m.gaudy_each_opossum_play()" :description="m.grassy_acidic_gadfly_cure()"> <SwitchInput
<Switch :model-value="value" @update:model-value="handleChange" /> :title="m.gaudy_each_opossum_play()"
:description="m.grassy_acidic_gadfly_cure()"
>
<Switch
:model-value="value"
@update:model-value="handleChange"
/>
</SwitchInput> </SwitchInput>
</FormField> </FormField>
<FormField v-slot="{ value, handleChange }" name="locked" as-child> <FormField v-slot="{ value, handleChange }" name="locked" as-child>
<SwitchInput :title="m.dirty_moving_shark_emerge()" :description="m.bright_fun_mouse_boil()"> <SwitchInput
<Switch :model-value="value" @update:model-value="handleChange" /> :title="m.dirty_moving_shark_emerge()"
:description="m.bright_fun_mouse_boil()"
>
<Switch
:model-value="value"
@update:model-value="handleChange"
/>
</SwitchInput> </SwitchInput>
</FormField> </FormField>
<FormField v-slot="{ value, handleChange }" name="discoverable" as-child> <FormField
<SwitchInput :title="m.red_vivid_cuckoo_spark()" :description="m.plain_zany_donkey_dart()"> v-slot="{ value, handleChange }"
<Switch :model-value="value" @update:model-value="handleChange" /> name="discoverable"
as-child
>
<SwitchInput
:title="m.red_vivid_cuckoo_spark()"
:description="m.plain_zany_donkey_dart()"
>
<Switch
:model-value="value"
@update:model-value="handleChange"
/>
</SwitchInput> </SwitchInput>
</FormField> </FormField>
</form> </form>

View file

@ -2,25 +2,59 @@
<FormItem> <FormItem>
<FormLabel> <FormLabel>
{{ title }} {{ title }}
<Button type="button" variant="secondary" size="icon" class="ml-auto" @click="addField()" :title="m.front_north_eel_gulp()"> <Button
type="button"
variant="secondary"
size="icon"
class="ml-auto"
@click="addField()"
:title="m.front_north_eel_gulp()"
>
<Plus/> <Plus/>
</Button> </Button>
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<VueDraggable class="grid gap-4" v-model="list" :animation="200" handle=".drag-handle"> <VueDraggable
<div v-for="(field, index) in list" :key="field.id" class="grid gap-4"
class="grid items-center grid-cols-[auto_repeat(3,minmax(0,1fr))_auto] gap-2"> v-model="list"
<Button as="span" variant="ghost" size="icon" class="drag-handle cursor-grab"> :animation="200"
handle=".drag-handle"
>
<div
v-for="(field, index) in list"
:key="field.id"
class="grid items-center grid-cols-[auto_repeat(3,minmax(0,1fr))_auto] gap-2"
>
<Button
as="span"
variant="ghost"
size="icon"
class="drag-handle cursor-grab"
>
<GripVertical/> <GripVertical/>
</Button> </Button>
<Input :model-value="field.name" placeholder="Name" @update:model-value=" <Input
:model-value="field.name"
placeholder="Name"
@update:model-value="
(e) => updateKey(index, String(e)) (e) => updateKey(index, String(e))
" /> "
<Input :model-value="field.value" placeholder="Value" class="col-span-2" @update:model-value=" />
<Input
:model-value="field.value"
placeholder="Value"
class="col-span-2"
@update:model-value="
(e) => updateValue(index, String(e)) (e) => updateValue(index, String(e))
" /> "
<Button type="button" variant="secondary" size="icon" @click="removeField(index)"> />
<Button
type="button"
variant="secondary"
size="icon"
@click="removeField(index)"
>
<Trash/> <Trash/>
</Button> </Button>
</div> </div>

View file

@ -15,9 +15,7 @@
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent> <DialogContent>
<DialogTitle> <DialogTitle>{{ m.due_hour_husky_prosper() }}</DialogTitle>
{{ m.due_hour_husky_prosper() }}
</DialogTitle>
<DialogDescription class="sr-only"> <DialogDescription class="sr-only">
{{ m.suave_broad_albatross_drop() }} {{ m.suave_broad_albatross_drop() }}
</DialogDescription> </DialogDescription>
@ -85,9 +83,11 @@
</FormControl> </FormControl>
<FormMessage/> <FormMessage/>
<div v-if="value" class="grid gap-4 !mt-4"> <div v-if="value" class="grid gap-4 !mt-4">
<Label>{{ <Label>
{{
m.witty_honest_wallaby_support() m.witty_honest_wallaby_support()
}}</Label> }}
</Label>
<Avatar class="size-32" :src="gravatarUrl"/> <Avatar class="size-32" :src="gravatarUrl"/>
</div> </div>
</FormItem> </FormItem>
@ -111,9 +111,11 @@
</FormControl> </FormControl>
<FormMessage/> <FormMessage/>
<div v-if="value" class="grid gap-4 !mt-4"> <div v-if="value" class="grid gap-4 !mt-4">
<Label>{{ <Label>
{{
m.witty_honest_wallaby_support() m.witty_honest_wallaby_support()
}}</Label> }}
</Label>
<Avatar class="size-32" :src="value"/> <Avatar class="size-32" :src="value"/>
</div> </div>
</FormItem> </FormItem>

View file

@ -1,11 +1,15 @@
<template> <template>
<Card class="grid gap-3 text-sm max-w-sm"> <Card class="grid gap-3 text-sm max-w-sm">
<dl class="grid gap-3"> <dl class="grid gap-3">
<div v-for="[key, value] of data" :key="key" class="flex flex-row items-baseline justify-between gap-4 truncate"> <div
<dt class="text-muted-foreground"> v-for="[key, value] of data"
{{ key }} :key="key"
</dt> class="flex flex-row items-baseline justify-between gap-4 truncate"
<dd class="font-mono" v-if="typeof value === 'string'">{{ value }}</dd> >
<dt class="text-muted-foreground">{{ key }}</dt>
<dd class="font-mono" v-if="typeof value === 'string'">
{{ value }}
</dd>
<dd class="font-mono" v-else> <dd class="font-mono" v-else>
<component :is="value"/> <component :is="value"/>
</dd> </dd>

View file

@ -1,14 +1,14 @@
<template> <template>
<Base :pref="pref" :name="name" v-slot="{ setValue, value }"> <TypeBase :pref="pref" :name="name" v-slot="{ setValue, value }">
<Switch @update:model-value="setValue" :model-value="value"/> <Switch @update:model-value="setValue" :model-value="value"/>
</Base> </TypeBase>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { Switch } from "~/components/ui/switch"; import { Switch } from "~/components/ui/switch";
import type { preferences as prefs } from "../preferences"; import type { preferences as prefs } from "../preferences";
import type { BooleanPreference } from "../types"; import type { BooleanPreference } from "../types";
import Base from "./base.vue"; import TypeBase from "./type-base.vue";
const { pref, name } = defineProps<{ const { pref, name } = defineProps<{
pref: BooleanPreference; pref: BooleanPreference;

View file

@ -1,19 +1,21 @@
<template> <template>
<Collapsible as-child> <Collapsible as-child>
<Base :name="name" :pref="pref"> <TypeBase :name="name" :pref="pref">
<template #default> <template #default>
<CollapsibleTrigger as-child> <CollapsibleTrigger as-child>
<Button variant="outline"> <Button variant="outline">Open code</Button>
Open code
</Button>
</CollapsibleTrigger> </CollapsibleTrigger>
</template> </template>
<template #extra="{ setValue, value }"> <template #extra="{ setValue, value }">
<CollapsibleContent class="col-span-2 mt-2"> <CollapsibleContent class="col-span-2 mt-2">
<Textarea :rows="10" :model-value="value" @update:model-value="setValue" /> <Textarea
:rows="10"
:model-value="value"
@update:model-value="setValue"
/>
</CollapsibleContent> </CollapsibleContent>
</template> </template>
</Base> </TypeBase>
</Collapsible> </Collapsible>
</template> </template>
@ -27,7 +29,7 @@ import {
import { Textarea } from "~/components/ui/textarea"; import { Textarea } from "~/components/ui/textarea";
import type { preferences as prefs } from "../preferences"; import type { preferences as prefs } from "../preferences";
import type { CodePreference } from "../types"; import type { CodePreference } from "../types";
import Base from "./base.vue"; import TypeBase from "./type-base.vue";
const { pref, name } = defineProps<{ const { pref, name } = defineProps<{
pref: CodePreference; pref: CodePreference;

View file

@ -1,25 +1,27 @@
<template> <template>
<Base :pref="pref" :name="name" v-slot="{ setValue, value }"> <TypeBase :pref="pref" :name="name" v-slot="{ setValue, value }">
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger as-child> <DropdownMenuTrigger as-child>
<Button variant="outline"> <Button variant="outline">Pick</Button>
Pick
</Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent class="w-56"> <DropdownMenuContent class="w-56">
<DropdownMenuCheckboxItem v-for="[option, title] in Object.entries(pref.options.options)" :key="option" <DropdownMenuCheckboxItem
:model-value="value.includes(option)" @update:model-value="checked => { v-for="[option, title] in Object.entries(pref.options.options)"
:key="option"
:model-value="value.includes(option)"
@update:model-value="checked => {
if (checked) { if (checked) {
setValue([...value, option]); setValue([...value, option]);
} else { } else {
setValue(value.filter((v: any) => v !== option)); setValue(value.filter((v: any) => v !== option));
} }
}"> }"
>
{{ title }} {{ title }}
</DropdownMenuCheckboxItem> </DropdownMenuCheckboxItem>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
</Base> </TypeBase>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -32,7 +34,7 @@ import {
} from "~/components/ui/dropdown-menu"; } from "~/components/ui/dropdown-menu";
import type { preferences as prefs } from "../preferences"; import type { preferences as prefs } from "../preferences";
import type { MultiSelectPreference } from "../types"; import type { MultiSelectPreference } from "../types";
import Base from "./base.vue"; import TypeBase from "./type-base.vue";
const { pref, name } = defineProps<{ const { pref, name } = defineProps<{
pref: MultiSelectPreference<string>; pref: MultiSelectPreference<string>;

View file

@ -1,13 +1,19 @@
<template> <template>
<Base :pref="pref" :name="name" v-slot="{ setValue, value }"> <TypeBase :pref="pref" :name="name" v-slot="{ setValue, value }">
<NumberField :model-value="value" @update:model-value="setValue" :min="pref.options.min" :max="pref.options.max" :step="pref.options.integer ? 1 : pref.options.step"> <NumberField
:model-value="value"
@update:model-value="setValue"
:min="pref.options.min"
:max="pref.options.max"
:step="pref.options.integer ? 1 : pref.options.step"
>
<NumberFieldContent> <NumberFieldContent>
<NumberFieldDecrement/> <NumberFieldDecrement/>
<NumberFieldInput/> <NumberFieldInput/>
<NumberFieldIncrement/> <NumberFieldIncrement/>
</NumberFieldContent> </NumberFieldContent>
</NumberField> </NumberField>
</Base> </TypeBase>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -20,7 +26,7 @@ import {
} from "~/components/ui/number-field"; } from "~/components/ui/number-field";
import type { preferences as prefs } from "../preferences"; import type { preferences as prefs } from "../preferences";
import type { NumberPreference } from "../types"; import type { NumberPreference } from "../types";
import Base from "./base.vue"; import TypeBase from "./type-base.vue";
const { pref, name } = defineProps<{ const { pref, name } = defineProps<{
pref: NumberPreference; pref: NumberPreference;

View file

@ -1,18 +1,21 @@
<template> <template>
<Base :pref="pref" :name="name" v-slot="{ setValue, value }"> <TypeBase :pref="pref" :name="name" v-slot="{ setValue, value }">
<Select :model-value="value" @update:model-value="setValue"> <Select :model-value="value" @update:model-value="setValue">
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder="Select an option"/> <SelectValue placeholder="Select an option"/>
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectGroup> <SelectGroup>
<SelectItem v-for="[val, title] in Object.entries(pref.options.options)" :value="val"> <SelectItem
v-for="[val, title] in Object.entries(pref.options.options)"
:value="val"
>
{{ title }} {{ title }}
</SelectItem> </SelectItem>
</SelectGroup> </SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</Base> </TypeBase>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -26,7 +29,7 @@ import {
} from "~/components/ui/select"; } from "~/components/ui/select";
import type { preferences as prefs } from "../preferences"; import type { preferences as prefs } from "../preferences";
import type { SelectPreference } from "../types"; import type { SelectPreference } from "../types";
import Base from "./base.vue"; import TypeBase from "./type-base.vue";
const { pref, name } = defineProps<{ const { pref, name } = defineProps<{
pref: SelectPreference<string>; pref: SelectPreference<string>;

View file

@ -1,14 +1,18 @@
<template> <template>
<Base :pref="pref" :name="name" v-slot="{ setValue, value }"> <TypeBase :pref="pref" :name="name" v-slot="{ setValue, value }">
<Input placeholder="Content here..." :model-value="value" @update:model-value="setValue" /> <Input
</Base> placeholder="Content here..."
:model-value="value"
@update:model-value="setValue"
/>
</TypeBase>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { Input } from "~/components/ui/input"; import { Input } from "~/components/ui/input";
import type { preferences as prefs } from "../preferences"; import type { preferences as prefs } from "../preferences";
import type { TextPreference } from "../types"; import type { TextPreference } from "../types";
import Base from "./base.vue"; import TypeBase from "./type-base.vue";
const { pref, name } = defineProps<{ const { pref, name } = defineProps<{
pref: TextPreference; pref: TextPreference;

View file

@ -1,9 +1,17 @@
<template> <template>
<div class="grid grid-cols-[minmax(0,1fr)_auto] gap-2 hover:bg-muted/40 duration-75 p-4"> <div
class="grid grid-cols-[minmax(0,1fr)_auto] gap-2 hover:bg-muted/40 duration-75 p-4"
>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<h3 class="text-sm font-semibold tracking-tight">{{ pref.options.name }}</h3> <h3 class="text-sm font-semibold tracking-tight">
<small v-if="pref.options.description" class="text-xs font-medium leading-none text-muted-foreground">{{ {{ pref.options.name }}
pref.options.description }}</small> </h3>
<small
v-if="pref.options.description"
class="text-xs font-medium leading-none text-muted-foreground"
>{{
pref.options.description }}</small
>
</div> </div>
<div class="flex items-center justify-end"> <div class="flex items-center justify-end">
<slot :value="value" :set-value="setValue"/> <slot :value="value" :set-value="setValue"/>

View file

@ -1,19 +1,21 @@
<template> <template>
<Collapsible as-child> <Collapsible as-child>
<Base :pref="pref" :name="name"> <TypeBase :pref="pref" :name="name">
<template #default> <template #default>
<CollapsibleTrigger as-child> <CollapsibleTrigger as-child>
<Button variant="outline"> <Button variant="outline">Edit URL</Button>
Edit URL
</Button>
</CollapsibleTrigger> </CollapsibleTrigger>
</template> </template>
<template #extra="{ setValue, value }"> <template #extra="{ setValue, value }">
<CollapsibleContent class="col-span-2 mt-2"> <CollapsibleContent class="col-span-2 mt-2">
<UrlInput placeholder="Type URL or domain here..." :model-value="value" @update:model-value="setValue" /> <UrlInput
placeholder="Type URL or domain here..."
:model-value="value"
@update:model-value="setValue"
/>
</CollapsibleContent> </CollapsibleContent>
</template> </template>
</Base> </TypeBase>
</Collapsible> </Collapsible>
</template> </template>
@ -27,7 +29,7 @@ import {
import { Input, UrlInput } from "~/components/ui/input"; import { Input, UrlInput } from "~/components/ui/input";
import type { preferences as prefs } from "../preferences"; import type { preferences as prefs } from "../preferences";
import type { TextPreference } from "../types"; import type { TextPreference } from "../types";
import Base from "./base.vue"; import TypeBase from "./type-base.vue";
const { pref, name } = defineProps<{ const { pref, name } = defineProps<{
pref: TextPreference; pref: TextPreference;

View file

@ -1,8 +1,6 @@
<template> <template>
<Avatar :class="['rounded-md bg-secondary']"> <Avatar :class="['rounded-md bg-secondary']">
<AvatarFallback v-if="name"> <AvatarFallback v-if="name">{{ getInitials(name) }}</AvatarFallback>
{{ getInitials(name) }}
</AvatarFallback>
<AvatarImage v-if="src" :src="src" :alt="`${name}'s avatar`"/> <AvatarImage v-if="src" :src="src" :alt="`${name}'s avatar`"/>
</Avatar> </Avatar>
</template> </template>

View file

@ -5,11 +5,17 @@
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent class="min-w-56"> <DropdownMenuContent class="min-w-56">
<DropdownMenuGroup> <DropdownMenuGroup>
<DropdownMenuItem as="button" @click="copyText(account.username)"> <DropdownMenuItem
as="button"
@click="copyText(account.username)"
>
<AtSign/> <AtSign/>
{{ m.cool_dark_tapir_belong() }} {{ m.cool_dark_tapir_belong() }}
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem as="button" @click="copyText(JSON.stringify(account, null, 4))"> <DropdownMenuItem
as="button"
@click="copyText(JSON.stringify(account, null, 4))"
>
<Code/> <Code/>
{{ m.yummy_moving_scallop_sail() }} {{ m.yummy_moving_scallop_sail() }}
</DropdownMenuItem> </DropdownMenuItem>
@ -28,7 +34,13 @@
<Link/> <Link/>
{{ m.solid_witty_zebra_walk() }} {{ m.solid_witty_zebra_walk() }}
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem as="a" v-if="isRemote" target="_blank" rel="noopener noreferrer" :href="account.url"> <DropdownMenuItem
as="a"
v-if="isRemote"
target="_blank"
rel="noopener noreferrer"
:href="account.url"
>
<ExternalLink/> <ExternalLink/>
{{ m.active_trite_lark_inspire() }} {{ m.active_trite_lark_inspire() }}
</DropdownMenuItem> </DropdownMenuItem>

View file

@ -3,7 +3,7 @@
<TooltipTrigger :as-child="true"> <TooltipTrigger :as-child="true">
<Badge variant="default" class="gap-1"> <Badge variant="default" class="gap-1">
<BadgeCheck v-if="verified"/> <BadgeCheck v-if="verified"/>
<img v-else-if="icon" :src="icon" alt="" class="size-4 rounded" /> <img v-else-if="icon" :src="icon" alt="" class="size-4 rounded">
{{ name }} {{ name }}
</Badge> </Badge>
</TooltipTrigger> </TooltipTrigger>

View file

@ -1,5 +1,7 @@
<template> <template>
<Row class="gap-2" wrap <Row
class="gap-2"
wrap
v-if="isDeveloper || account.bot || roles.length > 0" v-if="isDeveloper || account.bot || roles.length > 0"
> >
<ProfileBadge <ProfileBadge

View file

@ -1,10 +1,16 @@
<template> <template>
<Col class="gap-y-4"> <Column class="gap-y-4">
<Col v-for="field in fields" :key="field.name" class="gap-1 break-words"> <Column
<HeadingSmall v-render-emojis="emojis">{{ field.name }}</HeadingSmall> 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"/> <Html v-html="field.value" v-render-emojis="emojis"/>
</Col> </Column>
</Col> </Column>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -12,7 +18,7 @@ import type { CustomEmoji, Field } from "@versia/client/schemas";
import type { z } from "zod"; import type { z } from "zod";
import HeadingSmall from "~/components/typography/headings/small.vue"; import HeadingSmall from "~/components/typography/headings/small.vue";
import Html from "../typography/html.vue"; import Html from "../typography/html.vue";
import Col from "../typography/layout/col.vue"; import Column from "../typography/layout/col.vue";
defineProps<{ defineProps<{
fields: z.infer<typeof Field>[]; fields: z.infer<typeof Field>[];

View file

@ -6,7 +6,7 @@
:src="header" :src="header"
alt="" alt=""
class="object-cover w-full h-full" class="object-cover w-full h-full"
/> >
<!-- Shadow overlay at the bottom --> <!-- Shadow overlay at the bottom -->
<div <div
class="absolute bottom-0 w-full h-1/3 bg-gradient-to-b from-black/0 to-black/40" class="absolute bottom-0 w-full h-1/3 bg-gradient-to-b from-black/0 to-black/40"
@ -15,11 +15,7 @@
<div <div
class="absolute bottom-0 translate-y-1/3 left-4 flex flex-row items-start gap-2" class="absolute bottom-0 translate-y-1/3 left-4 flex flex-row items-start gap-2"
> >
<Avatar <Avatar class="size-32 border" :src="avatar" :name="displayName"/>
class="size-32 border"
:src="avatar"
:name="displayName"
/>
</div> </div>
</CardHeader> </CardHeader>
</template> </template>

View file

@ -1,6 +1,10 @@
<template> <template>
<Button variant="secondary" :disabled="isLoading || relationship?.requested" v-if="!isMe && authStore.isSignedIn" <Button
@click="relationship?.following ? unfollow() : follow()"> variant="secondary"
:disabled="isLoading || relationship?.requested"
v-if="!isMe && authStore.isSignedIn"
@click="relationship?.following ? unfollow() : follow()"
>
<Loader v-if="isLoading" class="animate-spin"/> <Loader v-if="isLoading" class="animate-spin"/>
<span v-else> <span v-else>
{{ {{

View file

@ -1,24 +1,24 @@
<template> <template>
<Row class="gap-2 w-full justify-around"> <Row class="gap-2 w-full justify-around">
<Col centered> <Column centered>
<Bold>{{ noteCount }}</Bold> <Bold>{{ noteCount }}</Bold>
<Small muted>{{ m.real_gray_stork_seek() }}</Small> <Small muted>{{ m.real_gray_stork_seek() }}</Small>
</Col> </Column>
<Col centered> <Column centered>
<Bold>{{ followerCount }}</Bold> <Bold>{{ followerCount }}</Bold>
<Small muted>{{ m.teal_helpful_parakeet_hike() }}</Small> <Small muted>{{ m.teal_helpful_parakeet_hike() }}</Small>
</Col> </Column>
<Col centered> <Column centered>
<Bold>{{ followingCount }}</Bold> <Bold>{{ followingCount }}</Bold>
<Small muted>{{ m.aloof_royal_samuel_startle() }}</Small> <Small muted>{{ m.aloof_royal_samuel_startle() }}</Small>
</Col> </Column>
</Row> </Row>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import * as m from "~~/paraglide/messages.js"; import * as m from "~~/paraglide/messages.js";
import Bold from "../typography/bold.vue"; import Bold from "../typography/bold.vue";
import Col from "../typography/layout/col.vue"; import Column from "../typography/layout/col.vue";
import Row from "../typography/layout/row.vue"; import Row from "../typography/layout/row.vue";
import Small from "../typography/small.vue"; import Small from "../typography/small.vue";

View file

@ -1,6 +1,10 @@
<template> <template>
<Card class="gap-4"> <Card class="gap-4">
<ProfileHeader :header="account.header" :avatar="account.avatar" :display-name="account.display_name" /> <ProfileHeader
:header="account.header"
:avatar="account.avatar"
:display-name="account.display_name"
/>
<Row class="justify-end gap-2"> <Row class="justify-end gap-2">
<ProfileRelationshipActions :account="account"/> <ProfileRelationshipActions :account="account"/>
<ProfileActions :account="account"> <ProfileActions :account="account">
@ -9,25 +13,33 @@
</Button> </Button>
</ProfileActions> </ProfileActions>
</Row> </Row>
<Col class="justify-center"> <Column class="justify-center">
<Text class="font-bold" v-render-emojis="account.emojis"> <Text class="font-bold" v-render-emojis="account.emojis">
{{ account.display_name }} {{ account.display_name }}
</Text> </Text>
<Address :username="username" :domain="domain"/> <Address :username="username" :domain="domain"/>
</Col> </Column>
<ProfileBadges :account="account"/> <ProfileBadges :account="account"/>
<Html v-html="account.note" v-render-emojis="account.emojis"/> <Html v-html="account.note" v-render-emojis="account.emojis"/>
<Separator/> <Separator/>
<ProfileFields v-if="account.fields.length > 0" :fields="account.fields" :emojis="account.emojis" /> <ProfileFields
v-if="account.fields.length > 0"
:fields="account.fields"
:emojis="account.emojis"
/>
<Separator v-if="account.fields.length > 0"/> <Separator v-if="account.fields.length > 0"/>
<Row> <Row>
<HeadingSmall class="flex items-center gap-1"> <HeadingSmall class="flex items-center gap-1">
<CalendarDays class="size-4" /> {{ formattedCreationDate }} <CalendarDays class="size-4"/>
{{ formattedCreationDate }}
</HeadingSmall> </HeadingSmall>
</Row> </Row>
<Separator/> <Separator/>
<ProfileStats :follower-count="account.followers_count" :following-count="account.following_count" <ProfileStats
:note-count="account.statuses_count" /> :follower-count="account.followers_count"
:following-count="account.following_count"
:note-count="account.statuses_count"
/>
</Card> </Card>
</template> </template>
@ -41,7 +53,7 @@ import { Separator } from "~/components/ui/separator";
import { getLocale } from "~~/paraglide/runtime"; import { getLocale } from "~~/paraglide/runtime";
import HeadingSmall from "../typography/headings/small.vue"; import HeadingSmall from "../typography/headings/small.vue";
import Html from "../typography/html.vue"; import Html from "../typography/html.vue";
import Col from "../typography/layout/col.vue"; import Column from "../typography/layout/col.vue";
import Row from "../typography/layout/row.vue"; import Row from "../typography/layout/row.vue";
import Text from "../typography/text.vue"; import Text from "../typography/text.vue";
import Address from "./address.vue"; import Address from "./address.vue";

View file

@ -5,7 +5,7 @@
:src="account.header" :src="account.header"
alt="" alt=""
class="object-cover w-full h-full" class="object-cover w-full h-full"
/> >
<!-- Shadow overlay at the bottom --> <!-- Shadow overlay at the bottom -->
<div <div
class="absolute bottom-0 w-full h-1/3 bg-gradient-to-b from-black/0 to-black/40" class="absolute bottom-0 w-full h-1/3 bg-gradient-to-b from-black/0 to-black/40"

View file

@ -3,7 +3,11 @@
class="flex-row gap-2 p-2 truncate items-center" class="flex-row gap-2 p-2 truncate items-center"
:class="naked ? 'p-0 bg-transparent ring-0 border-none shadow-none' : ''" :class="naked ? 'p-0 bg-transparent ring-0 border-none shadow-none' : ''"
> >
<Avatar :src="account.avatar" :name="account.display_name" class="size-10" /> <Avatar
:src="account.avatar"
:name="account.display_name"
class="size-10"
/>
<CardContent class="leading-tight"> <CardContent class="leading-tight">
<Text class="font-semibold" v-render-emojis="account.emojis"> <Text class="font-semibold" v-render-emojis="account.emojis">
{{ account.display_name }} {{ account.display_name }}

View file

@ -11,15 +11,30 @@
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div v-if="authStore.identities.length > 0" class="grid gap-4 py-2"> <div v-if="authStore.identities.length > 0" class="grid gap-4 py-2">
<div v-for="identity of authStore.identities" :key="identity.account.id" <div
class="grid grid-cols-[1fr_auto] has-[>[data-switch]]:grid-cols-[1fr_auto_auto] gap-2"> v-for="identity of authStore.identities"
<TinyCard :account="identity.account" :domain="identity.instance.domain" naked /> :key="identity.account.id"
<Button data-switch v-if="authStore.identity?.id !== identity.id" class="grid grid-cols-[1fr_auto] has-[>[data-switch]]:grid-cols-[1fr_auto_auto] gap-2"
@click="authStore.setActiveIdentity(identity.id)" variant="outline"> >
<TinyCard
:account="identity.account"
:domain="identity.instance.domain"
naked
/>
<Button
data-switch
v-if="authStore.identity?.id !== identity.id"
@click="authStore.setActiveIdentity(identity.id)"
variant="outline"
>
Switch Switch
</Button> </Button>
<Button @click="signOutAction(identity.id)" variant="outline" size="icon" <Button
:title="m.sharp_big_mallard_reap()"> @click="signOutAction(identity.id)"
variant="outline"
size="icon"
:title="m.sharp_big_mallard_reap()"
>
<LogOut/> <LogOut/>
</Button> </Button>
</div> </div>

View file

@ -26,8 +26,15 @@ const authStore = useAuthStore();
<SidebarMenu class="gap-3"> <SidebarMenu class="gap-3">
<SidebarMenuItem> <SidebarMenuItem>
<AccountManager> <AccountManager>
<SidebarMenuButton v-if="authStore.account && authStore.instance" size="lg"> <SidebarMenuButton
<TinyCard :account="authStore.account" :domain="authStore.instance.domain" naked /> v-if="authStore.account && authStore.instance"
size="lg"
>
<TinyCard
:account="authStore.account"
:domain="authStore.instance.domain"
naked
/>
<ChevronsUpDown class="ml-auto size-4"/> <ChevronsUpDown class="ml-auto size-4"/>
</SidebarMenuButton> </SidebarMenuButton>
<SidebarMenuButton v-else> <SidebarMenuButton v-else>
@ -38,19 +45,34 @@ const authStore = useAuthStore();
</AccountManager> </AccountManager>
</SidebarMenuItem> </SidebarMenuItem>
<SidebarMenuItem class="flex flex-col gap-2"> <SidebarMenuItem class="flex flex-col gap-2">
<Button v-if="authStore.isSignedIn" variant="default" size="lg" class="w-full group-data-[collapsible=icon]:px-4" <Button
@click="useEvent('composer:open')"> v-if="authStore.isSignedIn"
variant="default"
size="lg"
class="w-full group-data-[collapsible=icon]:px-4"
@click="useEvent('composer:open')"
>
<Pen/> <Pen/>
<span class="group-data-[collapsible=icon]:hidden"> <span class="group-data-[collapsible=icon]:hidden">
{{ m.salty_aloof_turkey_nudge() }} {{ m.salty_aloof_turkey_nudge() }}
</span> </span>
</Button> </Button>
<Button v-if="authStore.isSignedIn" size="lg" variant="secondary" @click="useEvent('preferences:open')"> <Button
v-if="authStore.isSignedIn"
size="lg"
variant="secondary"
@click="useEvent('preferences:open')"
>
<Cog/> <Cog/>
Preferences Preferences
</Button> </Button>
<Button v-if="$pwa?.needRefresh" variant="destructive" size="lg" <Button
class="w-full group-data-[collapsible=icon]:px-4" @click="$pwa?.updateServiceWorker(true)"> v-if="$pwa?.needRefresh"
variant="destructive"
size="lg"
class="w-full group-data-[collapsible=icon]:px-4"
@click="$pwa?.updateServiceWorker(true)"
>
<DownloadCloud/> <DownloadCloud/>
<span class="group-data-[collapsible=icon]:hidden"> <span class="group-data-[collapsible=icon]:hidden">
{{ m.quaint_low_felix_pave() }} {{ m.quaint_low_felix_pave() }}

View file

@ -14,7 +14,10 @@ const authStore = useAuthStore();
<SidebarMenu> <SidebarMenu>
<SidebarMenuItem> <SidebarMenuItem>
<NuxtLink href="/"> <NuxtLink href="/">
<InstanceSmallCard v-if="authStore.instance" :instance="authStore.instance" /> <InstanceSmallCard
v-if="authStore.instance"
:instance="authStore.instance"
/>
</NuxtLink> </NuxtLink>
</SidebarMenuItem> </SidebarMenuItem>
</SidebarMenu> </SidebarMenu>

View file

@ -3,9 +3,11 @@
<InstanceHeader/> <InstanceHeader/>
<SidebarContent> <SidebarContent>
<SidebarGroup> <SidebarGroup>
<SidebarGroupLabel>{{ <SidebarGroupLabel>
{{
m.trite_real_sawfish_drum() m.trite_real_sawfish_drum()
}}</SidebarGroupLabel> }}
</SidebarGroupLabel>
<NavItems <NavItems
:items=" :items="
sidebarConfig.other.filter((i) => sidebarConfig.other.filter((i) =>

View file

@ -24,5 +24,8 @@ const authStore = useAuthStore();
</header> </header>
<slot/> <slot/>
</main> </main>
<RightSidebar v-if="authStore.isSignedIn" v-show="preferences.display_notifications_sidebar" /> <RightSidebar
v-if="authStore.isSignedIn"
v-show="preferences.display_notifications_sidebar"
/>
</template> </template>

View file

@ -1,7 +1,15 @@
<template> <template>
<Timeline type="status" :items="items" :is-loading="isLoading" :has-reached-end="hasReachedEnd" <Timeline
:error="error" :load-next="loadNext" :load-prev="loadPrev" :remove-item="removeItem" type="status"
:update-item="updateItem" /> :items="items"
:is-loading="isLoading"
:has-reached-end="hasReachedEnd"
:error="error"
:load-next="loadNext"
:load-prev="loadPrev"
:remove-item="removeItem"
:update-item="updateItem"
/>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View file

@ -1,7 +1,15 @@
<template> <template>
<Timeline type="status" :items="items" :is-loading="isLoading" :has-reached-end="hasReachedEnd" <Timeline
:error="error" :load-next="loadNext" :load-prev="loadPrev" :remove-item="removeItem" type="status"
:update-item="updateItem" /> :items="items"
:is-loading="isLoading"
:has-reached-end="hasReachedEnd"
:error="error"
:load-next="loadNext"
:load-prev="loadPrev"
:remove-item="removeItem"
:update-item="updateItem"
/>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View file

@ -1,7 +1,15 @@
<template> <template>
<Timeline type="status" :items="items" :is-loading="isLoading" :has-reached-end="hasReachedEnd" <Timeline
:error="error" :load-next="loadNext" :load-prev="loadPrev" :remove-item="removeItem" type="status"
:update-item="updateItem" /> :items="items"
:is-loading="isLoading"
:has-reached-end="hasReachedEnd"
:error="error"
:load-next="loadNext"
:load-prev="loadPrev"
:remove-item="removeItem"
:update-item="updateItem"
/>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View file

@ -1,7 +1,15 @@
<template> <template>
<Timeline type="status" :items="items" :is-loading="isLoading" :has-reached-end="hasReachedEnd" <Timeline
:error="error" :load-next="loadNext" :load-prev="loadPrev" :remove-item="removeItem" type="status"
:update-item="updateItem" /> :items="items"
:is-loading="isLoading"
:has-reached-end="hasReachedEnd"
:error="error"
:load-next="loadNext"
:load-prev="loadPrev"
:remove-item="removeItem"
:update-item="updateItem"
/>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View file

@ -1,7 +1,15 @@
<template> <template>
<Timeline type="notification" :items="items" :is-loading="isLoading" <Timeline
:has-reached-end="hasReachedEnd" :error="error" :load-next="loadNext" :load-prev="loadPrev" type="notification"
:remove-item="removeItem" :update-item="updateItem" /> :items="items"
:is-loading="isLoading"
:has-reached-end="hasReachedEnd"
:error="error"
:load-next="loadNext"
:load-prev="loadPrev"
:remove-item="removeItem"
:update-item="updateItem"
/>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View file

@ -1,7 +1,15 @@
<template> <template>
<Timeline type="status" :items="items" :is-loading="isLoading" :has-reached-end="hasReachedEnd" <Timeline
:error="error" :load-next="loadNext" :load-prev="loadPrev" :remove-item="removeItem" type="status"
:update-item="updateItem" /> :items="items"
:is-loading="isLoading"
:has-reached-end="hasReachedEnd"
:error="error"
:load-next="loadNext"
:load-prev="loadPrev"
:remove-item="removeItem"
:update-item="updateItem"
/>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View file

@ -1,6 +1,11 @@
<template> <template>
<component :is="itemComponent" :note="type === 'status' ? item : undefined" :notification="type === 'notification' ? item : (undefined as any)" @update="$emit('update', $event)" <component
@delete="$emit('delete', item?.id)" /> :is="itemComponent"
:note="type === 'status' ? item : undefined"
:notification="type === 'notification' ? item : (undefined as any)"
@update="$emit('update', $event)"
@delete="$emit('delete', item?.id)"
/>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View file

@ -14,9 +14,7 @@
<Spinner v-if="isLoading"/> <Spinner v-if="isLoading"/>
<div v-if="error" class="timeline-error"> <div v-if="error" class="timeline-error">{{ error.message }}</div>
{{ error.message }}
</div>
<!-- If there are some posts, but the user scrolled to the end --> <!-- If there are some posts, but the user scrolled to the end -->
<ReachedEnd v-if="hasReachedEnd && items.length > 0"/> <ReachedEnd v-if="hasReachedEnd && items.length > 0"/>

View file

@ -16,7 +16,10 @@ const delegatedProps = computed(() => {
</script> </script>
<template> <template>
<AlertDialogAction v-bind="delegatedProps" :class="cn(buttonVariants(), props.class)"> <AlertDialogAction
v-bind="delegatedProps"
:class="cn(buttonVariants(), props.class)"
>
<slot/> <slot/>
</AlertDialogAction> </AlertDialogAction>
</template> </template>

View file

@ -8,11 +8,14 @@ const props = defineProps<{
</script> </script>
<template> <template>
<div data-slot="card" :class="cn( <div
data-slot="card"
:class="cn(
'bg-card text-card-foreground flex flex-col gap-6 rounded-md border p-4 shadow-sm', 'bg-card text-card-foreground flex flex-col gap-6 rounded-md border p-4 shadow-sm',
props.class, props.class,
) )
"> "
>
<slot/> <slot/>
</div> </div>
</template> </template>

View file

@ -8,10 +8,7 @@ const props = defineProps<{
</script> </script>
<template> <template>
<div <div data-slot="card-content" :class="cn('flex flex-col', props.class)">
data-slot="card-content"
:class="cn('flex flex-col', props.class)"
>
<slot/> <slot/>
</div> </div>
</template> </template>

View file

@ -5,10 +5,7 @@ const props = defineProps<CollapsibleContentProps>();
</script> </script>
<template> <template>
<CollapsibleContent <CollapsibleContent data-slot="collapsible-content" v-bind="props">
data-slot="collapsible-content"
v-bind="props"
>
<slot/> <slot/>
</CollapsibleContent> </CollapsibleContent>
</template> </template>

View file

@ -5,10 +5,7 @@ const props = defineProps<CollapsibleTriggerProps>();
</script> </script>
<template> <template>
<CollapsibleTrigger <CollapsibleTrigger data-slot="collapsible-trigger" v-bind="props">
data-slot="collapsible-trigger"
v-bind="props"
>
<slot/> <slot/>
</CollapsibleTrigger> </CollapsibleTrigger>
</template> </template>

View file

@ -25,7 +25,8 @@ const isRender = computed(
<Primitive <Primitive
v-if="isRender" v-if="isRender"
data-slot="command-empty" data-slot="command-empty"
v-bind="delegatedProps" :class="cn('py-6 text-center text-sm', props.class)" v-bind="delegatedProps"
:class="cn('py-6 text-center text-sm', props.class)"
> >
<slot/> <slot/>
</Primitive> </Primitive>

View file

@ -44,7 +44,10 @@ onUnmounted(() => {
:class="cn('text-foreground overflow-hidden p-1', props.class)" :class="cn('text-foreground overflow-hidden p-1', props.class)"
:hidden="isRender ? undefined : true" :hidden="isRender ? undefined : true"
> >
<ListboxGroupLabel v-if="heading" class="px-2 py-1.5 text-xs font-medium text-muted-foreground"> <ListboxGroupLabel
v-if="heading"
class="px-2 py-1.5 text-xs font-medium text-muted-foreground"
>
{{ heading }} {{ heading }}
</ListboxGroupLabel> </ListboxGroupLabel>
<slot/> <slot/>

View file

@ -13,10 +13,7 @@ const forwarded = useForwardPropsEmits(props, emits);
</script> </script>
<template> <template>
<DialogRoot <DialogRoot data-slot="dialog" v-bind="forwarded">
data-slot="dialog"
v-bind="forwarded"
>
<slot/> <slot/>
</DialogRoot> </DialogRoot>
</template> </template>

View file

@ -5,10 +5,7 @@ const props = defineProps<DialogCloseProps>();
</script> </script>
<template> <template>
<DialogClose <DialogClose data-slot="dialog-close" v-bind="props">
data-slot="dialog-close"
v-bind="props"
>
<slot/> <slot/>
</DialogClose> </DialogClose>
</template> </template>

View file

@ -5,10 +5,7 @@ const props = defineProps<DialogTriggerProps>();
</script> </script>
<template> <template>
<DialogTrigger <DialogTrigger data-slot="dialog-trigger" v-bind="props">
data-slot="dialog-trigger"
v-bind="props"
>
<slot/> <slot/>
</DialogTrigger> </DialogTrigger>
</template> </template>

View file

@ -13,10 +13,7 @@ const forwarded = useForwardPropsEmits(props, emits);
</script> </script>
<template> <template>
<DrawerRoot <DrawerRoot data-slot="drawer" v-bind="forwarded">
data-slot="drawer"
v-bind="forwarded"
>
<slot/> <slot/>
</DrawerRoot> </DrawerRoot>
</template> </template>

View file

@ -6,10 +6,7 @@ const props = defineProps<DrawerCloseProps>();
</script> </script>
<template> <template>
<DrawerClose <DrawerClose data-slot="drawer-close" v-bind="props">
data-slot="drawer-close"
v-bind="props"
>
<slot/> <slot/>
</DrawerClose> </DrawerClose>
</template> </template>

View file

@ -29,7 +29,9 @@ const forwarded = useForwardPropsEmits(props, emits);
props.class, props.class,
)" )"
> >
<div class="bg-muted mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block" /> <div
class="bg-muted mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block"
/>
<slot/> <slot/>
</DrawerContent> </DrawerContent>
</DrawerPortal> </DrawerPortal>

Some files were not shown because too many files have changed in this diff Show more