From c67ba072fa68295bf86de81d42c59a53ffa8576b Mon Sep 17 00:00:00 2001 From: Brad Estey Date: Tue, 29 Sep 2026 00:34:45 -0400 Subject: [PATCH] Revise readme. --- README.md | 448 ++++++++++++++++++------------------------------------ 1 file changed, 145 insertions(+), 303 deletions(-) diff --git a/README.md b/README.md index aa3e651..6d8b4ad 100644 --- a/README.md +++ b/README.md @@ -1,122 +1,74 @@ # sequential.work Lexicons -Shared lexicon schemas for comics on AT Protocol. -## Overview +Lexicon schemas for comics on AT Protocol, under `work.sequential.*`. Three formats: -This repository contains the lexicon definitions for sequential.work. Records live under the `work.sequential.*` namespace and support three formats: +- **Vertical**: webtoons, read by scrolling. Chapters are made of slices. +- **Page**: comic books. Chapters are made of pages, divided into panels. +- **Strip**: newspaper strips. Strips belong directly to their series. -- **Vertical**: webtoon-style comics read by scrolling. Chapters are made of slices. -- **Page**: traditional comic books. Chapters are made of pages, and pages are divided into panels. -- **Strip**: newspaper-style comic strips. Strips belong directly to their series. +## Setup -All formats support localized images and text, alt text for every image, and work-in-progress versions of the art. Chapters can have a primary cover plus variant covers. Series have 1–3 categories plus free-form tags. Readers can subscribe to series and curate reading lists and reading orders of chapters and strips. - -Many values are open-ended `string`s rather than enums, so each AppView can support its own set of values. - -Every list and free-form string has a maximum length, set generously in `src/constants.ts`. Raising a limit later is backward compatible; lowering one or adding a new one is not. - -## Installation - -Requires Node 22.18 or later and pnpm. +Requires Node 22.18+ and pnpm. ```bash pnpm install +pnpm lexicon:emit # TypeScript → JSON schemas in out/ +pnpm lexicon:import # JSON schemas → TypeScript +pnpm typecheck ``` -## Usage +## Concepts -```bash -pnpm lexicon:emit # Generate JSON schemas from TypeScript -pnpm lexicon:import # Generate TypeScript from JSON schemas -pnpm typecheck # Type-check the TypeScript sources -``` +### Record Structure -## Project Structure +**Vertical** ``` -/ -├── src/ -│ ├── constants.ts # Shared values (size limits, image types) -│ └── lexicons/ # TypeScript lexicon definitions (source) -│ ├── work.sequential.chapter.ts -│ ├── work.sequential.cover.ts -│ ├── work.sequential.defs.ts -│ ├── work.sequential.graph.list.ts -│ ├── work.sequential.graph.subscription.ts -│ ├── work.sequential.page.ts -│ ├── work.sequential.series.ts -│ ├── work.sequential.slice.ts -│ └── work.sequential.strip.ts -└── out/ # Generated JSON schemas (not committed) - └── work/ - └── sequential/ - ├── chapter.json - ├── cover.json - ├── defs.json - ├── graph/ - │ ├── list.json - │ └── subscription.json - ├── page.json - ├── series.json - ├── slice.json - └── strip.json +series (format: vertical) +└── chapters[] → chapter + ├── covers[] → cover (first = primary, rest = variants) + └── slices[] → slice ``` -## Concepts - -### Record Structure +**Page** ``` -series (format: vertical | page) -└── chapter - ├── covers[] → cover (first = primary, rest = variants) - ├── slices[] → slice (format: vertical) - └── pages[] → page (format: page) - └── panels[] (inline) +series (format: page) +└── chapters[] → chapter + ├── covers[] → cover (first = primary, rest = variants) + └── pages[] → page + └── panels[] (inline) +``` +**Strip** + +``` series (format: strip) -└── strip (many, ordered by createdAt) +└── strip (ordered by createdAt) └── panels[] (inline) ``` -A parent record holds an ordered list of AT-URIs to its children. Each child points back to its parent with an AT-URI. AT-URIs stored in records should always use the DID form (`at://did:plc:…/…`), never a handle: handles can change, and a handle-based link breaks when they do. The schema can't enforce this, so AppViews should resolve handles to DIDs before writing. Covers use the same pattern as slices and pages, so adding a variant cover means creating a `cover` record and adding its URI to `chapter.covers`. - -Strips are the exception. A long-running strip can have thousands of entries, and keeping them all in one ever-growing list on the series record would be fragile: every new strip rewrites the whole list, and PDSes limit record size. Strips only point to their series, and AppViews order them by `createdAt`. - -Panels are stored inline on their page or strip rather than as separate records. A panel only has meaning relative to its image, and storing it inline means re-cutting a page's panels is a single record update. +- Parents list their children in order, and children point back to their parent. +- Strips aren't listed on the series, because a long-running strip would outgrow the record. +- Panels are inline, so re-cutting a page's panels is one record update. +- Use DID-form AT-URIs (`at://did:plc:…`), never handles. AppViews should resolve handles before writing. ### Reader Records -Readers write these records in their own repos. They point to creators' records but never change them. - -#### Subscriptions - -A `graph.subscription` subscribes a reader to a series, like following it. AppViews use subscriptions to notify readers about new chapters or strips. To unsubscribe, the reader deletes the record. +Readers write these in their own repos. -#### Reading Lists - -A `graph.list` is a curated reading list or reading order, like a playlist. Anyone can create one in their own repo, and its items can point to any creator's chapters or strips: - -``` -graph.list -└── items[] → chapter or strip (in the curator's order) -``` - -The order of `items` is the reading order, so a list can lay out a crossover event across several series, or collect favorite strips. Lists only point to chapters and strips, not whole series. AT-URIs can't be restricted to a record type in the schema, so AppViews should skip items that point to anything else, or to records that have been deleted. +- **`graph.subscription`**: follows a series. Delete it to unsubscribe. +- **`graph.list`**: a reading list or reading order of chapters and strips from any creator. AppViews skip items that aren't chapters or strips, or that were deleted. ### Publishing -AT Protocol repos are public, so anything written to a repo can be read by anyone. There are no draft records. Drafts and scheduled posts are kept privately by the AppView and written to the repo when they're published, which makes `createdAt` the publish date. - -AppViews should publish a chapter atomically with `com.atproto.repo.applyWrites`: the chapter, its covers, and its slices or pages go in one commit. Choosing the chapter's record key ahead of time lets its children point to it within that same commit, so readers never see a half-uploaded chapter. - -A creator can also publish a chapter a little at a time, such as a page a day. Set the chapter's `status` to `ongoing`, then add each new slice or page in its own commit: create the child record and update the chapter's `slices` or `pages` list together. Set `status` to `completed` (or remove it) when the chapter is complete. AppViews can use this to show a chapter as ongoing, and to notify subscribers about new pages. - -When importing older work, set `createdAt` to the original publication date. +- Repos are public, so drafts and scheduled posts stay in the AppView until published. `createdAt` is the publish date. For older work, use the original date. +- Publish a chapter in one `com.atproto.repo.applyWrites` commit: the chapter, its covers, and its slices or pages. Pick the chapter's record key first so the children can point to it. +- To release a chapter a page at a time, set its `status` to `ongoing`. Each new page is one commit: the page plus the updated `pages` list. Set `status` to `completed` when done. ### Localization -A single record holds every language. Titles, descriptions, images and alt text are lists of entries tagged with a BCP-47 language code (e.g. `en`, `pt-BR`, `zh-Hant`): +One record holds every language. Text and images are lists tagged with BCP-47 codes: ```json "title": [ @@ -125,20 +77,18 @@ A single record holds every language. Titles, descriptions, images and alt text ] ``` -Panel positions, versions and credits are shared by every language, so translating a comic means adding re-lettered images and translated text, not duplicating records. AppViews show the reader's language and fall back to the series' `nativeLanguage`. - -Length limits count graphemes (visible characters) rather than bytes, so CJK text gets the same room as English. +- Panels, versions and credits are shared, so a translation only adds images and text. +- Apps fall back to the series' `nativeLanguage`. +- Length limits count graphemes, so CJK text gets the same room as English. -### Panel-by-Panel Reading +### Panels -Pages and strips can be read panel by panel. Each panel in `panels` has four optional pieces that control how an app shows it: +Pages and strips can be read panel by panel. Every panel field is optional. -- **`bounds`**: a simple rectangle around the panel. The app fits it in the viewport. -- **`background`**: fills the viewport around the panel when the panel's aspect ratio doesn't match the viewport's. It's a color plus an opacity (`a`, 0–100). At `0`, the surrounding page stays visible around the panel. At `100`, the page is hidden behind a solid color so the panel stands on its own. Values in between dim the page. Omitting it means fully transparent. -- **`mask`**: a black and white PNG of the panel's exact shape, for round or irregular panels, or art that bleeds past the panel border. White shows the art, black hides it, and grays blend for soft edges. -- **`transition`**: how the app moves to this panel from the previous one. `none` (the default) switches instantly, `pan` moves the view across the image to the panel, `fade` crossfades from the previous panel, and `zoom` pulls back to show the whole image then pushes in to the panel. Reading backward plays the same transition in reverse. The AppView decides how long transitions take, and treats unknown values as `none`. - -The mask covers the panel's `bounds`, at the same size or at least the same aspect ratio. Draw the bounds around everything the panel shows, including art that bleeds past its border, and the mask trims that rectangle to the panel's real shape. A mask needs bounds to line up with, so apps ignore it on panels without bounds. Changing a panel's bounds means exporting a new mask. +- **`bounds`**: the rectangle the app fits to the viewport. +- **`background`**: color around the panel. Opacity `0` shows the page, `100` hides it. +- **`mask`**: black and white PNG of the panel's exact shape, covering its `bounds`. Needs `bounds`. +- **`transition`**: `none` (default), `pan`, `fade` or `zoom`. Reversed when reading backward. ```json { @@ -152,36 +102,21 @@ The mask covers the panel's `bounds`, at the same size or at least the same aspe ### Alt Text -Alt text is never stored on an image. It's a list of `defs#localizedAlt` entries, so it can be translated independently of which localized images exist. Slices and covers have an `alt` list on the record. Pages and strips have one on each panel. - -Alt text should both describe the art and transcribe all of the lettering: dialogue, captions and sound effects. Screen reader users then get the whole story, and the text can be searched and translated. +- Slices and covers have `alt` on the record. Pages and strips have it on each panel. +- Describe the art and transcribe all lettering: dialogue, captions and sound effects. ```json -"alt": [ - { "lang": "en", "text": "The hero stands over the defeated monster, bored. HERO: \"OK.\" SFX: THOOM" } -] +"alt": [{ "lang": "en", "text": "The hero stands over the defeated monster, bored. HERO: \"OK.\" SFX: THOOM" }] ``` ### Labels -Content warnings are AT Protocol self-labels, set on the series rather than on individual chapters or strips. - -**Standard values.** Bluesky and moderation services already understand these: +Content warnings are AT Protocol self-labels on the series. -- `porn`: explicit sexual content -- `sexual`: sexually suggestive content -- `nudity`: non-sexual nudity -- `graphic-media`: gore and graphic violence +- **Standard**: `porn`, `sexual`, `nudity`, `graphic-media` +- **Suggested**: `profanity`, `violence`, `drugs`, `self-harm`, `abuse` -**Suggested additional values.** AppViews may support these or define their own. Apps that don't recognize a value ignore it: - -- `profanity`: strong language -- `violence`: non-graphic violence, like fights and action -- `drugs`: drug or alcohol use -- `self-harm`: self-harm or suicide -- `abuse`: domestic, child or sexual abuse - -Label values are lowercase with hyphens. +Apps ignore values they don't recognize. ```json "labels": { @@ -192,237 +127,144 @@ Label values are lowercase with hyphens. ## Lexicons -- [`work.sequential.series`](#worksequentialseries) -- [`work.sequential.chapter`](#worksequentialchapter) -- [`work.sequential.cover`](#worksequentialcover) -- [`work.sequential.slice`](#worksequentialslice) -- [`work.sequential.page`](#worksequentialpage) -- [`work.sequential.strip`](#worksequentialstrip) -- [`work.sequential.graph.list`](#worksequentialgraphlist) -- [`work.sequential.graph.subscription`](#worksequentialgraphsubscription) -- [`work.sequential.defs`](#worksequentialdefs) +`defs#x` is short for `work.sequential.defs#x`. On arrays, `(1–100)` and `(≤ 100)` are length limits. ### `work.sequential.series` -A comic series. - -| Property | Type | Required | Description | +| Property | Type | Req | Notes | | --- | --- | --- | --- | -| `title` | array of `defs#localizedTitle` (min 1, max 100) | yes | Localized title of the series. | -| `banner` | blob (jpeg/png/webp/avif, max 4 MiB) | yes | Banner image. 3:1 aspect ratio (e.g. 3000×1000). | -| `poster` | blob (jpeg/png, max 4 MiB) | yes | Open Graph share image. 1.91:1 aspect ratio (e.g. 1200×630). JPEG or PNG only, because many link preview crawlers don't support newer formats. | -| `description` | array of `series#localizedDescription` (min 1, max 100) | yes | Localized short description of the series. | -| `createdAt` | string (datetime) | yes | When the series was created. | -| `categories` | array of `defs#category` (min 1, max 3) | yes | Categories, most relevant first. | -| `tags` | array of `defs#tag` (max 10) | no | Free-form tags for discovery. | -| `format` | string | yes | Reading format. Known values: `vertical`, `page`, `strip`. | -| `readingDirection` | string (default `ltr`) | no | Horizontal reading direction of panels and pages. Known values: `ltr`, `rtl` (e.g. manga). | -| `status` | string (default `ongoing`) | no | Publication status. Known values: `ongoing`, `completed`, `hiatus`, `cancelled`. | -| `nativeLanguage` | string (language) | no | BCP-47 tag of the language the series was created in, e.g. `ja`. Readers fall back to it when their language isn't available. | -| `labels` | `com.atproto.label.defs#selfLabels` | no | Self-labels and content warnings for the series. See [Labels](#labels). | -| `chapters` | array of at-uri (max 5000) | no | Ordered list of chapters. Not used by the `strip` format. | - -#### `series#localizedDescription` - -Same as `defs#localizedDescription`, but limited to 255 graphemes so it fits on series cards. +| `title` | `defs#localizedTitle[]` (1–100) | yes | | +| `banner` | blob (≤ 4 MiB) | yes | 3:1, e.g. 3000×1000. | +| `poster` | blob (jpeg/png, ≤ 4 MiB) | yes | Share image. 1.91:1, e.g. 1200×630. | +| `description` | `series#localizedDescription[]` (1–100) | yes | Max 255 graphemes each. | +| `createdAt` | datetime | yes | | +| `categories` | `defs#category[]` (1–3) | yes | Most relevant first. | +| `tags` | `defs#tag[]` (≤ 10) | no | | +| `format` | string | yes | `vertical`, `page`, `strip` | +| `readingDirection` | string | no | `ltr` (default), `rtl` | +| `status` | string | no | `ongoing` (default), `completed`, `hiatus`, `cancelled` | +| `nativeLanguage` | language | no | Fallback language. | +| `labels` | `selfLabels` | no | See [Labels](#labels). | +| `chapters` | at-uri[] (≤ 5000) | no | Not used by `strip`. | ### `work.sequential.chapter` -A chapter in a series. Use `slices` or `pages`, whichever matches the series `format`. - -| Property | Type | Required | Description | +| Property | Type | Req | Notes | | --- | --- | --- | --- | -| `series` | string (at-uri) | yes | AT-URI of the parent series. | -| `title` | array of `defs#localizedTitle` (max 100) | no | Localized chapter title or number. | -| `description` | array of `defs#localizedDescription` (max 100) | no | Localized short description of the chapter. | -| `createdAt` | string (datetime) | yes | When the chapter was published. | -| `status` | string (default `completed`) | no | Publication status. Known values: `ongoing`, `completed`, `hiatus`, `cancelled`. Use `ongoing` while the chapter is still being published. | -| `covers` | array of at-uri (min 1, max 500) | yes | Ordered `cover` records. The first is the primary cover and the rest are variants. | -| `slices` | array of at-uri (max 1000) | no | Ordered `slice` records (vertical format). | -| `pages` | array of at-uri (max 1000) | no | Ordered `page` records (page format). | -| `credits` | array of `defs#credit` (max 100) | no | Credits for this chapter. | +| `series` | at-uri | yes | | +| `title` | `defs#localizedTitle[]` (≤ 100) | no | | +| `description` | `defs#localizedDescription[]` (≤ 100) | no | | +| `createdAt` | datetime | yes | Publish date. | +| `status` | string | no | `completed` (default), `ongoing`, `hiatus`, `cancelled` | +| `covers` | at-uri[] (1–500) | yes | First is primary. | +| `slices` | at-uri[] (≤ 1000) | no | Vertical format. | +| `pages` | at-uri[] (≤ 1000) | no | Page format. | +| `credits` | `defs#credit[]` (≤ 100) | no | | ### `work.sequential.cover` -A cover for a chapter: either the primary cover or a variant. Put the same art with a different treatment (sketch, black and white, virgin) in the cover's `versions`. Different art (e.g. Cover B) gets its own `cover` record. +Different art (Cover B) is its own record. The same art with a different treatment (sketch, virgin) goes in `versions`. -| Property | Type | Required | Description | +| Property | Type | Req | Notes | | --- | --- | --- | --- | -| `chapter` | string (at-uri) | yes | AT-URI of the parent chapter. | -| `label` | string (max 100 graphemes) | no | Name that tells this cover apart from others, e.g. `Cover A`, `1:25 Incentive`. | -| `images` | array of `defs#localizedImage` (min 1, max 100) | yes | Localized final images. | -| `alt` | array of `defs#localizedAlt` (max 100) | no | Localized alt text for the cover. | -| `versions` | array of `defs#version` (max 50) | no | Other versions of the artwork: production stages or alternate treatments. | -| `aspectRatio` | `defs#aspectRatio` | no | The aspect ratio of the cover. | -| `credits` | array of `defs#credit` (max 100) | no | Cover credits, when they differ from the chapter's (e.g. a guest variant artist). | +| `chapter` | at-uri | yes | | +| `label` | string (≤ 100 graphemes) | no | e.g. `Cover A`, `1:25 Incentive` | +| `images` | `defs#localizedImage[]` (1–100) | yes | | +| `alt` | `defs#localizedAlt[]` (≤ 100) | no | | +| `versions` | `defs#version[]` (≤ 50) | no | | +| `aspectRatio` | `defs#aspectRatio` | no | | +| `credits` | `defs#credit[]` (≤ 100) | no | When different from the chapter's. | ### `work.sequential.slice` -A slice of a vertically formatted comic. - -| Property | Type | Required | Description | -| --- | --- | --- | --- | -| `chapter` | string (at-uri) | yes | AT-URI of the parent chapter. | -| `images` | array of `defs#localizedImage` (min 1, max 100) | yes | Localized final images. | -| `alt` | array of `defs#localizedAlt` (max 100) | no | Localized alt text for the slice. | -| `versions` | array of `defs#version` (max 50) | no | Other versions of the artwork: production stages or alternate treatments. | -| `aspectRatio` | `defs#aspectRatio` | no | The aspect ratio of the slice. | +| Property | Type | Req | +| --- | --- | --- | +| `chapter` | at-uri | yes | +| `images` | `defs#localizedImage[]` (1–100) | yes | +| `alt` | `defs#localizedAlt[]` (≤ 100) | no | +| `versions` | `defs#version[]` (≤ 50) | no | +| `aspectRatio` | `defs#aspectRatio` | no | ### `work.sequential.page` -A page of a page-format comic. - -| Property | Type | Required | Description | +| Property | Type | Req | Notes | | --- | --- | --- | --- | -| `chapter` | string (at-uri) | yes | AT-URI of the parent chapter. | -| `images` | array of `defs#localizedImage` (min 1, max 100) | yes | Localized final images. | -| `versions` | array of `defs#version` (max 50) | no | Other versions of the artwork: production stages or alternate treatments. | -| `aspectRatio` | `defs#aspectRatio` | no | The aspect ratio of the page. | -| `span` | integer (min 1, default 1) | no | Number of printed pages the image covers: 1 for a single page, 2 for a spread, 3+ for a gatefold or foldout. | -| `panels` | array of `defs#panel` (max 100) | no | Panels in reading order. Alt text for pages lives on panels. | +| `chapter` | at-uri | yes | | +| `images` | `defs#localizedImage[]` (1–100) | yes | | +| `versions` | `defs#version[]` (≤ 50) | no | | +| `aspectRatio` | `defs#aspectRatio` | no | | +| `span` | integer (≥ 1) | no | Printed pages covered: `1` (default), `2` for a spread, `3+` for a foldout. | +| `panels` | `defs#panel[]` (≤ 100) | no | In reading order. | ### `work.sequential.strip` -A comic strip. Strips belong directly to a series, with no chapters. Each strip is one image divided into panels, like a page. - -| Property | Type | Required | Description | +| Property | Type | Req | Notes | | --- | --- | --- | --- | -| `series` | string (at-uri) | yes | AT-URI of the parent series. | -| `title` | array of `defs#localizedTitle` (max 100) | no | Localized strip title or number. | -| `tags` | array of `defs#tag` (max 10) | no | Free-form tags for this strip. | -| `createdAt` | string (datetime) | yes | When the strip was published. Strips are ordered by this date. | -| `images` | array of `defs#localizedImage` (min 1, max 100) | yes | Localized final images. | -| `versions` | array of `defs#version` (max 50) | no | Other versions of the artwork: production stages or alternate treatments. | -| `aspectRatio` | `defs#aspectRatio` | no | The aspect ratio of the strip. | -| `panels` | array of `defs#panel` (max 100) | no | Panels in reading order. Alt text for strips lives on panels. | -| `credits` | array of `defs#credit` (max 100) | no | Credits for this strip. | +| `series` | at-uri | yes | | +| `title` | `defs#localizedTitle[]` (≤ 100) | no | | +| `tags` | `defs#tag[]` (≤ 10) | no | | +| `createdAt` | datetime | yes | Publish date and sort order. | +| `images` | `defs#localizedImage[]` (1–100) | yes | | +| `versions` | `defs#version[]` (≤ 50) | no | | +| `aspectRatio` | `defs#aspectRatio` | no | | +| `panels` | `defs#panel[]` (≤ 100) | no | In reading order. | +| `credits` | `defs#credit[]` (≤ 100) | no | | ### `work.sequential.graph.list` -A curated reading list or reading order. See [Reading Lists](#reading-lists). - -| Property | Type | Required | Description | +| Property | Type | Req | Notes | | --- | --- | --- | --- | -| `title` | array of `defs#localizedTitle` (min 1, max 100) | yes | Localized title of the list. | -| `description` | array of `defs#localizedDescription` (max 100) | no | Localized description of the list. | -| `createdAt` | string (datetime) | yes | When the list was created. | -| `items` | array of at-uri (max 1000) | no | Ordered `chapter` or `strip` records, in the curator's order. For a reading order, this is the order to read them in. | +| `title` | `defs#localizedTitle[]` (1–100) | yes | | +| `description` | `defs#localizedDescription[]` (≤ 100) | no | | +| `createdAt` | datetime | yes | | +| `items` | at-uri[] (≤ 1000) | no | Chapters or strips, in reading order. | ### `work.sequential.graph.subscription` -A reader's subscription to a series. See [Subscriptions](#subscriptions). - -| Property | Type | Required | Description | -| --- | --- | --- | --- | -| `series` | string (at-uri) | yes | AT-URI of the series being subscribed to. | -| `createdAt` | string (datetime) | yes | When the subscription was created. | +| Property | Type | Req | +| --- | --- | --- | +| `series` | at-uri | yes | +| `createdAt` | datetime | yes | ### `work.sequential.defs` -Shared definitions. - -#### `defs#panel` - -A panel on a page or strip. It carries the panel's alt text and, optionally, how to show it when reading panel by panel. A splash page or single-panel strip is a single panel. Every field is optional, so creators can write panel alt text without mapping positions. See [Panel-by-Panel Reading](#panel-by-panel-reading). - -| Property | Type | Required | Description | -| --- | --- | --- | --- | -| `bounds` | `defs#bounds` | no | Where the panel sits on the image. | -| `alt` | array of `defs#localizedAlt` (max 100) | no | Localized alt text for the panel. | -| `background` | `defs#rgba` | no | Fills the viewport around the panel. Opacity `0` keeps the surrounding page visible, `100` hides it behind a solid color. Fully transparent when omitted. | -| `mask` | blob (png, max 4 MiB) | no | Black and white mask of the panel's exact shape, covering the panel's `bounds`. White shows, black hides. Ignored without `bounds`. | -| `transition` | string (default `none`) | no | How the app moves to this panel from the previous one. Known values: `none`, `pan`, `fade`, `zoom`. Reversed when reading backward. Duration is up to the AppView. | - -#### `defs#rgba` - -A color with opacity. - -| Property | Type | Required | Description | -| --- | --- | --- | --- | -| `r` | integer (0–255) | yes | Red. | -| `g` | integer (0–255) | yes | Green. | -| `b` | integer (0–255) | yes | Blue. | -| `a` | integer (0–100) | yes | Opacity, from 0 (fully transparent) to 100 (fully opaque). | - -#### `defs#bounds` +**`panel`**: all fields optional. See [Panels](#panels). -A rectangle measured in ten-thousandths of the image's width and height (`0`–`10000`), with the origin at the top left. Lexicons have no floats, and relative units let the same bounds work for every localized image. - -| Property | Type | Required | +| Property | Type | Notes | | --- | --- | --- | -| `x` | integer (0–10000) | yes | -| `y` | integer (0–10000) | yes | -| `width` | integer (1–10000) | yes | -| `height` | integer (1–10000) | yes | - -#### `defs#localizedImage` +| `bounds` | `bounds` | | +| `alt` | `localizedAlt[]` (≤ 100) | | +| `background` | `rgba` | Transparent when omitted. | +| `mask` | blob (png, ≤ 4 MiB) | | +| `transition` | string | `none` (default), `pan`, `fade`, `zoom` | -| Property | Type | Required | Description | -| --- | --- | --- | --- | -| `image` | blob (jpeg/png/webp/avif, max 4 MiB) | yes | The localized image. | -| `lang` | string (language) | yes | BCP-47 language tag, e.g. `en`, `pt-BR`. | +**`bounds`**: `x`, `y`, `width`, `height`, in ten-thousandths of the image (`0`–`10000`, origin top left), so they fit every localized image. -#### `defs#localizedAlt` - -| Property | Type | Required | Description | -| --- | --- | --- | --- | -| `lang` | string (language) | yes | BCP-47 language tag. | -| `text` | string (max 10000 graphemes) | yes | Alt text: describes the art and transcribes the lettering. | +**`rgba`**: `r`, `g`, `b` (`0`–`255`) and `a` (opacity, `0`–`100`). -#### `defs#localizedTitle` +**`aspectRatio`**: `width`, `height` (≥ 1). -| Property | Type | Required | Description | -| --- | --- | --- | --- | -| `lang` | string (language) | yes | BCP-47 language tag. | -| `text` | string (max 100 graphemes) | yes | The title. | - -#### `defs#localizedDescription` - -| Property | Type | Required | Description | -| --- | --- | --- | --- | -| `lang` | string (language) | yes | BCP-47 language tag. | -| `text` | string (max 3000 graphemes) | yes | The description. | +**`localizedImage`**: `image` (blob, jpeg/png/webp/avif, ≤ 4 MiB), `lang`. -#### `defs#version` +**`localizedAlt`**, **`localizedTitle`**, **`localizedDescription`**: `lang` and `text`, max 10000, 100 and 3000 graphemes. -Another version of the same artwork, such as a production stage or an alternate treatment. +**`version`**: another version of the same art. -| Property | Type | Required | Description | -| --- | --- | --- | --- | -| `image` | blob (jpeg/png/webp/avif, max 4 MiB) | yes | The version image. | -| `kind` | string (max 64 graphemes) | yes | What this version shows. Known values: `thumbnails`, `layout`, `roughs`, `pencils`, `lineart`, `inks`, `flats`, `colors`, `letters`. AppViews may define their own kinds. | - -**Example kinds:** - -- Production stages: `thumbnails`, `layout`, `roughs`, `pencils`, `lineart`, `inks`, `flats`, `colors`, `letters` -- Alternate treatments: `sketch`, `blackAndWhite`, `virgin` (no logo or trade dress) - -#### `defs#aspectRatio` - -| Property | Type | Required | +| Property | Type | Notes | | --- | --- | --- | -| `width` | integer (min 1) | yes | -| `height` | integer (min 1) | yes | +| `image` | blob (≤ 4 MiB) | | +| `kind` | string (≤ 64 graphemes) | Stages: `thumbnails`, `layout`, `roughs`, `pencils`, `lineart`, `inks`, `flats`, `colors`, `letters`. Treatments: e.g. `sketch`, `blackAndWhite`, `virgin`. | -#### `defs#credit` +**`credit`** -| Property | Type | Required | Description | +| Property | Type | Req | Notes | | --- | --- | --- | --- | -| `displayName` | string (max 100 graphemes) | yes | Display name of the credited person. | -| `did` | string (did) | no | DID of the credited person, if they have one. | -| `role` | string (max 64 graphemes) | yes | The role of the credit. Known values: `writer`, `penciler`, `inker`, `colorist`, `flats`, `letterer`, `cover`, `creator`, `editor`, `editorInChief`, `associateEditor`, `assistantEditor`, `designer`, `artist`, `translator`, `painter`, `backgrounds`. AppViews may define their own roles. | - -#### `defs#category` - -A category for a series. AppViews may define their own (e.g. `action`, `comedy`, `drama`, `fantasy`, `horror`, `mystery`, `romance`, `sciFi`, `sliceOfLife`, `superhero`). Max 64 graphemes. `(string)` - -#### `defs#tag` +| `displayName` | string (≤ 100 graphemes) | yes | | +| `did` | did | no | | +| `role` | string (≤ 64 graphemes) | yes | `writer`, `penciler`, `inker`, `colorist`, `flats`, `letterer`, `cover`, `creator`, `editor`, `editorInChief`, `associateEditor`, `assistantEditor`, `designer`, `artist`, `translator`, `painter`, `backgrounds` | -A free-form tag, without a leading `#`. Max 64 graphemes. `(string)` +**`category`**: a string, e.g. `action`, `comedy`, `fantasy`, `sliceOfLife`. Max 64 graphemes. -## Resources +**`tag`**: a string without a leading `#`. Max 64 graphemes. -- [AT Protocol](https://atproto.com/) - The underlying protocol -- [Lexicon Documentation](https://atproto.com/specs/lexicon) - AT Protocol lexicon spec -- [Labels](https://atproto.com/specs/label) - AT Protocol labels and self-labels spec -- [Prototypey](https://github.com/tylersayshi/prototypey) - AT Protocol lexicon TypeScript toolkit +All string values listed above are known values, not a closed list. AppViews may add their own and ignore ones they don't recognize. -- 2.51.2