12a: spec/flavour.md and AGENTS.md spell the !adf: grammar
This commit was merged in pull request #82.
This commit is contained in:
@@ -43,8 +43,11 @@ Round-trip equality is a property tested over a corpus, not a claim made in pros
|
||||
|
||||
## 4. The flavour
|
||||
|
||||
- Directives, one grammar for everything markdown lacks: `:::panel info` … `:::` blocks,
|
||||
`:mention[@Mikael]{id=5b10a2}` inline. Prior art: CommonMark's generic-directives proposal.
|
||||
- Directives, one grammar for everything markdown lacks, namespaced under `!adf:`: `!adf:panel info`
|
||||
… `!adf:/panel` blocks, `!adf:mention[@Mikael]{id=5b10a2}` inline, `\!adf:` the one escape. Not
|
||||
CommonMark's generic-directives proposal: its `:::` claims a form prose writes, and its
|
||||
fence-length discipline ties a container's opener to its own body, where closing from the opener
|
||||
nests by itself and leaf versus container falls out of the node's content model.
|
||||
- Plain CommonMark is a subset, with carve-outs (`spec/flavour.md`): literal text shaped like a
|
||||
directive, a pipe table or a `~~` pair is claimed — plus one image gap.
|
||||
- Tables: one header row plus plain inline cells → pipe table; anything richer → directive form.
|
||||
@@ -98,15 +101,17 @@ against the README's personas.
|
||||
|
||||
The emitted markdown and HTML are contracts. After 1.0: previously-emitted output parsing
|
||||
differently, or not at all, is MAJOR; new syntax while old output still round-trips is MINOR.
|
||||
Pre-1.0, normal 0.x rules.
|
||||
Pre-1.0, normal 0.x rules. A spelled node's content model is part of that contract — leaf or
|
||||
container is the model, not the syntax — so giving a spelled node's model content it had not, or
|
||||
taking it away, is MAJOR whatever ADF's own schema does.
|
||||
|
||||
The error surface is a contract too. `ConvertError` is `{ code, message, path, position? }` — the
|
||||
code from a closed list a consumer may switch exhaustively, the message free text, the path the
|
||||
node's place from the document root, the position where a parse read the refusal in its input.
|
||||
A message names the violation, not the rule alone — a rule by itself states a truth the reader
|
||||
must invert before it reads as a failure — and where the flavour's claim refuses ordinary prose it
|
||||
names the escape that unclaims the form claimed: `\:::` for a directive line, `\|` for every pipe
|
||||
row, `\:` for an inline directive.
|
||||
names the escape that unclaims the form claimed: `\!adf:` for a directive, block line and inline
|
||||
alike, `\|` for every pipe row.
|
||||
Adding, removing or renaming a code is breaking, so a milestone meeting a new failure cause
|
||||
reuses a code where one fits; the list is complete at `0.1.0`. A code names the
|
||||
cause; where one cause recurs across node types, across one mark's attributes or across
|
||||
@@ -116,7 +121,7 @@ direction hits it, `unspellable-link` the destination and the title alike. Where
|
||||
apart, the line between them is what they name: `unspellable-character` is a character CommonMark
|
||||
rewrites wherever text holds it, `unspellable-whitespace` the newline no inline directive's
|
||||
content slot spans, in either direction. A claim code names the spelling claimed, never the node that spelling would have built:
|
||||
a malformed `:::table` is a `malformed-directive`, and an alignment colon a `malformed-pipe-table` —
|
||||
a malformed `!adf:table` is a `malformed-directive`, and an alignment colon a `malformed-pipe-table` —
|
||||
the flavour's own delimiter row is `-` runs, so the grammar refuses the colon rather than ADF's
|
||||
missing column model doing it. A refusal no spelling recovers from is a gap in the flavour rather
|
||||
than a code: give the flavour the spelling and the code goes, which the freeze is the last moment
|
||||
@@ -125,8 +130,8 @@ spelling writes rides the carry with its node. A directive whose name reads back
|
||||
`unknown-directive-name` rather than a claim code — the spelling is well formed, and telling that
|
||||
apart from a typo is what a consumer switches on when a later MINOR gives the name meaning. A
|
||||
reserved name is a known name, so never that code, and the two the flavour reserves part on form:
|
||||
a form the grammar does not have is a claim code — `::adf`, whose carry is the fence — and a
|
||||
well-formed form in the wrong place is `unsupported-node-shape`, `::listBreak` parting anything
|
||||
a form the grammar does not have is a claim code — `!adf:carry`, whose carry is the fence — and a
|
||||
well-formed form in the wrong place is `unsupported-node-shape`, `!adf:listBreak` parting anything
|
||||
but two adjacent lists of one type. What
|
||||
the grammar itself refuses stays a claim code, key order among it; a well-formed directive the
|
||||
node tables refuse — an attribute a node does not hold or spells elsewhere, a value outside its
|
||||
|
||||
+113
-110
@@ -3,7 +3,7 @@
|
||||
The grammar of the extended markdown `adfToMarkdown` emits and `markdownToAdf` parses. Plain
|
||||
CommonMark is a subset apart from raw HTML (below), with three carve-outs: literal text that
|
||||
matches directive syntax below or reads as a pipe table is claimed by the flavour, and a matched
|
||||
`~~` pair spells `strike` (escape the `:`, `|` or `~` to keep it literal) — and one gap: a
|
||||
`~~` pair spells `strike` (escape the `!adf:`, `|` or `~` to keep it literal) — and one gap: a
|
||||
CommonMark image fits only as its own
|
||||
title-less paragraph — mid-text and titled images are named errors. The emitted form is contract
|
||||
(AGENTS.md §8). Per-node syntaxes build on this grammar in the sections below.
|
||||
@@ -25,7 +25,7 @@ normalizes to it through the round-trip.
|
||||
whose first item is empty), whatever block sits above it. Blank lines between items normalize
|
||||
away, and no list opens beside one of its own kind — the marker change CommonMark starts a
|
||||
second list on merges instead: ADF records no tightness, so one `- ` spelling reads two
|
||||
adjacent lists of a kind back as one. The leaf `::listBreak` parts them, taking the separation
|
||||
adjacent lists of a kind back as one. The leaf `!adf:listBreak` parts them, taking the separation
|
||||
any directive block takes where it sits. It builds no node, and it reads only between two
|
||||
adjacent lists of one type: elsewhere, or carrying an argument, `{attrs}` or a body, it is a
|
||||
named error. A list whose item holds a line of spaces or tabs alone, which a list item reads
|
||||
@@ -43,7 +43,8 @@ normalizes to it through the round-trip.
|
||||
- Hard break: backslash at end of line (survives editors that trim trailing spaces). Where
|
||||
CommonMark admits no spelling — the end of a block, inside an ATX heading — or where the node
|
||||
carries an attribute, it is the inline directive.
|
||||
- An empty paragraph — real payloads carry them — is `::paragraph`.
|
||||
- An empty paragraph — real payloads carry them — is an `!adf:paragraph` … `!adf:/paragraph` pair
|
||||
holding nothing.
|
||||
- Links `[text](url)`; `<…>` around a destination containing spaces, `<>` an empty one beside a
|
||||
title; title in double quotes. A backslash escapes a parenthesis the destination leaves
|
||||
unbalanced, and a quote inside the title; a balanced pair stays bare. `<url>` autolink form only
|
||||
@@ -72,56 +73,59 @@ normalizes to it through the round-trip.
|
||||
|
||||
## Directives
|
||||
|
||||
One grammar for everything CommonMark lacks. A directive name is `[a-z][A-Za-z0-9]*` — the ADF
|
||||
node and mark names the sections below spell as directives. Recognition is syntactic and
|
||||
name-set-independent: anything matching the forms below parses as a directive regardless of
|
||||
whether the name is known, and an unknown name is an error result naming it — so output an old
|
||||
emitter escaped stays escaped, and erroring input gaining meaning later is MINOR, never a reparse
|
||||
(§8). Each name belongs to one position, and a name the other one spells — a mark or an inline
|
||||
node written as a block directive, a block node written inline — is a different error, naming the
|
||||
spelling it takes. Two reserved names read back to no node: `adf` for the opaque carry, as both
|
||||
directive name and fence info string, and `listBreak` for the leaf that parts two adjacent lists
|
||||
(Canonical form).
|
||||
One grammar for everything CommonMark lacks, namespaced: every directive opens with the literal
|
||||
`!adf:`. A directive name is `[a-z][A-Za-z0-9]*` — the ADF node and mark names the sections below
|
||||
spell as directives. Recognition is syntactic and name-set-independent: anything matching the forms
|
||||
below parses as a directive regardless of whether the name is known, and an unknown name is an
|
||||
error result naming it at the opener, whatever follows it — so output an old emitter escaped stays
|
||||
escaped, and erroring input gaining meaning later is MINOR, never a reparse (§8). Each name belongs
|
||||
to one position, and a name the other one spells — a mark or an inline node written as a block
|
||||
directive, a block node written inline — is a different error, naming the spelling it takes. Two
|
||||
reserved names read back to no node: `carry` for the opaque carry, as both directive name and fence
|
||||
info string, and `listBreak` for the leaf that parts two adjacent lists (Canonical form).
|
||||
|
||||
**Inline**: `:name[content]{attrs}`, on one line — an inline directive never spans lines.
|
||||
**Claiming**: an unescaped `!adf:` claims wherever it stands. What follows picks the form: `/name`
|
||||
closes a container, and a name picks by what follows it in turn — a space or the line's end a block
|
||||
line, `[` or `{` an inline directive. A `!adf:` completing none of the three is a named error, and
|
||||
`\!adf:` is the literal, block and inline alike. A claimed block line also ends a lazy continuation:
|
||||
the blockquote or list item whose paragraph CommonMark would fold it into closes instead.
|
||||
|
||||
**Inline**: `!adf:name[content]{attrs}`, on one line — an inline directive never spans lines.
|
||||
`[content]` is inline markdown; brackets inside balance as in CommonMark link text, `\]` for a
|
||||
literal bracket. Whitespace at either edge of `[content]`, space or tab, is part of it and
|
||||
survives inline parsing. Each section below says whether content is required. `:` opens a
|
||||
directive only when the name is followed immediately by `[` or `{`, and `{attrs}` must follow
|
||||
`]` (or the name) with no gap — anything else (`10:30`, `:smile:`, a stray `{…}` in text) is
|
||||
literal text. An inline directive binds as a unit before bracket matching, the way a code span
|
||||
does: a `]` or `(` inside its `{attrs}` is the directive's, never the enclosing content's, and a
|
||||
`(` after its closing `]` opens no link.
|
||||
survives inline parsing. Each section below says whether content is required, and `{attrs}` must
|
||||
follow `]` (or the name) with no gap. An inline directive binds as a unit before bracket matching,
|
||||
the way a code span does: a `]` or `(` inside its `{attrs}` is the directive's, never the enclosing
|
||||
content's, and a `(` after its closing `]` opens no link.
|
||||
|
||||
**Container block**:
|
||||
**Block container**:
|
||||
|
||||
```
|
||||
:::name arg {attrs}
|
||||
!adf:name arg {attrs}
|
||||
block content
|
||||
:::
|
||||
!adf:/name
|
||||
```
|
||||
|
||||
The fence is three or more colons. `arg` is one optional bare token whose meaning each node
|
||||
defines (e.g. the panel type). The body is block markdown. The closing fence is a line of at
|
||||
least the opening's length and closes the innermost open container however long its run, and a
|
||||
container's fence is longer than every directive fence line anywhere in its body, however deeply a list item or blockquote nests it; a colon run inside a code
|
||||
fence or opaque carry is content. Canonical form uses minimal lengths.
|
||||
Directive fence lines follow code-fence indentation (up to three spaces relative to their
|
||||
container).
|
||||
`arg` is one optional bare token whose meaning each node defines (e.g. the panel type). The body is
|
||||
block markdown. The closer names the innermost open container and carries nothing after the name;
|
||||
one naming another node, or standing where no container is open, is a named error. Opening and
|
||||
closing is what nests, so the opener reads the same at every depth, and a closer crosses no list
|
||||
item or blockquote edge — a container opened inside one closes inside it. Directive block lines
|
||||
follow code-fence indentation (up to three spaces relative to their container); an `!adf:` inside a
|
||||
code fence or opaque carry is content.
|
||||
|
||||
**Leaf block**: `::name arg {attrs}` — a block-position node with no body, `arg` reading as
|
||||
above.
|
||||
**Block leaf**: `!adf:name arg {attrs}` — the same opener with no closer, `arg` reading as above.
|
||||
|
||||
Which of the two a node takes is its content model, never the spelling: a model taking content is
|
||||
written as an opener–closer pair and one taking none as a leaf, so a leaf given a body and a
|
||||
container missing its closer are each a named error. A node holding no content whose model takes
|
||||
some is an empty pair. A spelled node's content model is contract in consequence — changing one is
|
||||
MAJOR (AGENTS.md §8).
|
||||
|
||||
Canonical spacing is the only spacing input reads: one space parts the name, `arg` and `{attrs}`,
|
||||
and one parts each attribute pair, with no padding inside the braces. Trailing whitespace on a
|
||||
directive block line is tolerated in input, never emitted.
|
||||
|
||||
**Claiming at block level**, symmetric with inline: a line whose leading run of two or more
|
||||
colons is followed immediately by a name character is claimed and must parse fully as a container
|
||||
opening or a leaf, else it is a named error. A bare colon-run line is a closing fence while a
|
||||
container is open, a named error otherwise. A claimed line also ends a lazy continuation: the
|
||||
blockquote or list item whose paragraph CommonMark would fold it into closes instead.
|
||||
|
||||
**Attributes**: `{key=value key2="two words"}`. `{attrs}` is optional in every form, and `{}` is
|
||||
valid — no attributes. A bare value matches `[A-Za-z0-9_-]+`; any other value is double-quoted
|
||||
with JSON string escaping (`\"` `\\` `\n` `\t` `\uXXXX`, …) — total over
|
||||
@@ -131,24 +135,23 @@ after a directive binds and stay raw. The closing `}` is the first one outside q
|
||||
quoted value holds `}` unescaped. All values are strings at the grammar level; each node's section
|
||||
assigns types.
|
||||
Canonical form orders keys alphabetically, spells values bare wherever allowed, escapes inside
|
||||
quotes in the shortest form each escape has, and omits empty `{attrs}` except where the `{` itself
|
||||
claims the directive (`:hardBreak{}`). Input reads that spelling alone: keys out of order, a value
|
||||
quoted where bare carries it, an escape longer than it need be, an empty `{attrs}` the name or the
|
||||
`[content]` already claims, and a number or `json` value outside its canonical JSON spelling are
|
||||
each a named error naming the spelling to write instead.
|
||||
quotes in the shortest form each escape has, and omits empty `{attrs}` except where the `{` is what
|
||||
ends the name (`!adf:hardBreak{}`). Input reads that spelling alone: keys out of order, a value
|
||||
quoted where bare carries it, an escape longer than it need be, an empty `{attrs}` on a block line
|
||||
or after a `[content]`, and a number or `json` value outside its canonical JSON spelling are each a
|
||||
named error naming the spelling to write instead.
|
||||
|
||||
**Escaping**: the emitter backslash-escapes whatever literal text would otherwise parse as
|
||||
directive syntax — the leading `:` of a would-be directive, `]` inside content, a bracket a link's
|
||||
destination and title inside content leave unbalanced, a backtick there that would open a code span
|
||||
and a `:` there that would open an inline directive, a `{` right after a directive's closing `]`,
|
||||
which would otherwise be read as the attributes it has none of; outside code spans and code blocks,
|
||||
a backslash before `:` in input yields a literal colon.
|
||||
directive syntax — every literal `!adf:`, `]` inside content, a bracket a link's destination and
|
||||
title inside content leave unbalanced, a backtick there that would open a code span, a `{` right
|
||||
after a directive's closing `]`, which would otherwise be read as the attributes it has none of;
|
||||
outside code spans and code blocks, `\!adf:` in input yields the literal text.
|
||||
|
||||
**Malformed directives are error results**, named: an unclosed container at end of input, a body
|
||||
fence line of the container's length or longer, a bare colon-run line outside any container or
|
||||
shorter than the fence it would close, an inline `[content]` or `{attrs}` left unclosed at end of
|
||||
line, unparseable or duplicate-keyed attrs, invalid JSON in an opaque carry. Never a silent
|
||||
literal-text fallback — a typo that reparses as prose is the silent loss §2 refuses.
|
||||
**Malformed directives are error results**, named: an unclosed container at end of input, a closer
|
||||
naming no open container or a node other than the innermost open one, a leaf given a body, an
|
||||
`!adf:` completing no directive, an inline `[content]` or `{attrs}` left unclosed at end of line,
|
||||
unparseable or duplicate-keyed attrs, invalid JSON in an opaque carry. Never a silent literal-text
|
||||
fallback — a typo that reparses as prose is the silent loss §2 refuses.
|
||||
|
||||
## The opaque carry (AGENTS.md §3)
|
||||
|
||||
@@ -158,16 +161,15 @@ a node the emitter spells natively: it restores unreinterpreted, and the next em
|
||||
canonically (AGENTS.md §2). Block and inline positions canonicalize differently, each fitting
|
||||
where it sits:
|
||||
|
||||
- **Block position**: a fenced code block with info string `adf`, body = the node's JSON —
|
||||
- **Block position**: a fenced code block with info string `carry`, body = the node's JSON —
|
||||
two-space indent, object keys sorted.
|
||||
- **Inline position**: `:adf{json="…"}` — compact serialization (keys sorted, no whitespace),
|
||||
- **Inline position**: `!adf:carry{json="…"}` — compact serialization (keys sorted, no whitespace),
|
||||
JSON-string-escaped into the attribute.
|
||||
|
||||
The info string `adf` is reserved: a genuine `codeBlock` whose `language` is exactly `adf` takes
|
||||
The info string `carry` is reserved: a genuine `codeBlock` whose `language` is exactly `carry` takes
|
||||
the attribute the section below keeps for a language no info string holds, so the reservation
|
||||
stays absolute.
|
||||
In block-directive positions (`::adf`, `:::adf`) the reserved name is a named error — the
|
||||
carry's block form is the fence.
|
||||
In block-directive position `!adf:carry` is a named error — the carry's block form is the fence.
|
||||
|
||||
## Raw HTML in input
|
||||
|
||||
@@ -193,9 +195,9 @@ editor-normal ADF reads an empty attrs object, marks array or content array as t
|
||||
(AGENTS.md §2) — the grammar's empty-`{attrs}` omission already collapses the two spellings.
|
||||
|
||||
Marks on a block node ride the reserved attribute key `marks` — the node's marks array as a
|
||||
`json` value: `::::layoutSection {marks="[{\"attrs\":{\"mode\":\"wide\"},\"type\":\"breakout\"}]"}`.
|
||||
`json` value: `!adf:layoutSection {marks="[{\"attrs\":{\"mode\":\"wide\"},\"type\":\"breakout\"}]"}`.
|
||||
A section saying its body is inline takes at most one paragraph, whose inline content becomes
|
||||
the node's `content`; any other body is a named error, and a node holding no content is the leaf.
|
||||
the node's `content`; any other body is a named error, and an empty pair is a node holding none.
|
||||
|
||||
A node the sections cannot spell rides the opaque carry: an attrs key its section does not
|
||||
list, a value that is not the section's type, or an arg-slot value that is no bare token. In
|
||||
@@ -212,7 +214,7 @@ form.
|
||||
- `codeBlock` — container, body one fenced code block whose info string is the language and whose
|
||||
content is the node's. Attributes: `hideLineNumbers` (boolean), `language` (string), `localId`
|
||||
(string), `uniqueId` (string), `wrap` (boolean). A language no info string carries back — empty,
|
||||
the reserved `adf`, or holding a backtick, a backslash, a control character, edge whitespace or
|
||||
the reserved `carry`, or holding a backtick, a backslash, a control character, edge whitespace or
|
||||
an entity reference — rides the `language` attribute instead and the fence carries no info
|
||||
string; writing it in the slot that rule leaves empty, or in both, is a named error. The body is
|
||||
one ordinary code block, and a fence's info string decodes escapes and entity references as any
|
||||
@@ -227,11 +229,11 @@ form.
|
||||
- `rule` — leaf. Attributes: `localId` (string).
|
||||
|
||||
````
|
||||
:::codeBlock {localId=01a03d5c-9b21-73f4-8e6a-0c47b1d9e2f8 wrap=true}
|
||||
!adf:codeBlock {localId=01a03d5c-9b21-73f4-8e6a-0c47b1d9e2f8 wrap=true}
|
||||
```rust
|
||||
fn main() {}
|
||||
```
|
||||
:::
|
||||
!adf:/codeBlock
|
||||
````
|
||||
|
||||
### Panel
|
||||
@@ -242,9 +244,9 @@ fn main() {}
|
||||
panels.
|
||||
|
||||
```
|
||||
:::panel warning
|
||||
!adf:panel warning
|
||||
Check the collation before importing.
|
||||
:::
|
||||
!adf:/panel
|
||||
```
|
||||
|
||||
### Expand
|
||||
@@ -253,9 +255,9 @@ Check the collation before importing.
|
||||
which. Attributes: `localId` (string), `title` (string).
|
||||
|
||||
```
|
||||
:::expand {title="Full build log"}
|
||||
!adf:expand {title="Full build log"}
|
||||
…
|
||||
:::
|
||||
!adf:/expand
|
||||
```
|
||||
|
||||
### The media family
|
||||
@@ -264,19 +266,19 @@ Check the collation before importing.
|
||||
(string), `localId` (string), `occurrenceKey` (string), `type` (`external` `file` `link`),
|
||||
`url` (string), `width` (number). `file` and `link` media carry `collection` + `id`;
|
||||
`external` media carry `url`.
|
||||
- `mediaSingle` — container: one `::media`, then optionally one `:::caption`. Attributes:
|
||||
- `mediaSingle` — container: one `!adf:media`, then optionally one `!adf:caption`. Attributes:
|
||||
`layout` (`align-end` `align-start` `center` `full-width` `wide` `wrap-left` `wrap-right`),
|
||||
`localId` (string), `width` (number), `widthType` (`percentage` `pixel`).
|
||||
- `caption` — container, inline body. Attributes: `localId` (string).
|
||||
- `mediaGroup` — container of `::media` leaves. Attributes: none.
|
||||
- `mediaGroup` — container of `!adf:media` leaves. Attributes: none.
|
||||
|
||||
```
|
||||
::::mediaSingle {layout=center width=50}
|
||||
::media {collection=MediaServicesSample id=4478e39c-cf9b-41d1-ba92-68589487cd75 type=file}
|
||||
:::caption
|
||||
!adf:mediaSingle {layout=center width=50}
|
||||
!adf:media {collection=MediaServicesSample id=4478e39c-cf9b-41d1-ba92-68589487cd75 type=file}
|
||||
!adf:caption
|
||||
The moon, at night.
|
||||
:::
|
||||
::::
|
||||
!adf:/caption
|
||||
!adf:/mediaSingle
|
||||
```
|
||||
|
||||
**The CommonMark image.** A paragraph whose entire inline content is one image `` is
|
||||
@@ -319,27 +321,27 @@ inline layer's ordinary CommonMark escaping yields the pipe; each cell is the in
|
||||
one paragraph, trimmed; canonical form pads cells with single spaces and ends rows with `|`
|
||||
(optional in input). Named errors: a delimiter or body row whose cell count differs from the
|
||||
header's, and an alignment colon in the delimiter row — ADF holds no column alignment. In a
|
||||
pipe cell a hard break is `:hardBreak{}` and a literal `|` is `\|`; a `|` inside a quoted
|
||||
pipe cell a hard break is `!adf:hardBreak{}` and a literal `|` is `\|`; a `|` inside a quoted
|
||||
attribute value is already `\u007c`, so the split never reaches it.
|
||||
|
||||
The directive form nests cells as containers of block content inside `tableRow` containers:
|
||||
|
||||
```
|
||||
:::::table {isNumberColumnEnabled=true width=760}
|
||||
::::tableRow
|
||||
:::tableHeader {colspan=2 colwidth="[340,420]"}
|
||||
!adf:table {isNumberColumnEnabled=true width=760}
|
||||
!adf:tableRow
|
||||
!adf:tableHeader {colspan=2 colwidth="[340,420]"}
|
||||
Assembly
|
||||
:::
|
||||
::::
|
||||
::::tableRow
|
||||
:::tableCell {background="#deebff"}
|
||||
!adf:/tableHeader
|
||||
!adf:/tableRow
|
||||
!adf:tableRow
|
||||
!adf:tableCell {background="#deebff"}
|
||||
Bolt M8
|
||||
:::
|
||||
:::tableCell {valign=top}
|
||||
!adf:/tableCell
|
||||
!adf:tableCell {valign=top}
|
||||
40
|
||||
:::
|
||||
::::
|
||||
:::::
|
||||
!adf:/tableCell
|
||||
!adf:/tableRow
|
||||
!adf:/table
|
||||
```
|
||||
|
||||
- `table` — container of `tableRow` containers. Attributes: `displayMode` (`default` `fixed`),
|
||||
@@ -363,14 +365,14 @@ Bolt M8
|
||||
free-form; the editor writes `DECIDED`).
|
||||
|
||||
```
|
||||
::::taskList {localId=0198f3a2-7c41-7f2e-9b3a-4d8e2c1a6b90}
|
||||
:::taskItem DONE {localId=0198f3a2-8d52-70b1-8c4f-5e9f3d2b7ca1}
|
||||
!adf:taskList {localId=0198f3a2-7c41-7f2e-9b3a-4d8e2c1a6b90}
|
||||
!adf:taskItem DONE {localId=0198f3a2-8d52-70b1-8c4f-5e9f3d2b7ca1}
|
||||
Write the spec
|
||||
:::
|
||||
:::taskItem TODO {localId=0198f3a2-9e63-7d80-a15b-6fa04e3c8db2}
|
||||
!adf:/taskItem
|
||||
!adf:taskItem TODO {localId=0198f3a2-9e63-7d80-a15b-6fa04e3c8db2}
|
||||
Ship it
|
||||
:::
|
||||
::::
|
||||
!adf:/taskItem
|
||||
!adf:/taskList
|
||||
```
|
||||
|
||||
### Layout
|
||||
@@ -380,14 +382,14 @@ Ship it
|
||||
`middle` `top`), `width` (number — percent).
|
||||
|
||||
```
|
||||
::::layoutSection
|
||||
:::layoutColumn {width=50}
|
||||
!adf:layoutSection
|
||||
!adf:layoutColumn {width=50}
|
||||
Left.
|
||||
:::
|
||||
:::layoutColumn {width=50}
|
||||
!adf:/layoutColumn
|
||||
!adf:layoutColumn {width=50}
|
||||
Right.
|
||||
:::
|
||||
::::
|
||||
!adf:/layoutColumn
|
||||
!adf:/layoutSection
|
||||
```
|
||||
|
||||
### Extensions
|
||||
@@ -399,7 +401,7 @@ Right.
|
||||
- `extensionFrame` — container, block body. Attributes: none.
|
||||
|
||||
```
|
||||
::extension {extensionKey=toc extensionType="com.atlassian.confluence.macro.core" parameters="{\"maxLevel\":2}"}
|
||||
!adf:extension {extensionKey=toc extensionType="com.atlassian.confluence.macro.core" parameters="{\"maxLevel\":2}"}
|
||||
```
|
||||
|
||||
### Sync blocks
|
||||
@@ -408,7 +410,7 @@ Right.
|
||||
`localId` (string), `resourceId` (string).
|
||||
|
||||
```
|
||||
::syncBlock {localId=0198f3a2-af74-7e91-b26c-70b15f4d9ec3 resourceId="ari:cloud:confluence:site/page/123"}
|
||||
!adf:syncBlock {localId=0198f3a2-af74-7e91-b26c-70b15f4d9ec3 resourceId="ari:cloud:confluence:site/page/123"}
|
||||
```
|
||||
|
||||
## Inline nodes
|
||||
@@ -418,7 +420,7 @@ the nodes below, `emoji`, `mention` and `status` spell their `text` attribute in
|
||||
as plain text: `[]` is the empty string, absent content is the absent attribute, non-empty content
|
||||
parsing to anything but one unmarked text node — adjacent text nodes with identical marks and no
|
||||
attributes merged first — is a named error, and so is a `text` key in `{attrs}`. An enclosing mark
|
||||
spelling does not reach into the slot. The rest take no content, `:text` included; content on a
|
||||
spelling does not reach into the slot. The rest take no content, `!adf:text` included; content on a
|
||||
node that takes none is a named error.
|
||||
|
||||
- `date` — Attributes: `localId` (string), `timestamp` (string, epoch milliseconds).
|
||||
@@ -436,15 +438,15 @@ node that takes none is a named error.
|
||||
(string), `style` (string), `text` (string).
|
||||
|
||||
```
|
||||
:status[In review]{color=yellow} — :mention[@Mikael]{id=01a032c3-7a7c-775f-a730-2d79351338b4}
|
||||
!adf:status[In review]{color=yellow} — !adf:mention[@Mikael]{id=01a032c3-7a7c-775f-a730-2d79351338b4}
|
||||
|
||||
Shipped :emoji[🎉]{shortName=":tada:"} on :date{timestamp=1756080000000}.
|
||||
Shipped !adf:emoji[🎉]{shortName=":tada:"} on !adf:date{timestamp=1756080000000}.
|
||||
```
|
||||
|
||||
**Whitespace CommonMark cannot hold.** A newline inside a text node, and a space or tab where
|
||||
CommonMark strips or refuses one — a block's inline content edges, either side of a line break,
|
||||
an em, strong or strike spelling's inner edges, a pipe cell's edges — is spelled
|
||||
`:text{text="…"}`, the reserved key carrying the node's text, escaped by the attribute grammar
|
||||
`!adf:text{text="…"}`, the reserved key carrying the node's text, escaped by the attribute grammar
|
||||
and never literal: pipe cells trim and pad. The emitter wraps the whitespace run alone and leaves
|
||||
the rest plain text; `markdownToAdf` merges adjacent text nodes carrying identical marks and no
|
||||
attributes (AGENTS.md §2). Input reads that spelling alone: the value is one run of spaces and
|
||||
@@ -452,14 +454,14 @@ tabs, or one run of newlines, and anything else — a mixed run, or text CommonM
|
||||
is a named error.
|
||||
|
||||
```
|
||||
:text{text=" "}Two leading spaces held, and one text node split:text{text="\n"}over two lines.
|
||||
!adf:text{text=" "}Two leading spaces held, and one text node split!adf:text{text="\n"}over two lines.
|
||||
```
|
||||
|
||||
## Marks
|
||||
|
||||
An inline node's marks ride the spelling wrapped around them, never the block sections' reserved
|
||||
`marks` key. `code`, `em`, `link`, `strike` and `strong` keep their markdown spellings, and are
|
||||
not directive names: `:em[x]` is a named error. `border`, `subsup`, `textColor` and `underline`
|
||||
not directive names: `!adf:em[x]` is a named error. `border`, `subsup`, `textColor` and `underline`
|
||||
are inline directives, content required non-empty.
|
||||
|
||||
- `border` — Attributes: `color` (string, `#rrggbb` or `#rrggbbaa`), `size` (number, 1–3).
|
||||
@@ -470,7 +472,8 @@ are inline directives, content required non-empty.
|
||||
- `underline` — Attributes: none.
|
||||
|
||||
A spelling adds its mark to every inline node it wraps, and nesting is the marks array in order,
|
||||
outermost first: `_:underline[x]_` gives marks `[em, underline]`, `:underline[_x_]` the reverse.
|
||||
outermost first: `_!adf:underline[x]_` gives marks `[em, underline]`, `!adf:underline[_x_]` the
|
||||
reverse.
|
||||
`adfToMarkdown` nests in the order the array holds rather than sorting it — §2's equality
|
||||
restores the array, not a set — and opens each spelling once over the longest run of adjacent
|
||||
inline nodes carrying an identical mark, attributes included, at that depth. A run breaks at every
|
||||
@@ -488,7 +491,7 @@ opaque carry inside a mark spelling is a named error in input: the carry restore
|
||||
exactly, marks included (AGENTS.md §3).
|
||||
|
||||
```
|
||||
:textColor[**Overdue**]{color="#ae2e24"}, H:subsup[2]{type=sub}O, :underline[signed].
|
||||
!adf:textColor[**Overdue**]{color="#ae2e24"}, H!adf:subsup[2]{type=sub}O, !adf:underline[signed].
|
||||
|
||||
:border[:mediaInline{collection=contentId-98237 id=01a032c3-7a90-70c9-88f6-c60f710eda07}]{color="#091e42" size=2}
|
||||
!adf:border[!adf:mediaInline{collection=contentId-98237 id=01a032c3-7a90-70c9-88f6-c60f710eda07}]{color="#091e42" size=2}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user