Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .nvmrc

This file was deleted.

2 changes: 1 addition & 1 deletion apps/web/content/docs/dev/passkeys/index.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Passkeys (WebAuthn)
description: Let members sign in with Face ID, Touch ID, Windows Hello or a security key. Configure the RP ID and origins, run the migration, and learn how the WebAuthn ceremonies work in VitNode.
icon: Fingerprint
icon: FingerprintPattern
---

import { Tab, Tabs } from "fumadocs-ui/components/tabs"
Expand Down
2 changes: 1 addition & 1 deletion apps/web/content/docs/dev/passkeys/meta.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"title": "Passkeys",
"description": "Passwordless sign-in with WebAuthn passkeys - Face ID, Touch ID, Windows Hello or a security key",
"icon": "Fingerprint",
"icon": "FingerprintPattern",
"pages": ["index", "using-passkeys", "admincp"]
}
4 changes: 2 additions & 2 deletions apps/web/content/docs/dev/performance.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ Heavy editors (like Tiptap) or complex modals should be lazy-loaded with `React.

```tsx title="plugins/blog/src/views/admin/article-editor.tsx"
import React, { Suspense } from 'react'
import { Loader } from '@vitnode/core/components/ui/loader'
import { Spinner } from '@vitnode/core/components/ui/spinner'

