diff --git a/CHANGELOG.md b/CHANGELOG.md index 06f1af4..2780df7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ `localId` as `data-local-id`. Every other node is an element of class `adf-carry` holding the node's JSON in `data-adf` and showing its content, links included. Every URL is written as the ADF holds it, `javascript:` included: sanitize HTML from an author you do not trust before - showing it. It refuses an inline card a link marks, as `unsupported-node-shape`: Atlassian's schema gives a + showing it. `adfToHtml` refuses an inline card a link marks, as `unsupported-node-shape`: Atlassian's schema gives a card no link mark, and HTML nests no link in a link. - `htmlToAdf` reads `adfToHtml`'s HTML back to the document it came from, a carry element from its `data-adf`. It reads that HTML as a browser's `innerHTML` writes it too (a raw `"` in text, diff --git a/README.md b/README.md index 4cb4cc2..99a5371 100644 --- a/README.md +++ b/README.md @@ -526,7 +526,7 @@ content. No stylesheet ships. The complete dialect: | `rule` | `
` | | `text` marked `code`, `em`, `strong` | ``, ``, ``, the first mark outermost | | an attribute its type holds that its element does not write, such as `localId`, on a paragraph, heading, quote, list, list item, code block, rule, emoji, mention, status, date or card | `data-*` named as HTML's `dataset` names it: `

` | -| any other node, or a node holding what its element cannot | `

its content

`, a `` inline | +| any other node, or a node holding what its element cannot, except an inline card a link marks, which is refused | `

its content

`, a `` inline | - Every URL is written as the ADF holds it, `javascript:` included: a link's, an image's, a card's, and a carried link's in the carry element. Sanitize the HTML before showing a document from diff --git a/docs/decisions.md b/docs/decisions.md index d52b483..c7a20d0 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -441,10 +441,15 @@ it joins the second panel's `` to that portable spelling. Inside a carry element, a card a link marks links to the link's target, and shows its own URL as its text where it has one, so both stay. A card showing only its name keeps its URL in `data-adf` alone. -Atlassian's schema gives no card a link mark, so only a hand-built document meets this, and -`adfToHtml` refuses such a card among the inline nodes it writes as `unsupported-node-shape`: no -element holds both its link and the card's own. Inside a carried block it shows as above, since a -carried node's JSON is not checked, and `htmlToAdf` still reads a carry element holding one, as it checks no carried JSON against the schema. Recorded 2026-10-24 without a panel of its own: it puts the second panel's `` around the text a card shows. +Recorded 2026-10-24 without a panel of its own: it puts the second panel's `` around the text a +card shows. + +Atlassian's schema gives no card a link mark, so only a hand-built document holds one. HTML nests no +`` in an ``, so no element can hold both the link's target and the card's URL: `adfToHtml` +refuses such a card as `unsupported-node-shape` wherever it writes the card as its own element. The +maintainer chose this on 2026-11-04 over a new spelling. Inside a carry element the card shows as +above, since `adfToHtml` checks no carried node's JSON against the schema, and `htmlToAdf` still +reads a carry element holding one for the same reason. A fifth panel (2026-11-04, three readers in the same roles, then four more for what the first three split on) was asked how to write a mention, emoji, status, date and inline card, which until then diff --git a/spec/html.md b/spec/html.md index 5a8b086..e72d9c5 100644 --- a/spec/html.md +++ b/spec/html.md @@ -94,7 +94,7 @@ later release writes it as its own element. - A hard break is `
`, and so is a line feed in text outside a `
`'s code block. - Text a link marks is `` around it, or the text alone where HTML cannot hold the link's `href` or `title`; a `title` that is no string is left out. A card is a link to - its URL; inside a carry element, to the target of a link marking it. + the target of a link marking it, else to its URL. - An external image with no link to another target is a link to its URL. An image marked by a link to another target shows as portable markdown writes it inline. First comes the link, around the image's `alt`, else the link's target, else the image's URL. Then comes ` (image)`, @@ -107,7 +107,9 @@ later release writes it as its own element. - Characters HTML cannot hold are dropped. - A node rides the carry where it holds a mark or a key its element does not write, an attribute its type does not hold as that kind, an empty `attrs`, `content` or `marks`, or a value its - element cannot hold: + element cannot hold. The exception is an inline card a link marks, which `adfToHtml` refuses as + `unsupported-node-shape`: Atlassian's schema gives a card no link mark, and HTML nests no link in + a link. - text holding no character, or a character HTML cannot hold; text holding a mark twice, or a mark with attributes besides a link's `href` and `title`; - an unmarked text node straight after one written as plain text, which would read back as one; @@ -155,8 +157,6 @@ that node in either spelling: an edit there would be lost, so it is `unmappable- is not the day its `data-timestamp` names, a card without `data-data` whose `href` and text are not one URL, and a card with `data-data` whose `href` is not the URL its `data` holds or whose text is not its name, else that URL, are `unmappable-html`. -- `adfToHtml` refuses an inline card a link marks, which Atlassian's schema gives no link mark, as - `unsupported-node-shape`. - A `data-*` attribute that its element does not take, such as `data-level` on `

`, or that holds a value outside its attribute's kind or not written as compact JSON, is `unsupported-node-shape`. - A carry element's `data-adf` holding invalid JSON is `unmappable-html`. JSON that is no ADF node, diff --git a/src/html/emit/adf-to-html.test.ts b/src/html/emit/adf-to-html.test.ts index 7f0cd4f..e9157ff 100644 --- a/src/html/emit/adf-to-html.test.ts +++ b/src/html/emit/adf-to-html.test.ts @@ -282,7 +282,7 @@ test('links a card a link marks, inside a carried node, to that link\'s target a assert.equal(shown(html(adfToHtml(document({ content: [paragraph(card)], type: 'panel' })))), '

https://example.com/roadmap

') }) -test('refuses an inline card a link marks, which ADF\'s schema gives no link mark', () => { +test('refuses an inline card a link marks, which Atlassian\'s schema does not allow', () => { const marks: AdfMark[] = [{ attrs: { href: 'https://example.com/' }, type: 'link' }] const cards: AdfNode[] = [{ attrs: { url: 'https://example.com/roadmap' }, marks, type: 'inlineCard' }, { attrs: { data: { name: 'Roadmap', url: 'https://example.com/roadmap' } }, marks, type: 'inlineCard' }] for (const card of cards) {