diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index dfe0770..0000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -# Auto detect text files and perform LF normalization -* text=auto diff --git a/README.md b/README.md new file mode 100644 index 0000000..2f8d72d --- /dev/null +++ b/README.md @@ -0,0 +1,278 @@ +# sequential.work Lexicons + +AT Protocol lexicons for comics in **vertical** (webtoon-style), **page** (traditional comic book) and **strip** (newspaper-style comic strip) formats. Records live under the `work.sequential.*` namespace. + +Highlights: + +- a separate **cover** record, so a chapter can have a primary cover plus variant covers +- **page** and **panel** support for page-format comics +- **strip** support for comic strips +- **categories** limited to 1–3 per series, plus free-form **tags** + +Many values are open-ended `string`s rather than enums, so each AppView can support its own set of values. + +## Structure + +``` +series (format: vertical | page) +└── chapter + ├── covers[] → cover (first = primary, rest = variants) + ├── slices[] → slice (format: vertical) + └── pages[] → page (format: page) + └── panels[] (inline) + +series (format: strip) +└── strip (many, 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. 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`. + +## 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. + +When importing older work, set `createdAt` to the original publication date. + +## 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.defs`](#worksequentialdefs) + +--- + +## `work.sequential.series` + +A comic series. + +| Property | Type | Required | Description | +| --- | --- | --- | --- | +| `title` | array of `defs#localizedTitle` (min 1) | 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). | +| `ogImage` | 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) | 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 | 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 | 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. + +### 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: + +- `porn`: explicit sexual content +- `sexual`: sexually suggestive content +- `nudity`: non-sexual nudity +- `graphic-media`: gore and graphic violence + +**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. + +```json +"labels": { + "$type": "com.atproto.label.defs#selfLabels", + "values": [{ "val": "graphic-media" }, { "val": "profanity" }] +} +``` + +## `work.sequential.chapter` + +A chapter in a series. Use `slices` or `pages`, whichever matches the series `format`. + +| Property | Type | Required | Description | +| --- | --- | --- | --- | +| `series` | string (at-uri) | yes | AT-URI of the parent series. | +| `title` | array of `defs#localizedTitle` | no | Localized chapter title or number. | +| `description` | array of `defs#localizedDescription` | no | Localized short description of the chapter. | +| `createdAt` | string (datetime) | yes | When the chapter was published. | +| `covers` | array of at-uri (min 1) | yes | Ordered `cover` records. The first is the primary cover and the rest are variants. | +| `slices` | array of at-uri | no | Ordered `slice` records (vertical format). | +| `pages` | array of at-uri | no | Ordered `page` records (page format). | +| `credits` | array of `defs#credit` | no | Credits for this chapter. | + +## `work.sequential.cover` + +A cover for a chapter: either the primary cover or a variant. It supports localized images and versions, the same as slices and pages. 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. + +| Property | Type | Required | Description | +| --- | --- | --- | --- | +| `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) | yes | Localized final images. | +| `alt` | array of `defs#localizedAlt` | no | Localized alt text for the cover. | +| `versions` | array of `defs#version` | 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` | no | Cover credits, when they differ from the chapter's (e.g. a guest variant artist). | + +## `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) | yes | Localized final images. | +| `alt` | array of `defs#localizedAlt` | no | Localized alt text for the slice. | +| `versions` | array of `defs#version` | no | Other versions of the artwork: production stages or alternate treatments. | +| `aspectRatio` | `defs#aspectRatio` | no | The aspect ratio of the slice. | + +## `work.sequential.page` + +A page of a page-format comic. + +| Property | Type | Required | Description | +| --- | --- | --- | --- | +| `chapter` | string (at-uri) | yes | AT-URI of the parent chapter. | +| `images` | array of `defs#localizedImage` (min 1) | yes | Localized final images. | +| `versions` | array of `defs#version` | 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` | no | Panels in reading order. Alt text for pages lives on panels. | + +## `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 | +| --- | --- | --- | --- | +| `series` | string (at-uri) | yes | AT-URI of the parent series. | +| `title` | array of `defs#localizedTitle` | 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) | yes | Localized final images. | +| `versions` | array of `defs#version` | 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` | no | Panels in reading order. Alt text for strips lives on panels. | +| `credits` | array of `defs#credit` | no | Credits for this strip. | + +## `work.sequential.defs` + +Shared definitions. + +### Alt text + +Alt text is never stored on an image. It's always an array of `defs#localizedAlt`, so every description 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. + +```json +"alt": [ + { "lang": "en", "text": "Saitama stands over the defeated monster, bored. SAITAMA: \"OK.\" SFX: THOOM" } +] +``` + +### Localized text + +Every piece of human-readable text is a list of `{ lang, text }` entries, one per language. Length limits count graphemes (visible characters) rather than bytes, so CJK titles get the same room as English ones. AppViews show the reader's language and fall back to the series' `nativeLanguage`. + +### `defs#panel` + +A panel on a page or strip. Panels are stored inline rather than as separate records. They carry the image's alt text, one localized description per panel, and optionally the panel's position for guided panel-by-panel reading. A splash page or single-panel strip is a single panel. Creators can write panel descriptions without mapping positions, because `bounds` is optional. + +| Property | Type | Required | Description | +| --- | --- | --- | --- | +| `bounds` | `defs#bounds` | no | Where the panel sits on the image. | +| `alt` | array of `defs#localizedAlt` | no | Localized alt text for the panel. | + +### `defs#bounds` + +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 | +| --- | --- | --- | +| `x` | integer (0–10000) | yes | +| `y` | integer (0–10000) | yes | +| `width` | integer (1–10000) | yes | +| `height` | integer (1–10000) | yes | + +### `defs#localizedImage` + +| 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`. | + +### `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. | + +### `defs#localizedTitle` + +| 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. | + +### `defs#version` + +Another version of the same artwork, such as a production stage or an alternate treatment. + +| Property | Type | Required | Description | +| --- | --- | --- | --- | +| `image` | blob (jpeg/png/webp/avif, max 4 MiB) | yes | The version image. | +| `kind` | string | yes | What this version shows. 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 | +| --- | --- | --- | +| `width` | integer (min 1) | yes | +| `height` | integer (min 1) | yes | + +### `defs#credit` + +| Property | Type | Required | Description | +| --- | --- | --- | --- | +| `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 | yes | e.g. `writer`, `penciler`, `inker`, `colorist`, `letterer`, `cover`, `editor`, `translator`, `other`. | + +### `defs#category` + +A category for a series. AppViews may define their own (e.g. `action`, `comedy`, `drama`, `fantasy`, `horror`, `mystery`, `romance`, `sciFi`, `sliceOfLife`, `superhero`). `(string)` + +### `defs#tag` + +A free-form tag, without a leading `#`. Max 64 graphemes. `(string)` diff --git a/lexicons/work/sequential/chapter.json b/lexicons/work/sequential/chapter.json new file mode 100644 index 0000000..f2f67e9 --- /dev/null +++ b/lexicons/work/sequential/chapter.json @@ -0,0 +1,70 @@ +{ + "lexicon": 1, + "id": "work.sequential.chapter", + "defs": { + "main": { + "type": "record", + "description": "A chapter in a series.", + "key": "tid", + "record": { + "type": "object", + "required": ["covers", "createdAt", "series"], + "properties": { + "series": { + "type": "string", + "format": "at-uri", + "description": "AT-URI of the parent series." + }, + "title": { + "type": "array", + "description": "Localized chapter title or number.", + "items": { "type": "ref", "ref": "work.sequential.defs#localizedTitle" } + }, + "description": { + "type": "array", + "description": "Localized short description of the chapter.", + "items": { "type": "ref", "ref": "work.sequential.defs#localizedDescription" } + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "The date and time the chapter was published. Drafts are kept by the AppView and only written to the repo when published." + }, + "covers": { + "type": "array", + "minLength": 1, + "description": "Ordered list of work.sequential.cover records. The first is the primary cover; the rest are variants.", + "items": { + "type": "string", + "format": "at-uri" + } + }, + "slices": { + "type": "array", + "description": "Ordered list of work.sequential.slice records. Used when the series format is 'vertical'.", + "items": { + "type": "string", + "format": "at-uri" + } + }, + "pages": { + "type": "array", + "description": "Ordered list of work.sequential.page records. Used when the series format is 'page'.", + "items": { + "type": "string", + "format": "at-uri" + } + }, + "credits": { + "type": "array", + "description": "The credits for the people who worked on this chapter.", + "items": { + "type": "ref", + "ref": "work.sequential.defs#credit" + } + } + } + } + } + } +} diff --git a/lexicons/work/sequential/cover.json b/lexicons/work/sequential/cover.json new file mode 100644 index 0000000..f057863 --- /dev/null +++ b/lexicons/work/sequential/cover.json @@ -0,0 +1,57 @@ +{ + "lexicon": 1, + "id": "work.sequential.cover", + "defs": { + "main": { + "type": "record", + "description": "A cover for a chapter. A chapter may have several covers: a primary cover and any number of variants.", + "key": "tid", + "record": { + "type": "object", + "required": ["chapter", "images"], + "properties": { + "chapter": { + "type": "string", + "format": "at-uri", + "description": "AT-URI of the parent chapter." + }, + "label": { + "type": "string", + "maxLength": 1000, + "maxGraphemes": 100, + "description": "Name distinguishing this cover from others on the chapter (e.g. 'Cover A', 'Cover B', '1:25 Incentive')." + }, + "images": { + "type": "array", + "minLength": 1, + "items": { "type": "ref", "ref": "work.sequential.defs#localizedImage" }, + "description": "Localized final images for this cover." + }, + "alt": { + "type": "array", + "description": "Localized alt text for the cover: describes the art and transcribes the title, cover text and any lettering.", + "items": { "type": "ref", "ref": "work.sequential.defs#localizedAlt" } + }, + "versions": { + "type": "array", + "description": "Other versions of the cover's artwork, such as production stages or alternate treatments. AppViews may let readers peel back stages or switch between versions.", + "items": { "type": "ref", "ref": "work.sequential.defs#version" } + }, + "aspectRatio": { + "type": "ref", + "ref": "work.sequential.defs#aspectRatio", + "description": "The aspect ratio of the cover." + }, + "credits": { + "type": "array", + "description": "The credits for the people who worked on this cover, when they differ from the chapter's.", + "items": { + "type": "ref", + "ref": "work.sequential.defs#credit" + } + } + } + } + } + } +} diff --git a/lexicons/work/sequential/defs.json b/lexicons/work/sequential/defs.json new file mode 100644 index 0000000..3085522 --- /dev/null +++ b/lexicons/work/sequential/defs.json @@ -0,0 +1,146 @@ +{ + "lexicon": 1, + "id": "work.sequential.defs", + "defs": { + "localizedImage": { + "type": "object", + "required": ["image", "lang"], + "properties": { + "image": { + "type": "blob", + "maxSize": 4194304, + "accept": ["image/jpeg", "image/png", "image/webp", "image/avif"], + "description": "The localized image." + }, + "lang": { + "type": "string", + "format": "language", + "description": "BCP-47 language tag for this image (e.g. 'en', 'pt-BR')." + } + } + }, + "localizedAlt": { + "type": "object", + "description": "Localized alt text. Describes the art and transcribes the dialogue, captions and sound effects.", + "required": ["lang", "text"], + "properties": { + "lang": { + "type": "string", + "format": "language", + "description": "BCP-47 language tag for this text." + }, + "text": { + "type": "string", + "maxLength": 100000, + "maxGraphemes": 10000 + } + } + }, + "localizedTitle": { + "type": "object", + "required": ["lang", "text"], + "properties": { + "lang": { + "type": "string", + "format": "language", + "description": "BCP-47 language tag for this title." + }, + "text": { + "type": "string", + "maxLength": 1000, + "maxGraphemes": 100 + } + } + }, + "localizedDescription": { + "type": "object", + "required": ["lang", "text"], + "properties": { + "lang": { + "type": "string", + "format": "language", + "description": "BCP-47 language tag for this description." + }, + "text": { + "type": "string", + "maxLength": 30000, + "maxGraphemes": 3000 + } + } + }, + "version": { + "type": "object", + "description": "Another version of the same artwork, such as a production stage (pencils, inks) or an alternate treatment (virgin, black and white).", + "required": ["image", "kind"], + "properties": { + "image": { + "type": "blob", + "maxSize": 4194304, + "accept": ["image/jpeg", "image/png", "image/webp", "image/avif"], + "description": "The version image." + }, + "kind": { + "type": "string", + "description": "What this version shows. AppViews may define their own kinds." + } + } + }, + "aspectRatio": { + "type": "object", + "required": ["height", "width"], + "properties": { + "width": { "type": "integer", "minimum": 1 }, + "height": { "type": "integer", "minimum": 1 } + } + }, + "credit": { + "type": "object", + "required": ["displayName", "role"], + "properties": { + "displayName": { "type": "string", "maxLength": 1000, "maxGraphemes": 100 }, + "did": { "type": "string", "format": "did" }, + "role": { + "type": "string", + "description": "The role of the credit. AppViews may define their own roles." + } + } + }, + "category": { + "type": "string", + "description": "A category for a series. AppViews may define their own categories." + }, + "tag": { + "type": "string", + "maxLength": 640, + "maxGraphemes": 64, + "description": "A free-form tag, without a leading '#'." + }, + "panel": { + "type": "object", + "description": "A panel on a page or strip. Carries the image's alt text, one description per panel, and optionally its position for guided (panel-by-panel) reading. A splash page or single-panel strip is a single panel.", + "properties": { + "bounds": { + "type": "ref", + "ref": "#bounds", + "description": "Where the panel sits on the image. Optional, so alt text can be added without mapping panel positions." + }, + "alt": { + "type": "array", + "description": "Localized alt text for this panel: describes the art and transcribes the dialogue, captions and sound effects.", + "items": { "type": "ref", "ref": "work.sequential.defs#localizedAlt" } + } + } + }, + "bounds": { + "type": "object", + "description": "A rectangle relative to an image, in ten-thousandths of the image's width and height (0-10000), with the origin at the top left.", + "required": ["x", "y", "width", "height"], + "properties": { + "x": { "type": "integer", "minimum": 0, "maximum": 10000 }, + "y": { "type": "integer", "minimum": 0, "maximum": 10000 }, + "width": { "type": "integer", "minimum": 1, "maximum": 10000 }, + "height": { "type": "integer", "minimum": 1, "maximum": 10000 } + } + } + } +} diff --git a/lexicons/work/sequential/page.json b/lexicons/work/sequential/page.json new file mode 100644 index 0000000..dc64230 --- /dev/null +++ b/lexicons/work/sequential/page.json @@ -0,0 +1,49 @@ +{ + "lexicon": 1, + "id": "work.sequential.page", + "defs": { + "main": { + "type": "record", + "description": "A page of a page formatted comic.", + "key": "tid", + "record": { + "type": "object", + "required": ["chapter", "images"], + "properties": { + "chapter": { + "type": "string", + "format": "at-uri", + "description": "AT-URI of the parent chapter." + }, + "images": { + "type": "array", + "minLength": 1, + "items": { "type": "ref", "ref": "work.sequential.defs#localizedImage" }, + "description": "Localized final images for this page." + }, + "versions": { + "type": "array", + "description": "Other versions of the page's artwork, such as production stages or alternate treatments. AppViews may let readers peel back stages or switch between versions.", + "items": { "type": "ref", "ref": "work.sequential.defs#version" } + }, + "aspectRatio": { + "type": "ref", + "ref": "work.sequential.defs#aspectRatio", + "description": "The aspect ratio of the page." + }, + "span": { + "type": "integer", + "minimum": 1, + "default": 1, + "description": "Number of printed pages this image covers. 1 for a single page, 2 for a two-page spread, 3 or more for a gatefold or foldout." + }, + "panels": { + "type": "array", + "description": "Panels on this page, in reading order. Alt text for pages lives on panels.", + "items": { "type": "ref", "ref": "work.sequential.defs#panel" } + } + } + } + } + } +} diff --git a/lexicons/work/sequential/series.json b/lexicons/work/sequential/series.json new file mode 100644 index 0000000..045562d --- /dev/null +++ b/lexicons/work/sequential/series.json @@ -0,0 +1,124 @@ +{ + "lexicon": 1, + "id": "work.sequential.series", + "defs": { + "main": { + "type": "record", + "description": "A comic series.", + "key": "tid", + "record": { + "type": "object", + "required": [ + "banner", + "categories", + "ogImage", + "createdAt", + "description", + "format", + "title" + ], + "properties": { + "title": { + "type": "array", + "minLength": 1, + "description": "Localized title of the series.", + "items": { "type": "ref", "ref": "work.sequential.defs#localizedTitle" } + }, + "banner": { + "type": "blob", + "maxSize": 4194304, + "accept": ["image/jpeg", "image/png", "image/webp", "image/avif"], + "description": "The banner image of the series. 3:1 aspect ratio. (e.g. 3000x1000 pixels)" + }, + "ogImage": { + "type": "blob", + "maxSize": 4194304, + "accept": ["image/jpeg", "image/png"], + "description": "The open graph share image of the series. 1.91:1 aspect ratio. (e.g. 1200x630 pixels) JPEG or PNG only, because many link preview crawlers don't support newer formats." + }, + "description": { + "type": "array", + "minLength": 1, + "description": "Localized short description of the series.", + "items": { "type": "ref", "ref": "#localizedDescription" } + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "The date and time the series was created." + }, + "categories": { + "type": "array", + "description": "Categories for this series, most relevant first.", + "minLength": 1, + "maxLength": 3, + "items": { + "type": "ref", + "ref": "work.sequential.defs#category" + } + }, + "tags": { + "type": "array", + "description": "Free-form tags for discovery.", + "maxLength": 10, + "items": { + "type": "ref", + "ref": "work.sequential.defs#tag" + } + }, + "format": { + "type": "string", + "knownValues": ["vertical", "page", "strip"], + "description": "The reading format of the series. 'vertical' chapters are made of slices; 'page' chapters are made of pages. 'strip' series have no chapters; their strips point to the series directly. AppViews may define their own formats." + }, + "readingDirection": { + "type": "string", + "knownValues": ["ltr", "rtl"], + "default": "ltr", + "description": "Horizontal reading direction of panels and pages. 'rtl' for right-to-left comics such as manga." + }, + "status": { + "type": "string", + "knownValues": ["ongoing", "completed", "hiatus", "cancelled"], + "description": "Publication status of the series. AppViews may define their own statuses." + }, + "nativeLanguage": { + "type": "string", + "format": "language", + "description": "BCP-47 language tag of the language the series was created in. AppViews should fall back to it when a reader's language isn't available." + }, + "labels": { + "type": "union", + "description": "Self-labels and content warnings for this series. Use the standard AT Protocol values ('porn', 'sexual', 'nudity', 'graphic-media') where they apply. AppViews may define additional values (e.g. 'profanity', 'violence'); AppViews that don't recognize a value ignore it.", + "refs": ["com.atproto.label.defs#selfLabels"] + }, + "chapters": { + "type": "array", + "description": "Ordered list of chapters in this series. Not used by the 'strip' format.", + "items": { + "type": "string", + "format": "at-uri" + } + } + } + } + }, + "localizedDescription": { + "type": "object", + "description": "Same as work.sequential.defs#localizedDescription, but limited to a short blurb.", + "required": ["lang", "text"], + "properties": { + "lang": { + "type": "string", + "format": "language", + "description": "BCP-47 language tag for this description." + }, + "text": { + "type": "string", + "maxLength": 2550, + "maxGraphemes": 255 + } + } + } + } +} diff --git a/lexicons/work/sequential/slice.json b/lexicons/work/sequential/slice.json new file mode 100644 index 0000000..421def4 --- /dev/null +++ b/lexicons/work/sequential/slice.json @@ -0,0 +1,43 @@ +{ + "lexicon": 1, + "id": "work.sequential.slice", + "defs": { + "main": { + "type": "record", + "description": "A slice of a vertically formatted comic.", + "key": "tid", + "record": { + "type": "object", + "required": ["chapter", "images"], + "properties": { + "chapter": { + "type": "string", + "format": "at-uri", + "description": "AT-URI of the parent chapter." + }, + "images": { + "type": "array", + "minLength": 1, + "items": { "type": "ref", "ref": "work.sequential.defs#localizedImage" }, + "description": "Localized final images for this slice." + }, + "alt": { + "type": "array", + "description": "Localized alt text for the slice: describes the art and transcribes the dialogue, captions and sound effects.", + "items": { "type": "ref", "ref": "work.sequential.defs#localizedAlt" } + }, + "versions": { + "type": "array", + "description": "Other versions of the slice's artwork, such as production stages or alternate treatments. AppViews may let readers peel back stages or switch between versions.", + "items": { "type": "ref", "ref": "work.sequential.defs#version" } + }, + "aspectRatio": { + "type": "ref", + "ref": "work.sequential.defs#aspectRatio", + "description": "The aspect ratio of the slice." + } + } + } + } + } +} diff --git a/lexicons/work/sequential/strip.json b/lexicons/work/sequential/strip.json new file mode 100644 index 0000000..edecf12 --- /dev/null +++ b/lexicons/work/sequential/strip.json @@ -0,0 +1,64 @@ +{ + "lexicon": 1, + "id": "work.sequential.strip", + "defs": { + "main": { + "type": "record", + "description": "A comic strip in a series. Strips belong directly to a series, without chapters, and are ordered by createdAt.", + "key": "tid", + "record": { + "type": "object", + "required": ["createdAt", "images", "series"], + "properties": { + "series": { + "type": "string", + "format": "at-uri", + "description": "AT-URI of the parent series." + }, + "title": { + "type": "array", + "description": "Localized strip title or number.", + "items": { "type": "ref", "ref": "work.sequential.defs#localizedTitle" } + }, + "tags": { + "type": "array", + "description": "Free-form tags for this strip.", + "maxLength": 10, + "items": { "type": "ref", "ref": "work.sequential.defs#tag" } + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "The date and time the strip was published. Strips are ordered by this date. Drafts are kept by the AppView and only written to the repo when published." + }, + "images": { + "type": "array", + "minLength": 1, + "items": { "type": "ref", "ref": "work.sequential.defs#localizedImage" }, + "description": "Localized final images for this strip." + }, + "versions": { + "type": "array", + "description": "Other versions of the strip's artwork, such as production stages or alternate treatments. AppViews may let readers peel back stages or switch between versions.", + "items": { "type": "ref", "ref": "work.sequential.defs#version" } + }, + "aspectRatio": { + "type": "ref", + "ref": "work.sequential.defs#aspectRatio", + "description": "The aspect ratio of the strip." + }, + "panels": { + "type": "array", + "description": "Panels in this strip, in reading order. Alt text for strips lives on panels.", + "items": { "type": "ref", "ref": "work.sequential.defs#panel" } + }, + "credits": { + "type": "array", + "description": "The credits for the people who worked on this strip.", + "items": { "type": "ref", "ref": "work.sequential.defs#credit" } + } + } + } + } + } +}