docs: 📝 Add ContentFormat docs

This commit is contained in:
Jesse Wierzbinski 2024-07-22 20:21:38 +02:00
parent eb5a58621a
commit 9b75c25fa0
No known key found for this signature in database
4 changed files with 187 additions and 18 deletions

View file

@ -13,10 +13,10 @@ The `User` entity represents an account on a Lysand instance. Users can post [No
<Col>
<Properties>
<Property name="avatar" type="ContentFormat" required={true}>
<Property name="avatar" type="ContentFormat" required={true} typeLink="/structures/content-format">
The user's avatar. Must be an image format (`image/*`).
</Property>
<Property name="bio" type="ContentFormat" required={true}>
<Property name="bio" type="ContentFormat" required={true} typeLink="/structures/content-format">
Short description of the user. Must be text format (`text/*`).
</Property>
<Property name="display_name" type="string" required={false}>
@ -37,7 +37,7 @@ The `User` entity represents an account on a Lysand instance. Users can post [No
Can only contain the following characters: `a-z` (lowercase), `0-9`, `_` and `-`. Should be limited to reasonable lengths.
</Property>
<Property name="header" type="ContentFormat" required={false}>
<Property name="header" type="ContentFormat" required={false} typeLink="/structures/content-format">
A header image for the user's profile. Also known as a cover photo or a banner. Must be an image format (`image/*`).
</Property>
<Property name="public_key" type="PublicKey" required={true}>
@ -65,19 +65,19 @@ The `User` entity represents an account on a Lysand instance. Users can post [No
The user's federation outbox. Refer to the [federation documentation](/federation).
</Property>
<Property name="followers" type="URI" required={true}>
User's followers. URI must resolve to a [Collection](/structures/collections) of [User](/entities/users) entities.
User's followers. URI must resolve to a [Collection](/structures/collection) of [User](/entities/users) entities.
</Property>
<Property name="following" type="URI" required={true}>
Users that the user follows. URI must resolve to a [Collection](/structures/collections) of [User](/entities/users) entities.
Users that the user follows. URI must resolve to a [Collection](/structures/collection) of [User](/entities/users) entities.
</Property>
<Property name="likes" type="URI" required={true}>
User's likes. URI must resolve to a [Collection](/structures/collections) of [Like](/entities/likes) entities.
User's likes. URI must resolve to a [Collection](/structures/collection) of [Like](/entities/likes) entities.
</Property>
<Property name="dislikes" type="URI" required={true}>
User's dislikes. URI must resolve to a [Collection](/structures/collections) of [Dislike](/entities/dislikes) entities.
User's dislikes. URI must resolve to a [Collection](/structures/collection) of [Dislike](/entities/dislikes) entities.
</Property>
<Property name="featured" type="URI" required={true}>
[Notes](/entities/notes) that the user wants to feature (also known as "pin") on their profile. URI must resolve to a [Collection](/structures/collections) of [Note](/entities/notes) entities.
[Notes](/entities/notes) that the user wants to feature (also known as "pin") on their profile. URI must resolve to a [Collection](/structures/collection) of [Note](/entities/notes) entities.
</Property>
</Properties>

View file

@ -0,0 +1,153 @@
export const metadata = {
title: 'ContentFormat',
description: 'Definition of the ContentFormat structure',
}
# ContentFormat
The `ContentFormat` structure is used to represent content with metadata. It supports multiple content types for the same file, such as a PNG image and a WebP image. {{ className: 'lead' }}
A `ContentFormat` structure is defined as follows:
```typescript
type ContentType = `${string}/${string}`;
type ContentFormat = {
[key: ContentType]: {
...
};
}
```
<Note>
Each piece of data in the `ContentFormat` structure is meant to be a different representation of the same content. For example, a PNG image and its WebP version are different representations of the same image. Do not mix unrelated files or data in the same `ContentFormat` structure.
**Good:**
```json
{
"image/png": {
...
},
"image/webp": {
...
}
}
```
**Bad:**
```json
{
"image/png": {
...
},
"application/json": {
...
}
}
```
</Note>
## Implementation Guidelines
### Text
Implementations should always process text content with the richest format available, such as HTML. However, they should also provide other formats like plain text and Markdown for compatibility with other systems.
HTML is the recommended content type for text content, and as such every text content should have an HTML representation. If the content is not HTML, it should be converted to HTML using appropriate conversion rules.
Rich formats include:
- `text/html`
- `text/markdown`
- `text/x.misskeymarkdown` (Misskey Flavoured Markdown, common on ActivityPub
Clients should display the richest possible format available. If the client does not support the richest format, it should fall back to the next richest format.
### Images
It is a good idea to provide at least two versions of an image (if possible): one in the original format and another in a more efficient format like WebP/AVIF. This allows clients to choose the most suitable format based on their capabilities.
## Entity Definition
<Row>
<Col>
<Properties>
<Property name="content" type="string | URI" required={true}>
Structure data. If `Content-Type` is a binary format, this field should be a URI to the binary data. Otherwise, it should be the content itself. Refer to the `remote` property for more information.
</Property>
<Property name="remote" type="boolean" required={true}>
If `true`, the content is hosted remotely and should be fetched by the client. If `false`, the content is embedded in the entity.
</Property>
<Property name="description" type="string" required={false}>
A human-readable description of the content. Also known as `alt` text.
</Property>
<Property name="size" type="number" required={false} numberType="u64">
Size of the content in bytes.
</Property>
<Property name="hash" type="Hash" required={false}>
Hash of the content.
```typescript
type HashNames = "sha256" | "sha512" | "sha3-256" | "sha3-512" | "blake2b-256" | "blake2b-512" | "blake3-256" | "blake3-512" | "md5" | "sha1" | "sha224" | "sha384" | "sha3-224" | "sha3-384" | "blake2s-256" | "blake2s-512" | "blake3-224" | "blake3-384";
type Hash = {
[key in HashNames]: string;
}
```
</Property>
<Property name="blurhash" type="string" required={false}>
Image in [BlurHash](https://blurha.sh/) format.
</Property>
<Property name="width" type="number" required={false} numberType="u64">
Width of the content in pixels. Only applicable to content types that have a width.
</Property>
<Property name="height" type="number" required={false} numberType="u64">
Height of the content in pixels. Only applicable to content types that have a height.
</Property>
<Property name="fps" type="number" required={false} numberType="u64">
Frames per second. Only applicable to video content.
</Property>
<Property name="duration" type="number" required={false} numberType="f64">
Duration of the content in seconds. Only applicable to content types that have a duration.
</Property>
</Properties>
</Col>
<Col sticky>
```jsonc {{ 'title': 'Images' }}
{
"image/png": {
"content": "https://cdn.example.com/attachments/ece2f9d9-27d7-457d-b657-4ce9172bdcf8.png",
"remote": true,
"description": "A jolly horse running through mountains",
"size": 453933,
"hash": {
"sha256": "91714fc336210d459d4f9d9233de663be2b87ffe923f1cfd76ece9d06f7c965d"
},
"blurhash": "UUKJ^v~q9G%L~qRj9GofRjofRjof",
"width": 1920,
"height": 1080
}
}
```
```jsonc {{ 'title': 'Text Formats' }}
{
"text/plain": {
"content": "The consequences of today are determined by the actions of the past. To change your future, alter your decisions today.",
"remote": false
},
"text/markdown": {
"content": "> The consequences of today are determined by the actions of the past.\n> To change your future, alter your decisions today.",
"remote": false
},
"text/html": {
"content": "<blockquote><p>The consequences of today are determined by the actions of the past.</p><p>To change your future, alter your decisions today.</p></blockquote>",
"remote": false
}
}
```
</Col>
</Row>

View file

@ -255,20 +255,17 @@ export const navigation: NavGroup[] = [
{ title: "Webhooks", href: "/webhooks" }, */
],
},
{
title: "Structures",
links: [
{ title: "ContentFormat", href: "/structures/content-format" },
{ title: "Collection", href: "/structures/collection" },
],
},
{
title: "Entities",
links: [{ title: "Users", href: "/entities/users" }],
},
/* {
title: "Resources",
links: [
{ title: "Contacts", href: "/contacts" },
{ title: "Conversations", href: "/conversations" },
{ title: "Messages", href: "/messages" },
{ title: "Groups", href: "/groups" },
{ title: "Attachments", href: "/attachments" },
],
}, */
];
export function Navigation(props: ComponentPropsWithoutRef<"nav">) {

View file

@ -92,17 +92,25 @@ export function Properties({ children }: { children: ReactNode }) {
);
}
const numberTypeTooltips = {
f64: "64-bit floating-point number",
i64: "64-bit signed integer",
u64: "64-bit unsigned integer",
};
export function Property({
name,
children,
type,
typeLink,
numberType,
required,
}: {
name: string;
children: ReactNode;
type?: string;
typeLink?: string;
numberType?: "f64" | "i64" | "u64";
required?: boolean;
}) {
return (
@ -120,6 +128,17 @@ export function Property({
</dd>
</>
)}
{numberType && (
<>
<dt className="sr-only">Type</dt>
<dd
className="inline-flex items-center rounded-md bg-blue-50 px-2 py-0 text-xs font-medium text-blue-700 ring-1 ring-inset ring-blue-500/10 dark:bg-blue-500/10 dark:text-blue-100 dark:ring-blue-200/20 hover:cursor-pointer"
title={numberTypeTooltips[numberType]}
>
{numberType}
</dd>
</>
)}
{type && (
<>
<dt className="sr-only">Type</dt>