3
0
mirror of https://github.com/larvit/adf-codec.git synced 2026-11-13 04:08:29 +01:00

Answer architecture round 2: the decision's title states it whole, an image's title drops inside a link, the readers refuse nesting past 500 levels, one owner for the empty-alt rule

This commit is contained in:
2026-10-09 03:39:45 +02:00
committed by Lillem4n
parent 88aef1c8c4
commit 4454aaca92
6 changed files with 18 additions and 16 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ In `docs/decisions.md`:
- CommonMark is a subset
- Tables
- Links
- What ADF holds no node for reads as a link
- Every CommonMark image and empty link reads: the image where it stands alone, else a link
- Ids stay site-local
- Portable task ids come from position
- A callout title keeps its link targets
+7 -6
View File
@@ -12,9 +12,9 @@ products store as ADF.
started from — panels, mentions, statuses and node types this version does not know included.
- **Markdown in:** `portableMarkdownToAdf` reads a bot's or a model's markdown, including `~~` and
pipe tables whose every row opens with a pipe. `commonMarkToAdf` reads strict CommonMark as its
spec says: nothing becomes a table, task or panel. Both refuse raw HTML, and
`portableMarkdownToAdf` a claimed shape that does not complete
([The guarantees](#the-guarantees)).
spec says: nothing becomes a table, task or panel. Both refuse raw HTML and nesting past 500
levels, and `portableMarkdownToAdf` a claimed shape that does not complete
([The errors](#the-errors)).
- **Portable markdown, lossy:** `adfToPortableMarkdown` and `portableMarkdownToAdf` convert to and
from the markdown that GitHub, GitLab and Obsidian render — GitHub Flavored Markdown's alerts,
task lists and pipe tables, Obsidian Flavored Markdown's callouts and highlights.
@@ -230,7 +230,8 @@ Serves Goals 1, 4 and 5.
- Markdown this library reads, and markdown it writes, means what the CommonMark spec says; from
`0.3.0`, well-formed HTML means what the HTML standard says, read or written. The bullets below
name every exception.
- CommonMark is valid input to `commonMarkToAdf`, apart from the raw HTML `unmappable-html` names.
- CommonMark is valid input to `commonMarkToAdf`, apart from the raw HTML `unmappable-html` names
and nesting past 500 levels.
- The other readers claim shapes CommonMark reads as text or code. `losslessMarkdownToAdf` claims
four: literal text matching directive, pipe-table or strikethrough syntax, and a code fence whose
info string opens `adf:`. `portableMarkdownToAdf` claims what [Portable
@@ -245,8 +246,8 @@ Serves Goals 1, 4 and 5.
- An image anywhere but alone in a plain paragraph reads as its alt text linked to its URL,
`See ![the moon](moon.png).` as `See [the moon](moon.png).`, and inside a link as plain text
the link marks.
- An image's title reads as its caption where the image stands alone in a plain paragraph, and
as its link's title where it reads as linked alt text.
- An image's title reads as its caption where the image stands alone in a plain paragraph, as
its link's title where it reads as alt text linked to its URL, and inside a link it drops.
And three more: a list continuing past a marker change stays one list against CommonMark's two,
a shortcut reference matching its definition only under Unicode case folding stays unresolved,
+5 -4
View File
@@ -202,10 +202,11 @@ link reference definition — and a directive link CommonMark could spell is ref
link — the bracket form goes literal, the directive form refused — which is CommonMark's prose
where its reference implementation nests one `<a>` in another.
## What ADF holds no node for reads as a link
## Every CommonMark image and empty link reads: the image where it stands alone, else a link
2026-10-04, the maintainer and a writer panel, 3 of 3; `[]()` 2026-10-09, by Goals 4 and 7. Goals 4,
5 and 6. Valid while `mediaInline` carries no URL and ADF holds no empty text node.
2026-10-04, the maintainer and a writer panel, 3 of 3; `[]()` 2026-10-09, settled by Goals 4 and 7
as the maintainer's plan item directed. Goals 4, 5, 6 and 7. Valid while `mediaInline` carries no
URL and ADF holds no empty text node.
Every reader reads every CommonMark image and empty link. An image anywhere but alone in a plain
paragraph reads as its alt text linked to its URL, titled by its title; an empty alt text reads as
@@ -213,7 +214,7 @@ the URL, and an empty link text as its destination. `[]()` reads as nothing: it
Goal 7 loses no content. A titled image alone in a plain paragraph reads as the image captioned with
its title. A link alone in a plain paragraph whose whole text is an image reads as the image with
the link marking its `media`. Inside a link otherwise, the alt text is plain text the link marks and
the image's URL drops: the panel chose this by Goal 6, which outranks Goal 7.
the image's URL and title drop: the panel chose this by Goal 6, which outranks Goal 7.
## Ids stay site-local
+2 -2
View File
@@ -324,8 +324,8 @@ Anywhere else — beside other content, in a heading, a table cell or an `!adf:p
reads as its alt text linked to its destination, the link titled by the image's title, since
`mediaInline` carries a media `collection` + `id`, never a URL. An empty alt text reads as the
destination, as an empty link text does. Inside a link's text, the alt text is plain text the link
marks, and the image's destination and title drop (`docs/decisions.md` §What ADF holds no node for
reads as a link).
marks, and the image's destination and title drop (`docs/decisions.md` §Every CommonMark image and
empty link reads).
### Tables
+1 -2
View File
@@ -4,11 +4,10 @@ import { nodeAttrs, nodeContent } from '../../adf/document.ts'
import { serializeCanonicalJson } from '../../canonical-json.ts'
import { tryImageLine } from './inline-line.ts'
// An empty `alt` takes the directive form: `![](url)` reads back with no `alt`.
export function tryImage(node: AdfNode): string | undefined {
const [media] = nodeContent(node)
const { alt, url } = media === undefined ? {} : nodeAttrs(media)
if (typeof url !== 'string' || (alt !== undefined && typeof alt !== 'string') || alt === '') return undefined
if (typeof url !== 'string' || (alt !== undefined && typeof alt !== 'string')) return undefined
const read = centeredImage([externalMedia(alt ?? '', url)])
return serializeCanonicalJson(node, 'compact') === serializeCanonicalJson(read, 'compact') ? tryImageLine(alt, url) : undefined
}
+2 -1
View File
@@ -1,10 +1,11 @@
import type { AdfNode } from '../adf/document.ts'
// spec/flavour.md, The CommonMark image: the shape `![alt](url)` reads to, and the only one written back as it.
// spec/flavour.md, The CommonMark image: `![alt](url)` reads to `centeredImage([externalMedia(alt, url)])`, the only shape written back as it.
export function centeredImage(content: AdfNode[]): AdfNode {
return { attrs: { layout: 'center' }, content, type: 'mediaSingle' }
}
// `![](url)` builds no `alt`, so a media holding an empty one takes the directive form.
export function externalMedia(alt: string, url: string): AdfNode {
return { attrs: alt === '' ? { type: 'external', url } : { alt, type: 'external', url }, type: 'media' }
}