// [!code ++:6]
const RichEditor = React.lazy(async () =>
Expand All @@ -75,7 +75,7 @@ const RichEditor = React.lazy(async () =>
)

export const ArticleEditor = (props) => (
<Suspense fallback={<Loader />}>
<Suspense fallback={<Spinner size="xl" />}>
<RichEditor {...props} />
</Suspense>
)
Expand Down
1 change: 1 addition & 0 deletions apps/web/content/docs/ui/accordion.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
title: Accordion
description: A component that allows users to expand and collapse sections of content.
icon: Rows3
---

## Preview
Expand Down
7 changes: 4 additions & 3 deletions apps/web/content/docs/ui/alert-dialog.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
title: Alert Dialog
description: Display important messages to users in a modal dialog.
icon: MessageSquareWarning
---

A modal dialog that interrupts the user with important content and expects a
Expand Down Expand Up @@ -30,18 +31,18 @@ import { Button } from '@vitnode/core/components/ui/button';

```tsx
<AlertDialog>
<AlertDialogTrigger render={<Button variant="outline">Show Dialog</Button>} />
<AlertDialogTrigger render={<Button variant="destructive">Delete account</Button>} />
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogTitle>Delete your account?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete your account
and remove your data from our servers.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction>Continue</AlertDialogAction>
<AlertDialogAction variant="destructive">Delete account</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
Expand Down
1 change: 1 addition & 0 deletions apps/web/content/docs/ui/alert.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
title: Alert
description: Display a short, important message to users.
icon: CircleAlert
---

## Preview
Expand Down
67 changes: 67 additions & 0 deletions apps/web/content/docs/ui/aspect-ratio.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
title: Aspect Ratio
description: Keep images, videos and embeds at the right shape, whatever the width.
icon: Ratio
---

## Preview

<Preview name="aspect-ratio" />

## Usage

```ts
import { AspectRatio } from '@vitnode/core/components/ui/aspect-ratio'
```

```tsx
<AspectRatio className="overflow-hidden rounded-lg" ratio={16 / 9}>
<img
alt="A mountain at sunrise"
className="size-full object-cover"
src="/images/mountain.webp"
/>
</AspectRatio>
```

The box takes the full width it is given and works out its height from
`ratio`, so the page doesn't jump around while the image loads. Give the child
`size-full` and `object-cover` to fill the box without stretching.

## Common ratios

| Ratio | Good for |
| -------- | ------------------------------ |
| `16 / 9` | Videos, hero images, banners |
| `4 / 3` | Photos, product shots |
| `1` | Avatars, thumbnails, galleries |
| `9 / 16` | Phone screenshots, stories |

## Video embed

```tsx
<AspectRatio ratio={16 / 9}>
<iframe
allowFullScreen
className="size-full rounded-lg"
src="https://www.youtube-nocookie.com/embed/VIDEO_ID"
title="Getting started with VitNode"
/>
</AspectRatio>
```

## Props

import { TypeTable } from 'fumadocs-ui/components/type-table'

<TypeTable
type={{
ratio: {
description: 'Width divided by height, for example `16 / 9`.',
type: 'number',
default: '1',
},
}}
/>

Every other prop goes straight to the underlying `div`.
179 changes: 179 additions & 0 deletions apps/web/content/docs/ui/attachment.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
---
title: Attachment
description: Upload one file or a sortable gallery, with drag and drop, progress, previews and errors.
icon: Paperclip
---

## Preview

<Preview name="attachment-auto-form" />

## Usage

import { Tab, Tabs } from "fumadocs-ui/components/tabs";

<Tabs items={['Auto Form', 'Manual']}>
<Tab value="Auto Form">

Two fields cover every upload: `AutoFormFile` for one file and `AutoFormFiles`
for many.

```ts
import { z } from 'zod'
import { AutoForm } from '@vitnode/core/components/form/auto-form'
import { AutoFormFile } from '@vitnode/core/components/form/fields/file'
import { AutoFormFiles } from '@vitnode/core/components/form/fields/files'
```

The form stores **file ids**, not the files themselves. One file is a number,
many files are an array of numbers:

```ts
const formSchema = z.object({
avatar: z.number().nullable().default(null),
gallery: z.array(z.number()).max(4).default([]),
})
```

```tsx
<AutoForm
formSchema={formSchema}
fields={[
{
id: 'avatar',
component: props => (
<AutoFormFile
{...props}
allowedExtensions={['.png', '.jpg', '.webp']}
label="Avatar"
maxBytes={5 * 1024 * 1024}
onUpload={uploadFile}
/>
),
},
{
id: 'gallery',
component: props => (
<AutoFormFiles
{...props}
label="Gallery"
maxBytes={5 * 1024 * 1024}
maxItems={4}
onUpload={uploadFile}
/>
),
},
]}
/>
```

</Tab>

<Tab value="Manual">

<Preview name="attachment" />

```ts
import {
Attachment,
AttachmentAction,
AttachmentActions,
AttachmentContent,
AttachmentDescription,
AttachmentMedia,
AttachmentTitle,
} from '@vitnode/core/components/ui/attachment'
```

```tsx
<Attachment>
<AttachmentMedia variant="image">
<img alt="" src={file.url} />
</AttachmentMedia>
<AttachmentContent>
<AttachmentTitle>{file.name}</AttachmentTitle>
<AttachmentDescription>48 KB</AttachmentDescription>
</AttachmentContent>
<AttachmentActions>
<AttachmentAction aria-label={`Remove ${file.name}`} onClick={remove}>
<XIcon />
</AttachmentAction>
</AttachmentActions>
</Attachment>
```

</Tab>
</Tabs>

## Uploading files

`onUpload` receives the `File` the user picked and returns the stored file. The
field shows a spinner while it runs and an error if it throws, then saves the
returned `id` into the form.

```ts
import type { AutoFormFileValue } from '@vitnode/core/components/form/fields/file'

const uploadFile = async (file: File): Promise<AutoFormFileValue> => {
const body = new FormData()
body.append('file', file)

const res = await fetch('/api/my-plugin/uploads', { body, method: 'POST' })
if (!res.ok) throw new Error('Upload failed')

return await res.json() // { id, name, size, url, mimeType? }
}
```

Editing something that already has files? Pass them in with `file={existing}`
on `AutoFormFile` or `files={existing}` on `AutoFormFiles`, and the field shows
their names and previews.

## Limits

Both fields check files **before** uploading, so nobody waits for a 2 GB video
just to hear it was too big:

- `maxBytes` sets the largest accepted file. It is required.
- `allowedExtensions` takes lowercase extensions with a leading dot, like
`['.pdf', '.png']`.
- `allowedMimeTypes` takes MIME types, for example
`['image/png', 'application/pdf']`.

`AutoFormFiles` also takes `maxItems` and `minItems`. People can reorder files
by dragging or with the keyboard. Pass `ordered={false}` when the order doesn't
matter.

Check the same limits again on your server. The browser check saves people a
wasted upload, but it can't stop someone determined.

## States

Set `state` on `Attachment` to match what the file is doing:

| State | Looks like |
| ------------ | ------------------------------------------------ |
| `done` | The default, a finished file |
| `idle` | Dashed border, waiting for a file |
| `uploading` | Shimmering title, dimmed preview |
| `processing` | Same as uploading, for server-side work |
| `error` | Red border and description. Say what went wrong. |

## Layouts

- `orientation="vertical"` turns the attachment into a card with a big preview,
great for galleries.
- `size="sm"` or `size="xs"` shrink it for chat composers and tight lists.
- Wrap several in `AttachmentGroup` for a horizontally scrolling row that fades
out at the edges.
- `AttachmentTrigger` makes the whole attachment clickable. Render it as a link to
open the file.

## Accessibility

- Give every `AttachmentAction` an `aria-label` that names the file, like
"Remove report.pdf". "Remove" alone is a riddle when there are five files.
- Previews in `AttachmentMedia` are decorative next to the file name, so
`alt=""` is right.
- The AutoForm fields announce reordering to screen readers and show every
rejected file with the reason.
1 change: 1 addition & 0 deletions apps/web/content/docs/ui/auto-form.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
title: Auto Form
description: Component creates form based on Zod schemas & TanStack Form with validation
icon: ClipboardPen
---

## Preview
Expand Down
Loading
Loading