mirror of
https://github.com/larvit/adf-codec.git
synced 2026-11-12 11:48:29 +01:00
Answer architecture round 4: 0 🔴, 1 🟡 (0 misread); one image rule in shown-text for both writers, a link mark's href kept past a bad title, the card rule recorded
This commit is contained in:
+2
-2
@@ -3,8 +3,8 @@
|
||||
## Unreleased
|
||||
|
||||
- `adfToHtml` writes ADF as HTML: paragraphs, headings, lists, quotes, code blocks, rules, hard
|
||||
breaks, a centered external image holding no attribute but its URL and alt, and `em`, `strong`,
|
||||
`code` and links as their elements. A paragraph, heading, quote, list, list item, code block or
|
||||
breaks, a centered, uncaptioned external image holding no attribute but its URL and alt, and `em`,
|
||||
`strong`, `code` and links as their elements. A paragraph, heading, quote, list, list item, code block or
|
||||
rule writes its other attributes as `data-*` attributes, `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,
|
||||
|
||||
@@ -20,8 +20,9 @@ products store as ADF.
|
||||
Flavored Markdown's callouts and highlights. It keeps the text, images by URL and link targets and
|
||||
drops the rest.
|
||||
- **HTML out:** `adfToHtml` writes paragraphs, headings, lists, quotes, code blocks, rules, line
|
||||
breaks, emphasis, inline code and links as their HTML elements. Every other node, an image
|
||||
Atlassian's editor wrote among them, becomes an element holding its JSON and showing its content.
|
||||
breaks, emphasis, inline code, links and a plain external image as their HTML elements. Every other
|
||||
node, an image Atlassian's editor wrote among them, becomes an element holding its JSON and showing
|
||||
its content.
|
||||
- **No throws:** every call returns a result; a refusal carries a code from a closed list.
|
||||
- **Runs anywhere:** pure functions with no runtime dependencies, in Node, Deno, Bun and browsers.
|
||||
TypeScript types ship beside the JavaScript.
|
||||
@@ -483,7 +484,7 @@ content. No stylesheet ships. The complete dialect:
|
||||
| `paragraph` | `<p>` |
|
||||
| `rule` | `<hr>` |
|
||||
| `text` marked `code`, `em`, `strong` | `<code>`, `<em>`, `<strong>`, the first mark outermost |
|
||||
| an attribute its element does not write, such as `localId`, on a paragraph, heading, quote, list, list item, code block or rule | `data-*` named as HTML's `dataset` names it: `<p data-local-id="a1f3">` |
|
||||
| an attribute its type holds that its element does not write, such as `localId`, on a paragraph, heading, quote, list, list item, code block or rule | `data-*` named as HTML's `dataset` names it: `<p data-local-id="a1f3">` |
|
||||
| any other node, or a node holding what its element cannot | `<div class="adf-carry" data-adf="{…}"><p>its content</p></div>`, a `<span>` inline |
|
||||
|
||||
- Every URL is written as the ADF holds it, `javascript:` included: a link's, an image's, and a
|
||||
|
||||
@@ -435,6 +435,11 @@ is written `[alt](href) ([image](url))`), so both targets stay; Goal 7 counts th
|
||||
marking a node that shows a note goes around the note. Recorded 2026-10-24 without a panel of its
|
||||
own: it joins the second panel's `<a>` 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,
|
||||
until `todo.md` item 133 writes inline cards as their own elements, in 0.3.0. Recorded 2026-10-24
|
||||
without a panel of its own: it puts the second panel's `<a>` around the text a card shows.
|
||||
|
||||
## No runtime dependencies
|
||||
|
||||
2026-08-23, the maintainer. Goal 8. Valid while ~20 lines of own code, or a vendored table, do each
|
||||
|
||||
+2
-2
@@ -84,8 +84,8 @@ later release writes it as its own element.
|
||||
next.
|
||||
- A hard break is `<br>`, and so is a line feed in text outside a `<div>`'s code block.
|
||||
- Text a link marks is `<a href="…" title="…">` around it, or the text alone where HTML cannot
|
||||
hold the link's `href` or `title`. A card is a link to the target of a link marking it, else to
|
||||
its URL.
|
||||
hold the link's `href` or `title`; a `title` that is no string is left out. A card is a link to
|
||||
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)`,
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import test from 'node:test'
|
||||
|
||||
import { isBlockNodeType, shownText, shownUrl } from './shown-text.ts'
|
||||
import type { AdfAttributes, AdfNode } from './document.ts'
|
||||
import { isBlockNodeType, shownImage, shownLink, shownText, shownUrl } from './shown-text.ts'
|
||||
|
||||
const moon = 'https://example.com/moon.png'
|
||||
const target = 'https://example.com/'
|
||||
|
||||
test('shows a date as its UTC day, one before 1970 included', () => {
|
||||
assert.equal(shownText({ attrs: { timestamp: '1700000000000' }, type: 'date' }), '2023-11-14')
|
||||
@@ -28,6 +32,36 @@ test('links an external image to its URL, and a stored one to nothing', () => {
|
||||
assert.equal(shownUrl({ attrs: { type: 'file', url: 'https://example.com/a.png' }, type: 'mediaInline' }), undefined)
|
||||
})
|
||||
|
||||
test('shows an image no link marks by its alt, else its URL, linked to its URL', () => {
|
||||
assert.deepEqual(shownImage({ attrs: { alt: 'The moon', type: 'external', url: moon }, type: 'media' }, undefined), { after: undefined, link: moon, text: 'The moon' })
|
||||
assert.deepEqual(shownImage({ attrs: { alt: ' ', type: 'external', url: moon }, type: 'mediaInline' }, undefined), { after: undefined, link: moon, text: moon })
|
||||
})
|
||||
|
||||
test('shows an image a link to its own URL marks by the same text, under that link', () => {
|
||||
assert.deepEqual(shownImage({ attrs: { alt: 'The moon', type: 'external', url: moon }, type: 'media' }, moon), { after: undefined, link: undefined, text: 'The moon' })
|
||||
assert.deepEqual(shownImage({ attrs: { type: 'external', url: moon }, type: 'mediaInline' }, moon), { after: undefined, link: undefined, text: moon })
|
||||
})
|
||||
|
||||
test('shows an image a link to another target marks by its alt, else the target, else its URL, under the link, its URL after it', () => {
|
||||
assert.deepEqual(shownImage({ attrs: { alt: 'The moon', type: 'external', url: moon }, type: 'media' }, target), { after: moon, link: undefined, text: 'The moon' })
|
||||
assert.deepEqual(shownImage({ attrs: { type: 'external', url: moon }, type: 'mediaInline' }, target), { after: moon, link: undefined, text: target })
|
||||
assert.deepEqual(shownImage({ attrs: { type: 'external', url: moon }, type: 'media' }, ' '), { after: moon, link: undefined, text: moon })
|
||||
})
|
||||
|
||||
test('shows an image with no URL by its alt, else by no text, a note standing in its place', () => {
|
||||
assert.deepEqual(shownImage({ attrs: { alt: 'The moon', type: 'file' }, type: 'media' }, target), { after: undefined, link: undefined, text: 'The moon' })
|
||||
assert.deepEqual(shownImage({ attrs: { type: 'file' }, type: 'mediaInline' }, undefined), { after: undefined, link: undefined, text: undefined })
|
||||
assert.deepEqual(shownImage({ attrs: { alt: 'The moon', type: 'external', url: ' ' }, type: 'media' }, undefined), { after: undefined, link: undefined, text: 'The moon' })
|
||||
})
|
||||
|
||||
test('follows a link mark holding a string href, keeping its title only where that is a string', () => {
|
||||
const linked = (attrs: AdfAttributes): AdfNode => ({ marks: [{ type: 'strong' }, { attrs, type: 'link' }], text: 'x', type: 'text' })
|
||||
assert.deepEqual(shownLink(linked({ href: target, title: 'Home' })), { href: target, title: 'Home' })
|
||||
assert.deepEqual(shownLink(linked({ href: target, title: 5 })), { href: target })
|
||||
assert.equal(shownLink(linked({ title: 'Home' })), undefined)
|
||||
assert.equal(shownLink({ text: 'x', type: 'text' }), undefined)
|
||||
})
|
||||
|
||||
test('counts a card standing apart as a block, as a block node is', () => {
|
||||
assert.equal(isBlockNodeType('blockCard'), true)
|
||||
assert.equal(isBlockNodeType('embedCard'), true)
|
||||
|
||||
+20
-5
@@ -1,7 +1,8 @@
|
||||
import type { AdfNode } from './document.ts'
|
||||
import type { JsonValue } from '../json-value.ts'
|
||||
import type { LinkAttrs } from './mark-attributes.ts'
|
||||
import { blockNodeModel } from './block-nodes.ts'
|
||||
import { nodeAttrs } from './document.ts'
|
||||
import { nodeAttrs, nodeMarks } from './document.ts'
|
||||
|
||||
export function isBlockNodeType(type: string): boolean {
|
||||
return blockNodeModel(type) !== undefined || type === 'blockCard' || type === 'embedCard'
|
||||
@@ -38,7 +39,24 @@ export function referenceName(node: AdfNode): string | undefined {
|
||||
}
|
||||
}
|
||||
|
||||
// The text a node shows in place of itself, read from the attribute holding its content; `undefined` where it holds none.
|
||||
// What an image shows in place of itself, `href` the target of the link marking it, if any: `text`, else a note; `link`, its URL, which `text` links to where no link marks it; `after`, its URL, shown after a link going elsewhere.
|
||||
export function shownImage(media: AdfNode, href: string | undefined): { after: string | undefined; link: string | undefined; text: string | undefined } {
|
||||
const alt = visibleText(nodeAttrs(media)['alt'])
|
||||
const url = shownUrl(media)
|
||||
if (url === undefined) return { after: undefined, link: undefined, text: alt }
|
||||
if (href === undefined) return { after: undefined, link: url, text: alt ?? url }
|
||||
if (href === url) return { after: undefined, link: undefined, text: alt ?? url }
|
||||
return { after: url, link: undefined, text: alt ?? visibleText(href) ?? url }
|
||||
}
|
||||
|
||||
// The link a node's first link mark makes: its `href` where that is a string, and its `title` only where that is one.
|
||||
export function shownLink(node: AdfNode): LinkAttrs | undefined {
|
||||
const { href, title } = nodeAttrs(nodeMarks(node).find((mark) => mark.type === 'link') ?? {})
|
||||
if (typeof href !== 'string') return undefined
|
||||
return typeof title === 'string' ? { href, title } : { href }
|
||||
}
|
||||
|
||||
// The text a node shows in place of itself, read from the attribute holding its content; `undefined` where it holds none, and for an image, which `shownImage` answers.
|
||||
export function shownText(node: AdfNode): string | undefined {
|
||||
const attrs = nodeAttrs(node)
|
||||
switch (node.type) {
|
||||
@@ -56,9 +74,6 @@ export function shownText(node: AdfNode): string | undefined {
|
||||
case 'extension':
|
||||
case 'inlineExtension':
|
||||
return visibleText(attrs['text'])
|
||||
case 'media':
|
||||
case 'mediaInline':
|
||||
return visibleText(attrs['alt']) ?? shownUrl(node)
|
||||
case 'mention': {
|
||||
const id = attrs['id']
|
||||
return visibleText(attrs['text']) ?? (typeof id === 'string' && id !== '' ? `@${id}` : undefined)
|
||||
|
||||
@@ -149,11 +149,12 @@ test('shows a carried node\'s content after a block it holds as a paragraph of i
|
||||
assert.equal(shown(html(adfToHtml(document({ content: [paragraph(text('a')), text('b')], type: 'panel' })))), '<p>a</p><p>b</p>')
|
||||
})
|
||||
|
||||
test('shows text a link marks in a carried node as a link', () => {
|
||||
test('shows text a link marks in a carried node as a link, leaving out a title that is no string', () => {
|
||||
const link: AdfMark = { attrs: { href: 'https://spec.commonmark.org/', title: 'The spec' }, type: 'link' }
|
||||
const panel: AdfNode = { content: [paragraph(text('Read the '), text('CommonMark spec', link), text(' first.'))], type: 'panel' }
|
||||
assert.equal(shown(html(adfToHtml(document(panel)))), '<p>Read the <a href="https://spec.commonmark.org/" title="The spec">CommonMark spec</a> first.</p>')
|
||||
assert.equal(shown(html(adfToHtml(document(paragraph(text('x', { type: 'underline' }, link)))))), '<a href="https://spec.commonmark.org/" title="The spec">x</a>')
|
||||
assert.equal(shown(html(adfToHtml(document(paragraph(text('x', { attrs: { href: 'https://spec.commonmark.org/', title: 5 }, type: 'link' })))))), '<a href="https://spec.commonmark.org/">x</a>')
|
||||
})
|
||||
|
||||
test('shows a hard break, and a line feed in carried text outside a code block, as a line break', () => {
|
||||
@@ -219,8 +220,11 @@ test('shows a carried image marked by a link to another target as that link, the
|
||||
assert.equal(image({ alt: 'The moon', type: 'external', url: 'https://example.com/' }), '<p><a href="https://example.com/">The moon</a></p>')
|
||||
const blank = shown(html(adfToHtml(document({ attrs: { layout: 'wide' }, content: [{ attrs: { type: 'external', url: 'https://example.com/moon.jpg' }, marks: [{ attrs: { href: ' ' }, type: 'link' }], type: 'media' }], type: 'mediaSingle' }))))
|
||||
assert.equal(blank, '<p><a href=" ">https://example.com/moon.jpg</a> (<a href="https://example.com/moon.jpg">image</a>)</p>')
|
||||
const inline: AdfNode = { attrs: { alt: 'The moon', type: 'external', url: 'https://example.com/moon.jpg' }, marks: [link], type: 'mediaInline' }
|
||||
assert.equal(shown(html(adfToHtml(document(paragraph(inline))))), '<a href="https://example.com/">The moon</a> (<a href="https://example.com/moon.jpg">image</a>)')
|
||||
const texted = shown(html(adfToHtml(document({ attrs: { layout: 'wide' }, content: [{ attrs: { alt: 'The moon', type: 'external', url: 'https://example.com/moon.jpg' }, marks: [link], text: 'Moon', type: 'media' }], type: 'mediaSingle' }))))
|
||||
assert.equal(texted, '<p><a href="https://example.com/">Moon</a> (<a href="https://example.com/moon.jpg">image</a>)</p>')
|
||||
const inline = (mark: AdfMark): string => shown(html(adfToHtml(document(paragraph({ attrs: { alt: 'The moon', type: 'external', url: 'https://example.com/moon.jpg' }, marks: [mark], type: 'mediaInline' })))))
|
||||
assert.equal(inline(link), '<a href="https://example.com/">The moon</a> (<a href="https://example.com/moon.jpg">image</a>)')
|
||||
assert.equal(inline({ attrs: { href: 'https://example.com/', title: 5 }, type: 'link' }), '<a href="https://example.com/">The moon</a> (<a href="https://example.com/moon.jpg">image</a>)')
|
||||
})
|
||||
|
||||
test('links a carried card a link marks to that link\'s target alone', () => {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { AdfNode } from '../../adf/document.ts'
|
||||
import type { LinkAttrs } from '../../adf/mark-attributes.ts'
|
||||
import { anchorOpener, jsonValue, languageClass, shownValue } from './escaping.ts'
|
||||
import { carriedJson } from '../../adf/carried-json.ts'
|
||||
import { largestNesting } from '../../nesting.ts'
|
||||
import { linkAttrs, type LinkAttrs } from '../../adf/mark-attributes.ts'
|
||||
import { nodeAttrs, nodeContent, nodeMarks } from '../../adf/document.ts'
|
||||
import { partsShownText, referenceName, shownText, shownUrl, visibleText } from '../../adf/shown-text.ts'
|
||||
import { nodeAttrs, nodeContent } from '../../adf/document.ts'
|
||||
import { partsShownText, referenceName, shownImage, shownLink, shownText, shownUrl } from '../../adf/shown-text.ts'
|
||||
import { success, type ConvertErrorPath, type Result } from '../../result.ts'
|
||||
|
||||
// The content a carry element shows of one block; `code` the code block holding it, if any.
|
||||
@@ -51,23 +51,25 @@ function shownBlocks(root: AdfNode, codeLineFeed: string): ShownBlock[] {
|
||||
// The node's own part of what its carry element shows.
|
||||
function shownPiece(node: AdfNode, lineFeed: string): string {
|
||||
if (node.type === 'hardBreak') return '<br>'
|
||||
const mark = nodeMarks(node).find((held) => held.type === 'link')
|
||||
const target = mark === undefined ? undefined : linkAttrs(nodeAttrs(mark))
|
||||
const url = shownUrl(node)
|
||||
if (['media', 'mediaInline'].includes(node.type) && target !== undefined && url !== undefined && target.href !== url) return linkedImage(node, target, url, lineFeed)
|
||||
const link = target ?? (url === undefined ? undefined : { href: url })
|
||||
const text = node.text ?? shownText(node)
|
||||
if (['media', 'mediaInline'].includes(node.type)) return linkedImage(node, lineFeed)
|
||||
return linkedText(node, shownText(node), shownUrl(node), lineFeed)
|
||||
}
|
||||
|
||||
// As portable markdown writes it inline, except that a link around a note stays (docs/decisions.md §The HTML dialect).
|
||||
function linkedImage(media: AdfNode, lineFeed: string): string {
|
||||
const { after, link, text } = shownImage(media, shownLink(media)?.href)
|
||||
const shown = linkedText(media, text, link, lineFeed)
|
||||
return after === undefined ? shown : `${shown} (${linked('image', { href: after })})`
|
||||
}
|
||||
|
||||
function linkedText(node: AdfNode, standIn: string | undefined, url: string | undefined, lineFeed: string): string {
|
||||
const link = shownLink(node) ?? (url === undefined ? undefined : { href: url })
|
||||
const text = node.text ?? standIn
|
||||
if (text !== undefined) return linked(shownValue(text, lineFeed), link)
|
||||
const name = referenceName(node)
|
||||
return name === undefined ? '' : linked(`<em>(${shownValue(name, lineFeed)})</em>`, link)
|
||||
}
|
||||
|
||||
// As portable markdown writes it inline (docs/decisions.md §The HTML dialect).
|
||||
function linkedImage(media: AdfNode, target: LinkAttrs, url: string, lineFeed: string): string {
|
||||
const text = visibleText(nodeAttrs(media)['alt']) ?? visibleText(target.href) ?? url
|
||||
return `${linked(shownValue(text, lineFeed), target)} (${linked('image', { href: url })})`
|
||||
}
|
||||
|
||||
// The shown HTML inside an `<a>` where it links and HTML can hold the link.
|
||||
function linked(shown: string, link: LinkAttrs | undefined): string {
|
||||
const anchor = link === undefined ? undefined : anchorOpener(link)
|
||||
|
||||
@@ -7,7 +7,7 @@ import { imageAside } from '../image-aside.ts'
|
||||
import { joinsWhenEditorNormal, sameMarkWhenEditorNormal } from './editor-normal.ts'
|
||||
import { largestNesting } from '../../nesting.ts'
|
||||
import { mergeAdjacentText, nodeAttrs, nodeContent, nodeMarks } from '../../adf/document.ts'
|
||||
import { partsShownText, referenceName, shownText, shownUrl, visibleText } from '../../adf/shown-text.ts'
|
||||
import { partsShownText, referenceName, shownImage, shownLink, shownText, shownUrl } from '../../adf/shown-text.ts'
|
||||
import { portableLineFallback, type PortableLineFallback } from '../emit/inline-line.ts'
|
||||
import { spellDestination } from '../commonmark/link-syntax.ts'
|
||||
|
||||
@@ -59,20 +59,15 @@ function cardLeaves(card: AdfNode, marks: readonly AdfMark[], container: LineCon
|
||||
return linkedLeaves(shownText(card) ?? href, href, marks, container)
|
||||
}
|
||||
|
||||
function linkedLeaves(text: string, href: string, marks: readonly AdfMark[], container: LineContainer): AdfNode[] {
|
||||
function linkedLeaves(text: string | undefined, href: string, marks: readonly AdfMark[], container: LineContainer): AdfNode[] {
|
||||
return textLeaves(text, [...marks.filter((mark) => mark.type !== 'link'), { attrs: { href }, type: 'link' }], container)
|
||||
}
|
||||
|
||||
// An image standing inline is a link to it: the reader builds no node for an inline image, reading it as its alt text linked, and inside a link as the link's text, then (image).
|
||||
function mediaLeaves(media: AdfNode, marks: readonly AdfMark[], container: LineContainer): AdfNode[] {
|
||||
const alt = visibleText(nodeAttrs(media)['alt'])
|
||||
const url = shownUrl(media)
|
||||
if (url === undefined) return textLeaves(alt, marks, container, referenceName(media))
|
||||
const link = marks.find((mark) => mark.type === 'link')
|
||||
const href = link === undefined ? undefined : nodeAttrs(link)['href']
|
||||
if (typeof href !== 'string') return linkedLeaves(alt ?? url, url, marks, container)
|
||||
if (href === url) return textLeaves(alt ?? url, marks, container)
|
||||
return [...textLeaves(alt ?? visibleText(href) ?? url, marks, container), ...imageAside((text) => linkedLeaves(text, url, marks, container))]
|
||||
const { after, link, text } = shownImage(media, shownLink(media)?.href)
|
||||
const shown = link === undefined ? textLeaves(text, marks, container, referenceName(media)) : linkedLeaves(text, link, marks, container)
|
||||
return after === undefined ? shown : [...shown, ...imageAside((word) => linkedLeaves(word, after, marks, container))]
|
||||
}
|
||||
|
||||
function noteLeaves(name: string): AdfNode[] {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|---|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| 142 | 0.3.0 | | security | **Settle in `docs/decisions.md`, weighing the pros and cons with the maintainer, whether `adfToHtml` writes a `javascript:`, `vbscript:`, `file:` or non-image `data:` URL as the ADF holds it, in a link's `href`, an image's `src` and each link the carry element shows.** | 2 | 2 | 2 | 4 | 3 | 6 by 3 | 4.5 |
|
||||
| 131 | 0.3.0 | | | **Ship `htmlToAdf`, reading back exactly what `adfToHtml` writes and refusing any other HTML as `unmappable-html`.** | 4 | 1 | 2 | 5 | 7 | 3 by 3 | 0.6 |
|
||||
| 133 | 0.3.0 | 131 | | **Write mentions, emoji, dates, statuses and inline cards as semantic HTML in `adfToHtml` and read them back in `htmlToAdf`, in place of the carry element.** | 2 | 2 | 2 | 4 | 4 | 6 by 3 | 3.0 |
|
||||
| 133 | 0.3.0 | 131 | decision | **Write mentions, emoji, dates, statuses and inline cards as semantic HTML in `adfToHtml` and read them back in `htmlToAdf`, in place of the carry element.** | 2 | 2 | 2 | 4 | 4 | 6 by 3 | 3.0 |
|
||||
| 132 | 0.3.0 | 131 | | **Write tables as `<table>` in `adfToHtml` and read them back in `htmlToAdf`, in place of the carry element.** | 2 | 2 | 2 | 4 | 5 | 6 by 4 | 2.4 |
|
||||
| 128 | 0.4.0 | | | **Pin the portable flavour's block reading and writing with tests: callout titles past one line, a link beside other marks in a title, the nesting limit of callouts and task lists, refusals inside tasks and their paths, and empty or non-paragraph tasks.** | 2 | 1 | 1 | 2 | 3 | code health by 3 | 19.6 |
|
||||
| 140 | 0.4.0 | | defect | **Read an inline link's target in time linear in the inline markdown, in every reader.** | 3 | 1 | 2 | 4 | 3 | 9 by 3 | 19.6 |
|
||||
@@ -28,8 +28,8 @@
|
||||
| 45 | 0.4.0 | | defect | **Replace `isAdfDocument` with a reader returning `Result<AdfDocument>`.** | 2 | 1 | 3 | 4 | 3 | 1 by 2 | 13.1 |
|
||||
| 68 | 0.4.0 | | defect | **Carry a non-text inline node holding `content` or `text`, a block holding `text`, and a leaf block holding `content`.** | 2 | 1 | 2 | 3 | 3 | 1 by 2 | 13.1 |
|
||||
| 127 | 0.4.0 | | principle | **Hold what `entity-references.test.ts`, `json-text.test.ts` and `result.test.ts` check by asserting outcomes, so none reads source text.** | 2 | 2 | 1 | 3 | 3 | code health by 2 | 13.1 |
|
||||
| 143 | 0.4.0 | | defect | **Keep the link around a note in `adfToPortableMarkdown`, so a linked stored image with no alt writes its note linked.** | 1 | 1 | 2 | 2 | 2 | 7 by 1 | 8.7 |
|
||||
| 144 | 0.4.0 | | | **Give every container `docker-runner.sh` and `publish.sh` start a memory limit and an equal swap limit, sized from the largest a gate leg measurably needs.** | 1 | 1 | 1 | 1 | 2 | code health by 1 | 8.7 |
|
||||
| 143 | 0.4.0 | | defect | **Keep the link around a note in `adfToPortableMarkdown`, so a linked stored image with no alt writes its note linked.** | 2 | 1 | 2 | 3 | 3 | 7 by 1 | 6.5 |
|
||||
| 112 | 0.4.0 | | | **Pad a GFM pipe table's short rows with empty cells, read its body rows holding no pipe, and read a one-column table without leading pipes in `portableMarkdownToAdf`.** | 2 | 1 | 2 | 3 | 3 | 4 by 4 | 6.2 |
|
||||
| 80 | 0.4.0 | | | **Name the node's path and the attribute key when `not-an-adf-document` refuses a node inside the document.** | 2 | 1 | 2 | 3 | 3 | 6 by 4 | 6.0 |
|
||||
| 81 | 0.4.0 | | | **Fail the gate unless the README's error tables list exactly the codes `ConvertErrorCode` holds.** | 1 | 1 | 1 | 1 | 2 | 3 by 2 | 4.2 |
|
||||
@@ -93,7 +93,9 @@ takes).
|
||||
|
||||
### 133. Write mentions, emoji, dates, statuses and inline cards as semantic HTML in `adfToHtml` and read them back in `htmlToAdf`, in place of the carry element.
|
||||
|
||||
A writer panel picks the elements, classes and `data-*` attributes.
|
||||
A writer panel picks the elements, classes and `data-*` attributes. Where a link marks a card, both
|
||||
the link's target and the card's URL stay in sight, a card showing only its name included
|
||||
(`docs/decisions.md` §The HTML dialect).
|
||||
|
||||
### 132. Write tables as `<table>` in `adfToHtml` and read them back in `htmlToAdf`, in place of the carry element.
|
||||
|
||||
@@ -212,12 +214,6 @@ Three tests read source text:
|
||||
Item 81 changes `result.test.ts` too. Found by the technical-principles audit of
|
||||
item 60, 2026-10-22.
|
||||
|
||||
### 143. Keep the link around a note in `adfToPortableMarkdown`, so a linked stored image with no alt writes its note linked.
|
||||
|
||||
`noteLeaves` (`src/markdown/portable/inline-reduction.ts`) drops the node's marks, so a stored
|
||||
image a link marks writes `_(image not included)_` without the link's target, which Goal 7 counts as
|
||||
content. `adfToHtml`'s carry element keeps it, `<a href="…"><em>(image)</em></a>`.
|
||||
|
||||
### 144. Give every container `docker-runner.sh` and `publish.sh` start a memory limit and an equal swap limit, sized from the largest a gate leg measurably needs.
|
||||
|
||||
`in_image`, `with_firefox` and `publish.sh` run `docker run` with neither `--memory` nor
|
||||
@@ -225,6 +221,19 @@ content. `adfToHtml`'s carry element keeps it, `<a href="…"><em>(image)</em></
|
||||
strings near V8's longest, about 512 MB each, so measure each leg's peak before picking the limit:
|
||||
one too small fails the gate.
|
||||
|
||||
### 143. Keep the link around a note in `adfToPortableMarkdown`, so a linked stored image with no alt writes its note linked.
|
||||
|
||||
`noteLeaves` (`src/markdown/portable/inline-reduction.ts`) drops the node's marks, so a stored
|
||||
image a link marks writes `_(image not included)_` without the link's target, which Goal 7 counts as
|
||||
content. `adfToHtml`'s carry element keeps it, `<a href="…"><em>(image)</em></a>`.
|
||||
|
||||
`cardLeaves`, in the same file, links a card to its own URL, so a card a link to another target
|
||||
marks writes a link to the card's URL and drops the mark's target. The carry element keeps both: it
|
||||
links the card's URL, as its text, to the mark's target (`docs/decisions.md` §The HTML dialect).
|
||||
|
||||
Move the choice of where a card's text and a note link into `src/adf/shown-text.ts`, beside
|
||||
`shownImage`'s for an image, so both writers render one answer.
|
||||
|
||||
### 112. Pad a GFM pipe table's short rows with empty cells, read its body rows holding no pipe, and read a one-column table without leading pipes in `portableMarkdownToAdf`.
|
||||
|
||||
GFM pads a short row with empty cells, reads a line holding no `|` after a table as a row, and
|
||||
|
||||
Reference in New Issue
Block a user