Compare commits
66 Commits
v0.1.0
...
9ba2d09ee3
| Author | SHA1 | Date | |
|---|---|---|---|
| 9ba2d09ee3 | |||
| 65c09bdbae | |||
| 302804cc4e | |||
| fe064efdb9 | |||
| 16af0b9592 | |||
| 4ac4a119e4 | |||
| ad1f3e996b | |||
| b6ba440410 | |||
| 7d20f3e895 | |||
| 5fe2e6ec42 | |||
| ef71d55c13 | |||
| a3073c69b2 | |||
| 8e7d429379 | |||
| 755b5cbe01 | |||
| fe47e6bc7b | |||
| aeb54d98d1 | |||
| 4735933d61 | |||
| ec5c92d4fd | |||
| fb10a1dc04 | |||
| 2901a28fcb | |||
| 047827fd86 | |||
| e23fd9495e | |||
| 10d63ff8e6 | |||
| f8149321f2 | |||
| a581e454ae | |||
| 342c2027d8 | |||
| fade9f51ea | |||
| b6930b45a4 | |||
| e166e969cb | |||
| 507f68aa13 | |||
| a2672bdc5b | |||
| 9303235434 | |||
| eafd863a32 | |||
| 1125715371 | |||
| 31156f4914 | |||
| 0185879943 | |||
| 9cd4b33020 | |||
| 758a5724e9 | |||
| 276433d635 | |||
| 7672531cdc | |||
| ec5f80e47f | |||
| da58a59f70 | |||
| 2796487b30 | |||
| dcea401ec2 | |||
| 25d851cc67 | |||
| 0f03edc8ef | |||
| 37f59cec40 | |||
| aa34d6e7fd | |||
| 3c1bacace6 | |||
| f8a5d5e9b3 | |||
| c9fb85f9db | |||
| 24b2e8cbbe | |||
| f66efabcbb | |||
| e424b35fd6 | |||
| 5c62b07530 | |||
| 1e20b0b6cc | |||
| e989cc3edd | |||
| f81bebe8d0 | |||
| d1331a6612 | |||
| 6e3b7e692c | |||
| dfbb92b7ab | |||
| 12b57a06a9 | |||
| e6610d7057 | |||
| 026ea5e1b6 | |||
| 5d19bdbae7 | |||
| 75f35eef0b |
@@ -17,12 +17,14 @@ When losslessness and readability conflict, losslessness wins.
|
||||
|
||||
The other direction is a canonical fixpoint, not byte-identity: human markdown normalizes, the way
|
||||
back yields the library's canonical spelling, and that spelling round-trips byte-identically —
|
||||
where there is a way back. CommonMark spells link destinations the flavour has no escape for, so a
|
||||
parse succeeding does not imply a spellable document; `todo.md` 3k's exception list names those.
|
||||
where there is a way back. CommonMark spells some things the flavour has no escape for — a link
|
||||
destination or title holding a backslash or newline, a paragraph opening with a code span whose
|
||||
backticks read back as a fence — so a parse succeeding does not imply a spellable document;
|
||||
`corpus/commonmark-spec/exceptions.json` names those.
|
||||
|
||||
"Equals" is structural equality over editor-normal ADF — adjacent text nodes with identical marks
|
||||
merged, JSON number semantics, an empty attrs object, marks array or content array the absent
|
||||
key — the only domain markdown can restore.
|
||||
and no attributes merged, JSON number semantics, an empty attrs object, marks array or content
|
||||
array the absent key — the only domain markdown can restore.
|
||||
|
||||
Round-trip equality is a property tested over a corpus, not a claim made in prose.
|
||||
|
||||
@@ -41,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.
|
||||
@@ -57,8 +62,16 @@ Round-trip equality is a property tested over a corpus, not a claim made in pros
|
||||
why ~20 lines of own code cannot do the job, who maintains it, and what auditing it costs. So the
|
||||
CommonMark and HTML parsers are written in this repo. A table a standard fixes is data rather than
|
||||
a dependency: HTML5's 2125 semicolon-terminated character references ship packed in their own
|
||||
module, so entity decoding is complete without one. `devDependencies`: few, each earning its keep;
|
||||
they never reach a consumer.
|
||||
module, so entity decoding is complete without one. The CommonMark spec suite is the same shape of
|
||||
data and ships vendored at `corpus/commonmark-spec/` rather than as the `commonmark-spec` dev
|
||||
dependency — that package is CommonJS-only, and Renovate auto-bumping a spec version would silently
|
||||
point the vendored exception list's example numbers at a renumbered suite. A spec bump is a
|
||||
deliberate re-pin, exceptions re-derived by hand beside it. Atlassian's ADF JSON Schemas ship
|
||||
vendored the same way, at `spec/adf-schema/`, rather than as the `@atlaskit/adf-schema` dev
|
||||
dependency — CommonJS-only, some fifty packages with React among them, and a release most days for
|
||||
Renovate to automerge — re-pinned by hand when a payload or a report shows the need.
|
||||
`devDependencies`: few, each earning its keep; they never reach a consumer. `fast-check` earns its
|
||||
place shrinking a failing generated document to the nodes that break it.
|
||||
|
||||
## 6. The package contract
|
||||
|
||||
@@ -88,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
|
||||
@@ -106,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
|
||||
@@ -115,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
|
||||
@@ -198,8 +213,10 @@ resolver maps them, under `NodeNext` alone; a `.d.ts` reader that is not `tsc` s
|
||||
`node-floor.js` round-trips the installed package under a Node pinned to `engines.node`'s floor.
|
||||
|
||||
A fourth engine reads the build rather than the source: a headless Firefox loads `dist/index.js`
|
||||
over HTTP and converts the whole corpus, which is §6's browser half and the only SpiderMonkey
|
||||
there is — the gate's other three engines are two V8s and a JavaScriptCore that is not Safari's.
|
||||
over HTTP and converts the round-trip, normalization and error fixtures and the real payloads —
|
||||
the `commonmark-spec` sort is the Node suite's to check — which is §6's browser half and the only
|
||||
SpiderMonkey there is — the gate's other three engines are two V8s and a JavaScriptCore that is
|
||||
not Safari's.
|
||||
A WebDriver session is what carries a verdict back out, the driver and the page's server sharing
|
||||
one network namespace so each is the other's `127.0.0.1`; `--headless --screenshot` has no such
|
||||
channel, and loading `dist/index.js` in a globals-stripped realm buys one by not running a browser.
|
||||
@@ -212,9 +229,13 @@ functions, and a branch floor that only ever moves upward. It sits below 100 bec
|
||||
`noUncheckedIndexedAccess` and ADF's optional keys force — `?? []`, `?? {}`, `?.`, an index
|
||||
compared against `undefined` — have a half no valid document reaches.
|
||||
|
||||
The corpus, all checked in: hand-built fixtures per node and combination; real sanitized ADF from
|
||||
live Atlassian APIs; property-generated ADF trees; the CommonMark spec suite against
|
||||
`markdownToAdf` and `markdownToHtml`.
|
||||
The corpus, all checked in: hand-built fixtures per node and combination; real ADF Atlassian's
|
||||
editor wrote; the CommonMark spec suite against `markdownToAdf` and `markdownToHtml`.
|
||||
|
||||
Beside the corpus, properties run over documents generated from the node tables and over generated
|
||||
markdown, on a fixed seed in the gate; `PROPERTY_RUNS=<runs>` raises the runs and randomizes the
|
||||
seed for local digging, and a counterexample found becomes a round-trip fixture. The generators and
|
||||
run parameters properties share live in `src/property-harness.ts`, outside the build and coverage.
|
||||
|
||||
`spec/flavour.md` is read as a source too, so the node tables cannot drift from the prose they
|
||||
copy: each `- ` bullet in `## Block nodes`, `## Inline nodes` and `## Marks` declares the nodes
|
||||
@@ -224,6 +245,12 @@ of a bullet; fenced examples are skipped. It guards the attributes alone: nodes
|
||||
content model share a bullet, and the argument attribute is spelled ahead of `Attributes: `, so
|
||||
both answer to the round-trip corpus and to nothing else where a node has no fixture.
|
||||
|
||||
The tables answer to Atlassian's schema too (§5): for every node and mark they spell, the attribute
|
||||
names and kinds equal what `full.json` and `stage-0.json` hold between them. Value sets stay
|
||||
documentation, since any value round-trips. What the schema holds and the tables do not spell is
|
||||
pinned by name — an attribute as a gap, a type as carried — so a re-pin adding either goes red until
|
||||
someone spells it or pins it.
|
||||
|
||||
## 11. Code rules
|
||||
|
||||
- Two-space indent, strict TypeScript, English everywhere. Alphabetical order wherever order
|
||||
@@ -316,17 +343,16 @@ worth deliberating; what matters is that nothing is left undone in the end. Per
|
||||
gate result (commit and outcome); a reviewer does not re-run `ci.sh` or the tests when a
|
||||
result exists for the commit under review, or when the diff since that result cannot affect
|
||||
it (docs-only) — re-run only what its own findings or fixes invalidate.
|
||||
3. Merge the PR (standing authorization, this repo only, granted through the `0.1.0` release —
|
||||
PR #3), check the box in `todo.md` and move the item's text to `todo-history.md`, leaving its
|
||||
title behind, report, stop. The next chunk gets a fresh session.
|
||||
3. Merge the PR (standing authorization, this repo only, granted through the `0.2.0` release —
|
||||
the maintainer, 2026-09-13), check the box in `todo.md` and move the item's text to
|
||||
`todo-history.md`, leaving its title behind, report, stop. The next chunk gets a fresh session.
|
||||
|
||||
Ask, don't guess: any choice where what the maintainer would pick is not near-certain gets asked,
|
||||
and the answer lands as a decision in this file. The confidence bar is very high — asking too
|
||||
often is the accepted cost, guessing wrong is not.
|
||||
|
||||
Reserved for the maintainer, never the agent: changing `version` in `package.json` (a bump on
|
||||
`main` publishes, §9 — every release including `0.1.0` is the maintainer's), making the repo
|
||||
public, and creating the `NPM_TOKEN` secret.
|
||||
`main` publishes, §9 — every release is the maintainer's) and the `NPM_TOKEN` secret.
|
||||
|
||||
A continuous loop session (`/loop`) counts as a chain of sessions: one chunk per iteration, each
|
||||
iteration starting by re-reading `AGENTS.md` and `todo.md` and trusting them over anything
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
Lossless conversion between **Atlassian Document Format** (ADF), an extended markdown flavour, and
|
||||
an HTML dialect.
|
||||
|
||||
**Status: pre-release — the markdown round-trip (`adfToMarkdown`, `markdownToAdf`); HTML not
|
||||
yet.**
|
||||
**Status: published — the markdown round-trip (`adfToMarkdown`, `markdownToAdf`); HTML at
|
||||
`0.3.0`.**
|
||||
Plan: `todo.md`. Decisions: `AGENTS.md`. The flavour's grammar:
|
||||
[`spec/flavour.md`](spec/flavour.md).
|
||||
|
||||
@@ -115,6 +115,11 @@ emit refuses:
|
||||
library's canonical spelling, which round-trips byte-identically — where it converts back at
|
||||
all: a parse succeeding is no promise of that, so keep the source until the way back succeeds.
|
||||
`[a](/a\b)`, `<http://x?a=1&b=2>` and `[a](/x y)` read cleanly and then refuse.
|
||||
- Three CommonMark spellings parse without an error and build a document the reference renders
|
||||
differently: `[](/url)` and `[]()` stay literal text against CommonMark's empty link, a list
|
||||
continuing past a marker change stays one list against CommonMark's two, and a shortcut
|
||||
reference matching its definition only under Unicode case folding stays unresolved. Each is
|
||||
pinned `pending` in `corpus/commonmark-spec/exceptions.json`.
|
||||
- Raw HTML in markdown input is an error result, never a silent drop — a tag, a comment and a
|
||||
processing instruction alike. ADF holds no raw-HTML node; the element mapping ships at `0.3.0`.
|
||||
- Not every document converts back: `adfToMarkdown` is partial on valid ADF — a text node holding
|
||||
|
||||
@@ -3,6 +3,11 @@ try {
|
||||
window.convertCorpus = (corpus) => ({
|
||||
errors: corpus.errors.map(({ markdown, name }) => ({ name, parsed: markdownToAdf(markdown) })),
|
||||
normalization: corpus.normalization.map(({ markdown, name }) => ({ name, parsed: markdownToAdf(markdown) })),
|
||||
realPayloads: corpus.realPayloads.map(({ json, name }) => {
|
||||
const adf = JSON.parse(json)
|
||||
const emitted = adfToMarkdown(adf)
|
||||
return { emitted, isDocument: isAdfDocument(adf), name, parsed: emitted.ok ? markdownToAdf(emitted.value) : undefined }
|
||||
}),
|
||||
roundTrip: corpus.roundTrip.map(({ json, markdown, name }) => {
|
||||
const adf = JSON.parse(json)
|
||||
return { emitted: adfToMarkdown(adf), isDocument: isAdfDocument(adf), name, parsed: markdownToAdf(markdown) }
|
||||
|
||||
+17
-4
@@ -2,6 +2,7 @@ import assert from 'node:assert/strict'
|
||||
import { readFileSync, readdirSync } from 'node:fs'
|
||||
import { createServer } from 'node:http'
|
||||
import { extname, join } from 'node:path'
|
||||
import { toEditorNormal } from '../dist/adf/editor-normal.js'
|
||||
|
||||
const contentTypes = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript' }
|
||||
const driver = 'http://127.0.0.1:4444'
|
||||
@@ -66,14 +67,16 @@ async function whenDriverAnswers() {
|
||||
const corpus = {
|
||||
errors: fixtureNames('errors', '.md').map((name) => ({ markdown: fixture(name, '.md'), name })),
|
||||
normalization: fixtureNames('normalization', '.md').map((name) => ({ markdown: fixture(name, '.md'), name })),
|
||||
realPayloads: fixtureNames('real-payloads', '.json').map((name) => ({ json: fixture(name, '.json'), name })),
|
||||
roundTrip: fixtureNames('round-trip', '.json').map((name) => ({ json: fixture(name, '.json'), markdown: fixture(name, '.md'), name })),
|
||||
}
|
||||
assert.deepEqual(
|
||||
readdirSync(corpusRoot, { withFileTypes: true })
|
||||
.filter((entry) => entry.isDirectory())
|
||||
.map((entry) => entry.name)
|
||||
.filter((name) => name !== 'commonmark-spec')
|
||||
.sort(),
|
||||
['errors', 'normalization', 'round-trip'],
|
||||
['errors', 'normalization', 'real-payloads', 'round-trip'],
|
||||
'a corpus kind the browser leg does not convert',
|
||||
)
|
||||
for (const [kind, fixtures] of Object.entries(corpus)) assert.ok(fixtures.length > 0, `the browser leg found no ${kind} fixture to convert`)
|
||||
@@ -104,7 +107,7 @@ for (const [index, { json, markdown, name }] of corpus.roundTrip.entries()) {
|
||||
assert.ok(result.emitted.ok, `it did not emit — ${refusal(result.emitted)}`)
|
||||
assert.equal(result.emitted.value, markdown)
|
||||
assert.ok(result.parsed.ok, `it did not parse — ${refusal(result.parsed)}`)
|
||||
assert.deepEqual(result.parsed.value, JSON.parse(json))
|
||||
assert.deepEqual(toEditorNormal(result.parsed.value), JSON.parse(json))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -112,7 +115,17 @@ for (const [index, { name }] of corpus.normalization.entries()) {
|
||||
const result = results.normalization[index]
|
||||
checking(name, result, () => {
|
||||
assert.ok(result.parsed.ok, `it did not parse — ${refusal(result.parsed)}`)
|
||||
assert.deepEqual(result.parsed.value, JSON.parse(fixture(name, '.json')))
|
||||
assert.deepEqual(toEditorNormal(result.parsed.value), JSON.parse(fixture(name, '.json')))
|
||||
})
|
||||
}
|
||||
|
||||
for (const [index, { json, name }] of corpus.realPayloads.entries()) {
|
||||
const result = results.realPayloads[index]
|
||||
checking(name, result, () => {
|
||||
assert.ok(result.isDocument, `${name}.json is no ADF document`)
|
||||
assert.ok(result.emitted.ok, `it did not emit — ${refusal(result.emitted)}`)
|
||||
assert.ok(result.parsed.ok, `it did not parse back — ${refusal(result.parsed)}`)
|
||||
assert.deepEqual(toEditorNormal(result.parsed.value), JSON.parse(json))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -125,5 +138,5 @@ for (const [index, { name }] of corpus.errors.entries()) {
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Firefox ${session.capabilities.browserVersion} converted ${corpus.roundTrip.length} round-trip, ${corpus.normalization.length} normalization and ${corpus.errors.length} error fixtures`,
|
||||
`Firefox ${session.capabilities.browserVersion} converted ${corpus.roundTrip.length} round-trip, ${corpus.normalization.length} normalization and ${corpus.errors.length} error fixtures and ${corpus.realPayloads.length} real payloads`,
|
||||
)
|
||||
|
||||
@@ -16,7 +16,7 @@ if printf '%s' "$test_output" | grep -q 'ℹ tests 0'; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
in_image "$deno_image" deno test --allow-read --no-check src/
|
||||
in_image "$deno_image" deno test --allow-env=PROPERTY_RUNS --allow-read --no-check src/
|
||||
in_image "$bun_image" bun test src/
|
||||
|
||||
in_image "$node_image" npm run build
|
||||
|
||||
+13
-3
@@ -10,7 +10,17 @@ One directory per contract kind, each landing with its milestone:
|
||||
markdown is not canonical.
|
||||
- `errors/` — `<name>.md`: markdown input that must not convert. A `<name>.error` beside it
|
||||
pins which error.
|
||||
- `real-payloads/` — `<name>.json`: sanitized live ADF, round-tripped ADF→markdown→ADF. No
|
||||
expected markdown.
|
||||
- `real-payloads/` — `<name>.json`: ADF Atlassian's editor wrote, round-tripped ADF→markdown→ADF.
|
||||
No expected markdown.
|
||||
- `commonmark-spec/` — the CommonMark suite run against `markdownToAdf` by three checks. `spec.json`
|
||||
is the suite; `refusals.json` pins each refusing example to its error `code`; `exceptions.json`
|
||||
pins each known divergence by `check`, `example`, `kind` and the exact `divergence`, with a
|
||||
`reason`. `kind` is `mark-model` (the permanent count divergence from ADF's mark-per-text-node
|
||||
model), `unspellable` (parses but the flavour has no spelling) or `pending` (a parser gap a later
|
||||
milestone may close).
|
||||
|
||||
JSON is editor-normal (AGENTS.md §2), two-space indent, keys sorted.
|
||||
JSON is editor-normal (AGENTS.md §2), two-space indent, keys sorted. `spec.json` is the vendored,
|
||||
upstream machine-readable suite, byte-exact from
|
||||
[spec.commonmark.org](https://spec.commonmark.org/0.31.2/spec.json) (CommonMark 0.31.2, © John
|
||||
MacFarlane, [CC-BY-SA-4.0](https://creativecommons.org/licenses/by-sa/4.0/)), and is not
|
||||
re-serialized by the corpus gate.
|
||||
|
||||
@@ -0,0 +1,436 @@
|
||||
[
|
||||
{
|
||||
"check": "fixpoint",
|
||||
"divergence": "unspellable-link",
|
||||
"example": 196,
|
||||
"kind": "unspellable",
|
||||
"reason": "The link title holds literal newlines no escape spells."
|
||||
},
|
||||
{
|
||||
"check": "fixpoint",
|
||||
"divergence": "unspellable-link",
|
||||
"example": 202,
|
||||
"kind": "unspellable",
|
||||
"reason": "The link destination holds a backslash the flavour cannot escape."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "ul 2/1",
|
||||
"example": 301,
|
||||
"kind": "pending",
|
||||
"reason": "A list continuing past a marker change renders as two lists, the parser opens one."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "ol 2/1",
|
||||
"example": 302,
|
||||
"kind": "pending",
|
||||
"reason": "A list continuing past a marker change renders as two lists, the parser opens one."
|
||||
},
|
||||
{
|
||||
"check": "fixpoint",
|
||||
"divergence": "unspellable-line-start",
|
||||
"example": 330,
|
||||
"kind": "unspellable",
|
||||
"reason": "A paragraph opens with a code span whose backticks read back as a fence."
|
||||
},
|
||||
{
|
||||
"check": "fixpoint",
|
||||
"divergence": "unspellable-line-start",
|
||||
"example": 331,
|
||||
"kind": "unspellable",
|
||||
"reason": "A paragraph opens with a code span whose backticks read back as a fence."
|
||||
},
|
||||
{
|
||||
"check": "fixpoint",
|
||||
"divergence": "unspellable-line-start",
|
||||
"example": 340,
|
||||
"kind": "unspellable",
|
||||
"reason": "A paragraph opens with a code span whose backticks read back as a fence."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "em 2/1",
|
||||
"example": 369,
|
||||
"kind": "mark-model",
|
||||
"reason": "CommonMark nests same-kind elements; the single mark collapses them to one."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "em 2/1",
|
||||
"example": 373,
|
||||
"kind": "mark-model",
|
||||
"reason": "CommonMark nests same-kind elements; the single mark collapses them to one."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "strong 2/1",
|
||||
"example": 389,
|
||||
"kind": "mark-model",
|
||||
"reason": "CommonMark nests same-kind elements; the single mark collapses them to one."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "em 1/3",
|
||||
"example": 393,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "strong 1/5",
|
||||
"example": 394,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "strong 1/3",
|
||||
"example": 395,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "em 1/3",
|
||||
"example": 399,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "em 1/2",
|
||||
"example": 404,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "em 1/3",
|
||||
"example": 406,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "em 2/1",
|
||||
"example": 407,
|
||||
"kind": "mark-model",
|
||||
"reason": "CommonMark nests same-kind elements; the single mark collapses them to one."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "em 2/1",
|
||||
"example": 408,
|
||||
"kind": "mark-model",
|
||||
"reason": "CommonMark nests same-kind elements; the single mark collapses them to one."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "em 2/1",
|
||||
"example": 409,
|
||||
"kind": "mark-model",
|
||||
"reason": "CommonMark nests same-kind elements; the single mark collapses them to one."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "em 1/3",
|
||||
"example": 410,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "em 1/3",
|
||||
"example": 411,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "em 1/2",
|
||||
"example": 413,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "em 1/2",
|
||||
"example": 414,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "em 1/2",
|
||||
"example": 415,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "strong 3/1",
|
||||
"example": 417,
|
||||
"kind": "mark-model",
|
||||
"reason": "CommonMark nests same-kind elements; the single mark collapses them to one."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "em 2/5 strong 1/3",
|
||||
"example": 418,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "strong 1/2",
|
||||
"example": 422,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "strong 1/3",
|
||||
"example": 424,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "strong 2/1",
|
||||
"example": 425,
|
||||
"kind": "mark-model",
|
||||
"reason": "CommonMark nests same-kind elements; the single mark collapses them to one."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "strong 2/1",
|
||||
"example": 426,
|
||||
"kind": "mark-model",
|
||||
"reason": "CommonMark nests same-kind elements; the single mark collapses them to one."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "strong 2/1",
|
||||
"example": 427,
|
||||
"kind": "mark-model",
|
||||
"reason": "CommonMark nests same-kind elements; the single mark collapses them to one."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "strong 1/3",
|
||||
"example": 428,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "strong 1/3",
|
||||
"example": 429,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "strong 1/2",
|
||||
"example": 430,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "strong 1/2",
|
||||
"example": 431,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "em 1/3 strong 2/5",
|
||||
"example": 432,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "strong 1/2",
|
||||
"example": 433,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "em 2/1",
|
||||
"example": 461,
|
||||
"kind": "mark-model",
|
||||
"reason": "CommonMark nests same-kind elements; the single mark collapses them to one."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "em 2/1",
|
||||
"example": 463,
|
||||
"kind": "mark-model",
|
||||
"reason": "CommonMark nests same-kind elements; the single mark collapses them to one."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "strong 2/1",
|
||||
"example": 464,
|
||||
"kind": "mark-model",
|
||||
"reason": "CommonMark nests same-kind elements; the single mark collapses them to one."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "strong 2/1",
|
||||
"example": 465,
|
||||
"kind": "mark-model",
|
||||
"reason": "CommonMark nests same-kind elements; the single mark collapses them to one."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "strong 3/1",
|
||||
"example": 466,
|
||||
"kind": "mark-model",
|
||||
"reason": "CommonMark nests same-kind elements; the single mark collapses them to one."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "strong 2/1",
|
||||
"example": 468,
|
||||
"kind": "mark-model",
|
||||
"reason": "CommonMark nests same-kind elements; the single mark collapses them to one."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "em 1/3",
|
||||
"example": 470,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "em 1/2",
|
||||
"example": 478,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "em 1/2",
|
||||
"example": 479,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "a 1/0",
|
||||
"example": 484,
|
||||
"kind": "pending",
|
||||
"reason": "An empty link text stays literal text; CommonMark renders an empty link."
|
||||
},
|
||||
{
|
||||
"check": "text",
|
||||
"divergence": "\"\" against \"[](./target.md)\"",
|
||||
"example": 484,
|
||||
"kind": "pending",
|
||||
"reason": "An empty link text stays literal text; CommonMark renders an empty link."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "a 1/0",
|
||||
"example": 487,
|
||||
"kind": "pending",
|
||||
"reason": "An empty link text stays literal text; CommonMark renders an empty link."
|
||||
},
|
||||
{
|
||||
"check": "text",
|
||||
"divergence": "\"\" against \"[]()\"",
|
||||
"example": 487,
|
||||
"kind": "pending",
|
||||
"reason": "An empty link text stays literal text; CommonMark renders an empty link."
|
||||
},
|
||||
{
|
||||
"check": "fixpoint",
|
||||
"divergence": "unspellable-link",
|
||||
"example": 502,
|
||||
"kind": "unspellable",
|
||||
"reason": "The link destination holds a backslash the flavour cannot escape."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "a 1/5 em 1/4",
|
||||
"example": 516,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "em 1/3",
|
||||
"example": 519,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "a 1/5 em 1/4",
|
||||
"example": 530,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "em 1/2",
|
||||
"example": 533,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "a 1/0",
|
||||
"example": 540,
|
||||
"kind": "pending",
|
||||
"reason": "The case-folding shortcut reference is unresolved; CommonMark folds case and links."
|
||||
},
|
||||
{
|
||||
"check": "text",
|
||||
"divergence": "\"ẞ\" against \"[ẞ]\"",
|
||||
"example": 540,
|
||||
"kind": "pending",
|
||||
"reason": "A case-folding shortcut reference is unresolved; CommonMark folds case and links."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "a 1/2",
|
||||
"example": 554,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "a 1/2",
|
||||
"example": 558,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "a 1/2",
|
||||
"example": 559,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "em 1/2",
|
||||
"example": 638,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
},
|
||||
{
|
||||
"check": "count",
|
||||
"divergence": "em 1/2",
|
||||
"example": 639,
|
||||
"kind": "mark-model",
|
||||
"reason": "The mark spans adjacent text nodes, counted once per node where CommonMark nests one element."
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,346 @@
|
||||
[
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 21
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 31
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 148
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 149
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 150
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 151
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 152
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 153
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 154
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 155
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 156
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 157
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 158
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 159
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 160
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 161
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 162
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 163
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 164
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 165
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 166
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 167
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 168
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 169
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 170
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 171
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 172
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 173
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 174
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 175
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 176
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 177
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 178
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 179
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 180
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 181
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 182
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 183
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 184
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 185
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 186
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 187
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 188
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 189
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 190
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 191
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 201
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 308
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 309
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 344
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 475
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 476
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 477
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 491
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 494
|
||||
},
|
||||
{
|
||||
"code": "unmappable-image",
|
||||
"example": 517
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 524
|
||||
},
|
||||
{
|
||||
"code": "unmappable-image",
|
||||
"example": 531
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 536
|
||||
},
|
||||
{
|
||||
"code": "unmappable-image",
|
||||
"example": 572
|
||||
},
|
||||
{
|
||||
"code": "unmappable-image",
|
||||
"example": 573
|
||||
},
|
||||
{
|
||||
"code": "unmappable-image",
|
||||
"example": 576
|
||||
},
|
||||
{
|
||||
"code": "unmappable-image",
|
||||
"example": 577
|
||||
},
|
||||
{
|
||||
"code": "unmappable-image",
|
||||
"example": 579
|
||||
},
|
||||
{
|
||||
"code": "unmappable-image",
|
||||
"example": 584
|
||||
},
|
||||
{
|
||||
"code": "unmappable-image",
|
||||
"example": 585
|
||||
},
|
||||
{
|
||||
"code": "unmappable-image",
|
||||
"example": 586
|
||||
},
|
||||
{
|
||||
"code": "unmappable-image",
|
||||
"example": 587
|
||||
},
|
||||
{
|
||||
"code": "unmappable-image",
|
||||
"example": 588
|
||||
},
|
||||
{
|
||||
"code": "unmappable-image",
|
||||
"example": 589
|
||||
},
|
||||
{
|
||||
"code": "unmappable-image",
|
||||
"example": 591
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 613
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 614
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 615
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 616
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 617
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 623
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 625
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 626
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 627
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 628
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 629
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 630
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 631
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 642
|
||||
},
|
||||
{
|
||||
"code": "unmappable-html",
|
||||
"example": 643
|
||||
}
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
_a :adf{json="{\"type\":\"blockCard\"}"} b_
|
||||
_a !adf:carry{json="{\"type\":\"blockCard\"}"} b_
|
||||
|
||||
@@ -1 +1 @@
|
||||
:adf{json="{"}
|
||||
!adf:carry{json="{"}
|
||||
|
||||
@@ -1 +1 @@
|
||||
:adf{json="[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"}
|
||||
!adf:carry{json="[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
:::adf
|
||||
:::carry
|
||||
{"type":"rule"}
|
||||
:::
|
||||
@@ -1 +1 @@
|
||||
Part :mention[**@Mikael**]{id=b1c2}.
|
||||
Part !adf:mention[**@Mikael**]{id=b1c2}.
|
||||
|
||||
@@ -1 +1 @@
|
||||
Part :mention[@A and more.
|
||||
Part !adf:mention[@A and more.
|
||||
|
||||
@@ -1 +1 @@
|
||||
Part :em[emphasised] here.
|
||||
Part !adf:em[emphasised] here.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
malformed-directive
|
||||
@@ -0,0 +1 @@
|
||||
Part !adf: here.
|
||||
@@ -0,0 +1 @@
|
||||
unsupported-node-shape
|
||||
@@ -0,0 +1 @@
|
||||
!adf:status[!adf:carry{json="{\"attrs\":{\"localId\":\"x\"},\"text\":\"ok\",\"type\":\"text\"}"}]{color=red}
|
||||
@@ -1 +1 @@
|
||||
Part :text{text=plain} here.
|
||||
Part !adf:text{text=plain} here.
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,173 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"level": 2,
|
||||
"localId": "0d42e7ff24ba"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Code blocks",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "a971506228e7"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Run the setup once, then start the server with ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "code"
|
||||
}
|
||||
],
|
||||
"text": "npm start",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": " and open the page.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"language": "javascript",
|
||||
"localId": "eaf42046-6454-451d-ab13-8bd5ed2333bb",
|
||||
"wrap": true
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "function greet(name) {\n return `Hello, ${name}!`\n}",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "codeBlock"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "d69616dee641"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "The same idea in Python:",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"language": "python",
|
||||
"localId": "2094b559-280a-49d5-9be3-182572e5d2d8",
|
||||
"wrap": true
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "def total(prices):\n return sum(prices)\n\nprint(total([3, 4.5]))",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "codeBlock"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "abe1971d1f18"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "A query that lists visitors by city:",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"language": "sql",
|
||||
"localId": "5551dc8e-8fe0-455e-a9cf-22e48b589fe8",
|
||||
"wrap": true
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "SELECT name, city\nFROM visitors\nWHERE city = 'Lund'\nORDER BY name;",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "codeBlock"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "51efdd2f1f02"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Plain output, no language chosen:",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "d0f83c92-c252-4a70-9e31-dfb5ac255cd1",
|
||||
"wrap": true
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "$ ./setup\nChecking folders... done\n 3 files copied",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "codeBlock"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "3dac57e146fa"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Keep ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "code"
|
||||
}
|
||||
],
|
||||
"text": ".env",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": " files out of version control, and never paste a ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "code"
|
||||
}
|
||||
],
|
||||
"text": "password",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": " into a ticket.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"level": 2,
|
||||
"localId": "0fcbc98aec1f"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Where things stand",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "32b6266eb05c"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Office move ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"color": "blue",
|
||||
"localId": "1ab19511-a0fa-41a1-b6d0-6729813c0526",
|
||||
"style": "mixedCase",
|
||||
"text": "In progress"
|
||||
},
|
||||
"type": "status"
|
||||
},
|
||||
{
|
||||
"text": " since ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "493624162706",
|
||||
"timestamp": "1790812800000"
|
||||
},
|
||||
"type": "date"
|
||||
},
|
||||
{
|
||||
"text": " , owned by ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"accessLevel": "",
|
||||
"id": "712020:78bf5058-bc76-4783-99af-6d70cb593cee",
|
||||
"localId": "604060e1-e533-4019-aa28-21952dfdea19",
|
||||
"text": "@Larvit Developer"
|
||||
},
|
||||
"type": "mention"
|
||||
},
|
||||
{
|
||||
"text": " .",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "73a94328c89f"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Furniture ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"color": "green",
|
||||
"localId": "7b27ff33-1483-452b-8982-311cf9429023",
|
||||
"style": "mixedCase",
|
||||
"text": "Done"
|
||||
},
|
||||
"type": "status"
|
||||
},
|
||||
{
|
||||
"text": " , cabling ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"color": "red",
|
||||
"localId": "b6eb05a9-e0e7-4269-9cbd-e912640364a6",
|
||||
"style": "mixedCase",
|
||||
"text": "Blocked"
|
||||
},
|
||||
"type": "status"
|
||||
},
|
||||
{
|
||||
"text": " , signage ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"color": "yellow",
|
||||
"localId": "ff7ed7fb-1846-48cb-b5ea-8be6b598ccc6",
|
||||
"style": "mixedCase",
|
||||
"text": "Waiting"
|
||||
},
|
||||
"type": "status"
|
||||
},
|
||||
{
|
||||
"text": " , parking ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"color": "purple",
|
||||
"localId": "e9218ff4-6fcb-4e43-b36a-b1bef8a19376",
|
||||
"style": "mixedCase",
|
||||
"text": "Maybe"
|
||||
},
|
||||
"type": "status"
|
||||
},
|
||||
{
|
||||
"text": " and keys ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"color": "neutral",
|
||||
"localId": "f1455340-2469-4bc6-a371-ed6b1f54386c",
|
||||
"style": "mixedCase",
|
||||
"text": "Not started"
|
||||
},
|
||||
"type": "status"
|
||||
},
|
||||
{
|
||||
"text": " ",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "8d9e2dc70265"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"accessLevel": "",
|
||||
"id": "712020:78bf5058-bc76-4783-99af-6d70cb593cee",
|
||||
"localId": "ab5b7997-43ce-4238-b540-db3864e0965c",
|
||||
"text": "@Larvit Developer"
|
||||
},
|
||||
"type": "mention"
|
||||
},
|
||||
{
|
||||
"text": " please confirm the handover on ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "77e2bdefa7fd",
|
||||
"timestamp": "1795132800000"
|
||||
},
|
||||
"type": "date"
|
||||
},
|
||||
{
|
||||
"text": " before lunch.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "f7fb8a7527a3"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Great work so far ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"id": "1f44d",
|
||||
"localId": "8fe9a02e94ce",
|
||||
"shortName": ":thumbsup:",
|
||||
"text": "👍"
|
||||
},
|
||||
"type": "emoji"
|
||||
},
|
||||
{
|
||||
"text": " and thanks for the snacks ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"id": "1f389",
|
||||
"localId": "c19496cc742f",
|
||||
"shortName": ":tada:",
|
||||
"text": "🎉"
|
||||
},
|
||||
"type": "emoji"
|
||||
},
|
||||
{
|
||||
"text": " ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"id": "1f600",
|
||||
"localId": "c05a6757d0ee",
|
||||
"shortName": ":grinning:",
|
||||
"text": "😀"
|
||||
},
|
||||
"type": "emoji"
|
||||
},
|
||||
{
|
||||
"text": " ",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "b75918031ebd"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Deliveries go to:",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"type": "hardBreak"
|
||||
},
|
||||
{
|
||||
"text": "Building C, third floor",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"type": "hardBreak"
|
||||
},
|
||||
{
|
||||
"text": "Ring the bell twice",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,527 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"level": 1,
|
||||
"localId": "cfdcc54363d9"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Project handbook",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "91177a27b0ff"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Welcome to the handbook for the spring project.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"level": 2,
|
||||
"localId": "7e7d9113a89b"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Getting started",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "e65711ca96b6"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Pack the following before the first day:",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "b5c36c57fe79"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "1f4aa02de452"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "8993e22a13aa"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Laptop",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "2b335a4504ab"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "c9180347caf0"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "60990e48da32"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Charger",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "fc5d4c8a875d"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "3b8ec5318463"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Mouse",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
}
|
||||
],
|
||||
"type": "bulletList"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "90f5f37a8a8f"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "90fab5a310c5"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Notebook",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "46840b8bd24b",
|
||||
"order": 1
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "b4f06fbd15ef"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "93db4583bdba"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Lined for notes",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "6904c80dd06d"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "0bc1c819d38d"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Squared for sketches",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
}
|
||||
],
|
||||
"type": "orderedList"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "65bd0afce26e"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "a753e5ecdad3"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Water bottle",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
}
|
||||
],
|
||||
"type": "bulletList"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"level": 3,
|
||||
"localId": "5b6a671499c5"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Unpacking",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "f460abd3a962"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "The first two steps happen at reception, so ours start at three:",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "baf0046a0432",
|
||||
"order": 3
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "b4df865a31a9"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "3f1b32f952d0"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Unpack the boxes",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "f64930d87d4e"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "d953389c865a"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Label the shelves",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "840daad9cb43"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "8708b8c7aeec"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Recycle the cardboard",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
}
|
||||
],
|
||||
"type": "orderedList"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"level": 4,
|
||||
"localId": "27d1d408ae3e"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Shelf labels",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "8b5cf239dbb5",
|
||||
"order": 1
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "0948562abdc3"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "4da9d4f45923"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Top shelf",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "36edf97d8e2c"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "7915e252429c"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "9b15cc9fa8a1"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Binders",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "efe607ab9e41"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "d559a2f29321"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Archive boxes",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
}
|
||||
],
|
||||
"type": "bulletList"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "229692097876"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "928b1862c273"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Bottom shelf",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
}
|
||||
],
|
||||
"type": "orderedList"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"level": 5,
|
||||
"localId": "353a84ef9126"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Quiet hours",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "9c11b949ed48"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "bd576489e8b6"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Good tools make good work, and quiet rooms make good thinking.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "727fda576a9d"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Please keep calls in the booths.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "blockquote"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"level": 6,
|
||||
"localId": "b12277440bc7"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Small print",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "9f6fbb5a19af"
|
||||
},
|
||||
"type": "rule"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "8a6e253c110e"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Office hours:",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"type": "hardBreak"
|
||||
},
|
||||
{
|
||||
"text": "Monday to Thursday, eight to five",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"type": "hardBreak"
|
||||
},
|
||||
{
|
||||
"text": "Friday, eight to three",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "b9c3f5daa373"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "That is all for now.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"level": 2,
|
||||
"localId": "d5c3a2503683"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Media and attachments",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "f71287760586"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "A photo of the meadow behind the office:",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"layout": "align-start",
|
||||
"localId": "95de25bcdecc",
|
||||
"width": 320,
|
||||
"widthType": "pixel"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"alt": "Green meadow under a yellow sun",
|
||||
"collection": "",
|
||||
"height": 180,
|
||||
"id": "8c586460-fb0d-44f6-949f-d800d5dd0f45",
|
||||
"localId": "630d0d0457b8",
|
||||
"type": "file",
|
||||
"width": 320
|
||||
},
|
||||
"marks": [
|
||||
{
|
||||
"attrs": {
|
||||
"color": "#091e4224",
|
||||
"size": 2
|
||||
},
|
||||
"type": "border"
|
||||
}
|
||||
],
|
||||
"type": "media"
|
||||
}
|
||||
],
|
||||
"type": "mediaSingle"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "b4bfee3ad3ec"
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "ffe5710ea995"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "The packing list is attached as a file:",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"collection": "",
|
||||
"id": "8dfd7046-2382-4377-b3ba-ec31e34cd01a",
|
||||
"type": "file"
|
||||
},
|
||||
"type": "media"
|
||||
}
|
||||
],
|
||||
"type": "mediaGroup"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "77fcb609f709"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Both were uploaded from the editor.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"level": 2,
|
||||
"localId": "d483ec27f809"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Panels",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "cb02f8e9812b"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Each colour of panel below holds a short note for the visiting team.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "b6d76c21e77e",
|
||||
"panelType": "info"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "2d871d80f6e1"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "The workshop starts at nine in the morning.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "panel"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "cbaab515445c",
|
||||
"panelType": "note"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "13cafc26c42d"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Bring your own laptop charger.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "panel"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "c5fa8b34e2aa",
|
||||
"panelType": "success"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "e186fca354d9"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "All rooms are booked for the week.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "panel"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "ee223a0f886e",
|
||||
"panelType": "warning"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "e0006fb2b972"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "The east entrance closes early on Fridays.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "ea6a56b9c863"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Use the main door after four.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "panel"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "fedbadc6d1c7",
|
||||
"panelType": "error"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "c0f6122d81b7"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "The projector in room two is out of order.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "panel"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"level": 2,
|
||||
"localId": "afe638de11c5"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Expands",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "e635bf2eac43",
|
||||
"title": "Agenda for the first day"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "c8beb4c392ee"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "6d6ff472d878"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "d1186101c5a1"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Welcome and introductions",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "01d203b97343"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "61bfec91218d"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Tour of the building",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "004e47604c2a"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "726aa40e1e38"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Lunch in the courtyard",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
}
|
||||
],
|
||||
"type": "bulletList"
|
||||
}
|
||||
],
|
||||
"type": "expand"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "cb6e48b9d746"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Questions go to the organisers before the event.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "32a8832682b2",
|
||||
"title": "Travel details"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "00b05814cccb"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Trains leave the central station every twenty minutes.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "eec0df0ed4f1",
|
||||
"panelType": "note"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "712c49fa7ecc"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Tickets are cheaper before seven.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "panel"
|
||||
}
|
||||
],
|
||||
"type": "expand"
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,630 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "3b911072ce78"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Summary of the planning session on Thursday. Attendees: ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"accessLevel": "",
|
||||
"id": "712020:78bf5058-bc76-4783-99af-6d70cb593cee",
|
||||
"localId": "c8b64b9f-d396-4525-9456-38cf690ade1e",
|
||||
"text": "@Larvit Developer"
|
||||
},
|
||||
"type": "mention"
|
||||
},
|
||||
{
|
||||
"text": " and two guests from the design team.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"level": 2,
|
||||
"localId": "3e97ef36cbc3"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Where we are",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "530e0e3aa621"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "The beta is ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"color": "green",
|
||||
"localId": "ac0975eb-fafe-44ad-8293-8a12fdbcaebc",
|
||||
"style": "mixedCase",
|
||||
"text": "On track"
|
||||
},
|
||||
"type": "status"
|
||||
},
|
||||
{
|
||||
"text": " and the public launch is set for ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "84d9a1cde860",
|
||||
"timestamp": "1793664000000"
|
||||
},
|
||||
"type": "date"
|
||||
},
|
||||
{
|
||||
"text": " .",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "ffcd53578640",
|
||||
"panelType": "warning"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "0f0e8b9e13bb"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Translation files are still ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "strong"
|
||||
}
|
||||
],
|
||||
"text": "missing",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": " for two languages.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "panel"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"level": 2,
|
||||
"localId": "d9b590d479ad"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Decisions",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "69dc2d52-34f7-43bb-8d6c-36d52a47179e"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "852a35a7-f660-4161-85dd-2d54adaf662b",
|
||||
"state": "DECIDED"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Ship the beta to internal users first",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "decisionItem"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "2df16457-331f-4d26-90ba-539f82949d70",
|
||||
"state": "DECIDED"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Keep the old export for one more release",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "decisionItem"
|
||||
}
|
||||
],
|
||||
"type": "decisionList"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"level": 2,
|
||||
"localId": "7813370b5e89"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Next steps",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "21a7a33c-199d-4396-a148-c8404f70554f"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "5343dfdb-e70d-4ce6-bca5-8a382123c7bd",
|
||||
"state": "TODO"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Draft the release notes",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "taskItem"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "67d65eb8-17e9-4cd6-93cb-7d495f84e01d",
|
||||
"state": "TODO"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Book the demo room",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "taskItem"
|
||||
}
|
||||
],
|
||||
"type": "taskList"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"level": 2,
|
||||
"localId": "e5806b037d61"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Numbers",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"isNumberColumnEnabled": false,
|
||||
"layout": "align-start",
|
||||
"localId": "cbae6bea-3c35-42b9-9a8f-3b37749e1a87"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "e4d343447bf5"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "4fcec7ad0a0f"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "974849d6e63c"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "strong"
|
||||
}
|
||||
],
|
||||
"text": "Metric",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableHeader"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "42df5203769e"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "8b0da0854a0a"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "strong"
|
||||
}
|
||||
],
|
||||
"text": "Beta",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableHeader"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "cbdae6c8508d"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "2584f222d6c3"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "strong"
|
||||
}
|
||||
],
|
||||
"text": "Goal",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableHeader"
|
||||
}
|
||||
],
|
||||
"type": "tableRow"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "65e4d7a700c0"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "a37175350eea"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "7efe32972497"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Sign-ups",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableCell"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "e0a95cdbd3c5"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "4628a3afe71b"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "140",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableCell"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "b802fc652961"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "94c6ec93acb2"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "200",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableCell"
|
||||
}
|
||||
],
|
||||
"type": "tableRow"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "bd6465cc546d"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "b6dcef3c6ac5"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "ba5c99353d30"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Crash reports",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableCell"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "554b7b5a8e71"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "569a134488bc"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "3",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableCell"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "fb03db2017b1"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "c0ce0ada482c"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "0",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableCell"
|
||||
}
|
||||
],
|
||||
"type": "tableRow"
|
||||
}
|
||||
],
|
||||
"type": "table"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "b4eb1362eb9f"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "The flag that turns the beta on:",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"language": "yaml",
|
||||
"localId": "14298a36-6632-458f-a548-7fccd4b7186b",
|
||||
"wrap": true
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "beta:\n enabled: true\n audience: internal",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "codeBlock"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "a98274e22eaa"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Risks we talked about:",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "fb3e387688ff"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "fdeee4ce0678"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "5ff2e05f1c7e"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Late translations",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "043a7ded718e"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "73a4d8e4ef0d"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "The holiday week",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "3e32c1b868a1"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "98969cd5548e"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "3b9989413325"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Half the team is away",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
}
|
||||
],
|
||||
"type": "bulletList"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
}
|
||||
],
|
||||
"type": "bulletList"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "ba7d8422f4c6"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "a6837cfad3f5"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Small steps, shipped often.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "blockquote"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "6ec9ef8df224"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Thanks, everyone ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"id": "1f389",
|
||||
"localId": "2481c60576e0",
|
||||
"shortName": ":tada:",
|
||||
"text": "🎉"
|
||||
},
|
||||
"type": "emoji"
|
||||
},
|
||||
{
|
||||
"text": " ",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "782129bf30c1",
|
||||
"title": "Raw notes"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "e9614e389875"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "We spent twenty minutes on the colour of the launch banner.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "expand"
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "3e9517d02129"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Looks good to me. Two small things:",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "5738867bf15d",
|
||||
"order": 1
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "ccd8d283e36d"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "638111edfab4"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Rename ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "code"
|
||||
}
|
||||
],
|
||||
"text": "beta.enabled",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": " before the launch",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "0adfb25bb321"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "980750fad0fe"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Move the demo to the ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "em"
|
||||
}
|
||||
],
|
||||
"text": "large",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": " room",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
}
|
||||
],
|
||||
"type": "orderedList"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "40e080c7acd4"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Thanks ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"accessLevel": "",
|
||||
"id": "712020:78bf5058-bc76-4783-99af-6d70cb593cee",
|
||||
"localId": "c4e073cd-8b3a-48b2-93a0-ade57b4ec2cf",
|
||||
"text": "@Larvit Developer"
|
||||
},
|
||||
"type": "mention"
|
||||
},
|
||||
{
|
||||
"text": " !",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"level": 2,
|
||||
"localId": "e632994a13ae"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Smart links",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "dcfa975dde87"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "An inline card sits in a sentence, like ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "042b9a548674",
|
||||
"url": "https://en.wikipedia.org/wiki/Markdown"
|
||||
},
|
||||
"type": "inlineCard"
|
||||
},
|
||||
{
|
||||
"text": " which keeps the text flowing.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "c138d931a131"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "A block card stands on its own line:",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "9e527ae5f2bd"
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "88bd10974ec6",
|
||||
"url": "https://spec.commonmark.org/"
|
||||
},
|
||||
"type": "blockCard"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "9879bb1368ea"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": " An embed shows the page itself, where the site allows it:",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "ccbed18139bb"
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"layout": "wide",
|
||||
"localId": "8320cf243168",
|
||||
"originalHeight": 480,
|
||||
"originalWidth": 853.344,
|
||||
"url": "https://www.youtube.com/watch?v=jNQXAC9IVRw",
|
||||
"width": 100
|
||||
},
|
||||
"type": "embedCard"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "22f60b236c1c"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": " And a plain address stays a link: ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"attrs": {
|
||||
"href": "https://www.markdownguide.org/"
|
||||
},
|
||||
"type": "link"
|
||||
}
|
||||
],
|
||||
"text": "https://www.markdownguide.org/",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": " for reference.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"level": 2,
|
||||
"localId": "3265660b5792"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Shared checklist",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "c7d94ae59a47"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "The block below is synced, so an edit shows up wherever it is used.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "72651b28-757c-44a1-be0b-586c3e6df546",
|
||||
"resourceId": "03a667ab-4c6b-402d-adae-0c55582e32eb"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "0eb44ec3924b"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Before every release:",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "4beeefa0c6fa"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "07d19212447f"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "2fd9fc0e5d71"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Run the tests",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "7f5f50f7642c"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "14d3435ce5f4"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Update the changelog",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
}
|
||||
],
|
||||
"type": "bulletList"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "e405ebea5e00"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Ask for a second pair of eyes.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "bodiedSyncBlock"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "73b5f0a1ef05"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Anything specific to this release goes below the synced block.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,620 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"level": 2,
|
||||
"localId": "a0c750451c14"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "A simple table",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "944306a6da33"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Rooms for the week:",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"isNumberColumnEnabled": false,
|
||||
"layout": "align-start",
|
||||
"localId": "92b28760-e51b-436d-82f0-45c6b6bf5b60"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "d53d5ad740cb"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "a8c82e01543e"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "00c1bde1a44d"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "strong"
|
||||
}
|
||||
],
|
||||
"text": "Room",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableHeader"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "c736c185bcd0"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "3dc846d8aaf9"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "strong"
|
||||
}
|
||||
],
|
||||
"text": "Capacity",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableHeader"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "b484834a890d"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "5b0671aadeed"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "strong"
|
||||
}
|
||||
],
|
||||
"text": "Notes",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableHeader"
|
||||
}
|
||||
],
|
||||
"type": "tableRow"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "45395ea36f04"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "8b19b08b6a93"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "363cf87d44be"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "North",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableCell"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "e189034484f3"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "42a5bad514ea"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "12",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableCell"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "9cfc78f9a553"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "2ae747c00211"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Projector",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableCell"
|
||||
}
|
||||
],
|
||||
"type": "tableRow"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "8bc7abba3229"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "0e7980a31697"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "a001411b475e"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "South",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableCell"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "e61493f2b3d9"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "79c522faf7e8"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "8",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableCell"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "db911772081a"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "6721e0a4f29f"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Quiet corner",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableCell"
|
||||
}
|
||||
],
|
||||
"type": "tableRow"
|
||||
}
|
||||
],
|
||||
"type": "table"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"level": 2,
|
||||
"localId": "c21221cbaeda"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "A richer table",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "b9cdc4e693ca"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Budget by season, with merged, coloured and resized cells:",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"isNumberColumnEnabled": false,
|
||||
"layout": "align-start",
|
||||
"localId": "ac1b1f4b-3d7a-4cd2-a0a7-05ece633ba69"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "c411338ed251"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"background": "#b3d4ff",
|
||||
"colwidth": [
|
||||
135
|
||||
],
|
||||
"localId": "798f1dea331f"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "b605b3c1f598"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "strong"
|
||||
}
|
||||
],
|
||||
"text": "Item",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableHeader"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"colwidth": [
|
||||
233
|
||||
],
|
||||
"localId": "db4768c076d4"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "b4890b2d833e"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "strong"
|
||||
}
|
||||
],
|
||||
"text": "Spring",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableHeader"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"colwidth": [
|
||||
184
|
||||
],
|
||||
"localId": "4dde2fbfb921"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "7986b41ed947"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "strong"
|
||||
}
|
||||
],
|
||||
"text": "Autumn",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableHeader"
|
||||
}
|
||||
],
|
||||
"type": "tableRow"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "b4ac97bec992"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"colwidth": [
|
||||
135
|
||||
],
|
||||
"localId": "ba92b57558d8"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "f23f030e9233"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Venue",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableHeader"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"background": "#abf5d1",
|
||||
"colspan": 2,
|
||||
"colwidth": [
|
||||
233,
|
||||
184
|
||||
],
|
||||
"localId": "51af31d44baa"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "ba55f45dc035"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Booked for both",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableCell"
|
||||
}
|
||||
],
|
||||
"type": "tableRow"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "e911af70b656"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"colwidth": [
|
||||
135
|
||||
],
|
||||
"localId": "50a820456456"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "b04d0749aee7"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Catering",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableHeader"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"colwidth": [
|
||||
233
|
||||
],
|
||||
"localId": "d0cc276de239"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "90211ae7a504"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "400",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableCell"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"colwidth": [
|
||||
184
|
||||
],
|
||||
"localId": "1962d05e8763"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "221cf059e38d"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "450",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableCell"
|
||||
}
|
||||
],
|
||||
"type": "tableRow"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "2c8a06942ac4"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"colwidth": [
|
||||
135
|
||||
],
|
||||
"localId": "ba4e1cb93e92"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "e6bb346c5fd4"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Notes",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableHeader"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"colwidth": [
|
||||
233
|
||||
],
|
||||
"localId": "d7a1dec97b03"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "5cbd8813deab"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Spring is confirmed.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "tableCell"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"colwidth": [
|
||||
184
|
||||
],
|
||||
"localId": "85f9a8dab00b"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "da90346e9a26",
|
||||
"title": "Payment terms"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "ef6c6f374bca"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Invoices arrive monthly.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "nestedExpand"
|
||||
}
|
||||
],
|
||||
"type": "tableCell"
|
||||
}
|
||||
],
|
||||
"type": "tableRow"
|
||||
}
|
||||
],
|
||||
"type": "table"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "10d03e65eff2"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Figures are estimates.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"level": 2,
|
||||
"localId": "26d50a5240c3"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Action items",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "f8b236b2-3b7c-412a-a0df-6c523af43a76"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "eebe3cb9-5773-4753-b6a3-5a5df104b6e8",
|
||||
"state": "DONE"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Book the meeting room",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "taskItem"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "2263a39a-c49c-4711-8f00-cca6e8f3ea35",
|
||||
"state": "TODO"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Order name badges",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "taskItem"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "e323e762-8880-43c0-945d-17a7341fdefb"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "55523903-9f79-4711-b276-ec1bc98be977",
|
||||
"state": "DONE"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Print a spare set for the front desk",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "taskItem"
|
||||
}
|
||||
],
|
||||
"type": "taskList"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "680d665a-a859-4345-b032-d5a46fd6db0d",
|
||||
"state": "TODO"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Send the agenda to everyone",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "taskItem"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "48d45b93-2180-4b80-b0ed-f9eb458ed1ed",
|
||||
"state": "TODO"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Ask ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"accessLevel": "",
|
||||
"id": "712020:78bf5058-bc76-4783-99af-6d70cb593cee",
|
||||
"localId": "09321e9b-b551-48ee-8868-5339e8b86863",
|
||||
"text": "@Larvit Developer"
|
||||
},
|
||||
"type": "mention"
|
||||
},
|
||||
{
|
||||
"text": " about parking by ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "16c6b2a1718e",
|
||||
"timestamp": "1792022400000"
|
||||
},
|
||||
"type": "date"
|
||||
},
|
||||
{
|
||||
"text": " ",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "taskItem"
|
||||
}
|
||||
],
|
||||
"type": "taskList"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "90a7d38e3ed7"
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "8c510497da2e"
|
||||
},
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"level": 2,
|
||||
"localId": "3b52fc685167"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Decisions",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "82dbc28b-49c8-428c-8c21-65cc000ef7c5"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "2f7a7680-2c41-4b45-9162-29d97f922a7b",
|
||||
"state": "DECIDED"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Hold the kickoff on the second floor",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "decisionItem"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "c94ef59b-6db4-4ddc-8d22-576821020306",
|
||||
"state": "DECIDED"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Keep the budget under the agreed limit",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "decisionItem"
|
||||
}
|
||||
],
|
||||
"type": "decisionList"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "562121f961a8"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Done items are ticked; the rest are still open.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"level": 2,
|
||||
"localId": "ef39f25be439"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Text formatting",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "heading"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "4a24d2441f4b"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "This sentence has ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "strong"
|
||||
}
|
||||
],
|
||||
"text": "bold",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": ", ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "em"
|
||||
}
|
||||
],
|
||||
"text": "italic",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": ", ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "underline"
|
||||
}
|
||||
],
|
||||
"text": "underlined",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": ", ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "strike"
|
||||
}
|
||||
],
|
||||
"text": "struck",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": " and ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "code"
|
||||
}
|
||||
],
|
||||
"text": "inline code",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": " words in it.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "707e9aa736b2"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Water is H",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"attrs": {
|
||||
"type": "sub"
|
||||
},
|
||||
"type": "subsup"
|
||||
}
|
||||
],
|
||||
"text": "2",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": "O and the room measures 20 m",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"attrs": {
|
||||
"type": "sup"
|
||||
},
|
||||
"type": "subsup"
|
||||
}
|
||||
],
|
||||
"text": "2",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": ".",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "a03bd1fe3442"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Some words are ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"attrs": {
|
||||
"color": "#ff5630"
|
||||
},
|
||||
"type": "textColor"
|
||||
}
|
||||
],
|
||||
"text": "red",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": ", some are ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"attrs": {
|
||||
"color": "#0747a6"
|
||||
},
|
||||
"type": "textColor"
|
||||
}
|
||||
],
|
||||
"text": "bold blue",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": ", and one is ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"attrs": {
|
||||
"color": "#f8e6a0"
|
||||
},
|
||||
"type": "backgroundColor"
|
||||
}
|
||||
],
|
||||
"text": "highlighted",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": ".",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "f3a744347f56"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "A ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "em"
|
||||
},
|
||||
{
|
||||
"type": "strong"
|
||||
}
|
||||
],
|
||||
"text": "bold italic",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": " phrase, and a ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "strike"
|
||||
},
|
||||
{
|
||||
"type": "underline"
|
||||
}
|
||||
],
|
||||
"text": "struck underlined",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": " one.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "643a019d97c1"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"text": "Read the ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"attrs": {
|
||||
"href": "https://spec.commonmark.org/0.31.2/"
|
||||
},
|
||||
"type": "link"
|
||||
}
|
||||
],
|
||||
"text": "CommonMark spec",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": " or the ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"attrs": {
|
||||
"href": "https://www.markdownguide.org/basic-syntax/"
|
||||
},
|
||||
"type": "link"
|
||||
}
|
||||
],
|
||||
"text": "basic syntax guide",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": " before you edit.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
"version": 1
|
||||
}
|
||||
@@ -2,5 +2,5 @@
|
||||
| --- | --- |
|
||||
| Bolt M8 | Grade `8.8` |
|
||||
| Nut \| washer | Sold as a pair |
|
||||
| Washer M8 | 100 pcs:hardBreak{}zinc-plated |
|
||||
| :text{text=" "}Spare | |
|
||||
| Washer M8 | 100 pcs!adf:hardBreak{}zinc-plated |
|
||||
| !adf:text{text=" "}Spare | |
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
a `b :inlineCard{url="c\u0060d"}
|
||||
a `b !adf:inlineCard{url="c\u0060d"}
|
||||
|
||||
:inlineCard{url="https://example.com/s?q=a\u0026amp;b\u003cc"}
|
||||
!adf:inlineCard{url="https://example.com/s?q=a\u0026amp;b\u003cc"}
|
||||
|
||||
::extension {extensionKey=jira parameters="{\"jql\":\"a\u007cb \u0060c\u0060 \u0026 d \u003c e\"}"}
|
||||
|
||||
[a:inlineCard{url="b](c)"}
|
||||
[a!adf:inlineCard{url="b](c)"}
|
||||
|
||||
@@ -38,4 +38,4 @@
|
||||
}
|
||||
```
|
||||
|
||||
See :adf{json="{\"attrs\":{\"localId\":5,\"text\":\"In review\"},\"type\":\"status\"}"} now.
|
||||
See !adf:carry{json="{\"attrs\":{\"localId\":5,\"text\":\"In review\"},\"type\":\"status\"}"} now.
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
Reviewed :adf{json="{\"marks\":[{\"attrs\":{\"annotationType\":\"inlineComment\",\"id\":\"0198f3a2-7c41-7f2e-9b3a-4d8e2c1a6b90\"},\"type\":\"annotation\"}],\"text\":\"audited\",\"type\":\"text\"}"} twice.
|
||||
Reviewed !adf:carry{json="{\"marks\":[{\"attrs\":{\"annotationType\":\"inlineComment\",\"id\":\"0198f3a2-7c41-7f2e-9b3a-4d8e2c1a6b90\"},\"type\":\"annotation\"}],\"text\":\"audited\",\"type\":\"text\"}"} twice.
|
||||
|
||||
:adf{json="{\"marks\":[{\"attrs\":{\"colour\":\"red\"},\"type\":\"em\"}],\"text\":\"urgent\",\"type\":\"text\"}"}
|
||||
!adf:carry{json="{\"marks\":[{\"attrs\":{\"colour\":\"red\"},\"type\":\"em\"}],\"text\":\"urgent\",\"type\":\"text\"}"}
|
||||
|
||||
:adf{json="{\"marks\":[{\"type\":\"code\"},{\"type\":\"strong\"}],\"text\":\"flag\",\"type\":\"text\"}"}
|
||||
!adf:carry{json="{\"marks\":[{\"type\":\"code\"},{\"type\":\"strong\"}],\"text\":\"flag\",\"type\":\"text\"}"}
|
||||
|
||||
:adf{json="{\"marks\":[{\"type\":\"code\"}],\"text\":\"a\\nb\",\"type\":\"text\"}"}
|
||||
!adf:carry{json="{\"marks\":[{\"type\":\"code\"}],\"text\":\"a\\nb\",\"type\":\"text\"}"}
|
||||
|
||||
:adf{json="{\"marks\":[{\"type\":\"link\"}],\"text\":\"Release notes\",\"type\":\"text\"}"}
|
||||
!adf:carry{json="{\"marks\":[{\"type\":\"link\"}],\"text\":\"Release notes\",\"type\":\"text\"}"}
|
||||
|
||||
a:adf{json="{\"marks\":[{\"type\":\"em\"}],\"text\":\".\",\"type\":\"text\"}"}b:adf{json="{\"marks\":[{\"type\":\"strong\"}],\"text\":\",\",\"type\":\"text\"}"}c
|
||||
a!adf:carry{json="{\"marks\":[{\"type\":\"em\"}],\"text\":\".\",\"type\":\"text\"}"}b!adf:carry{json="{\"marks\":[{\"type\":\"strong\"}],\"text\":\",\",\"type\":\"text\"}"}c
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "01a0a067-68cf-78af-abd6-c660ec0d189b"
|
||||
},
|
||||
"text": "Owner",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": " signs off.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"text": "Signed by ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "01a0a067-68d2-787e-afc5-2459776aa029"
|
||||
},
|
||||
"text": "the owner",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "01a0a067-68d5-7372-9962-29a039654056"
|
||||
},
|
||||
"text": "One ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"localId": "01a0a067-68d5-7372-9962-29a039654056"
|
||||
},
|
||||
"text": "anchor",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
!adf:carry{json="{\"attrs\":{\"localId\":\"01a0a067-68cf-78af-abd6-c660ec0d189b\"},\"text\":\"Owner\",\"type\":\"text\"}"} signs off.
|
||||
|
||||
Signed by !adf:carry{json="{\"attrs\":{\"localId\":\"01a0a067-68d2-787e-afc5-2459776aa029\"},\"text\":\"the owner\",\"type\":\"text\"}"}
|
||||
|
||||
!adf:carry{json="{\"attrs\":{\"localId\":\"01a0a067-68d5-7372-9962-29a039654056\"},\"text\":\"One \",\"type\":\"text\"}"}!adf:carry{json="{\"attrs\":{\"localId\":\"01a0a067-68d5-7372-9962-29a039654056\"},\"text\":\"anchor\",\"type\":\"text\"}"}
|
||||
@@ -3,7 +3,16 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"text": "Write :mention[@Mikael]{id=5b10a2} to tag someone.",
|
||||
"text": "Write !adf:mention[@Mikael]{id=5b10a2} to tag someone.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"text": "Namespaced under !adf: alone.",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
@@ -54,7 +63,7 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"text": ":status[nope]{color=red} vs ",
|
||||
"text": "!adf:status[nope]{color=red} vs ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
Write \:mention[@Mikael]{id=5b10a2} to tag someone.
|
||||
Write \!adf:mention[@Mikael]{id=5b10a2} to tag someone.
|
||||
|
||||
Namespaced under \!adf: alone.
|
||||
|
||||
\::syncBlock {localId=x}
|
||||
|
||||
@@ -6,6 +8,6 @@ Write \:mention[@Mikael]{id=5b10a2} to tag someone.
|
||||
|
||||
Stand-up at 10:30, shipped :tada:, {maxLevel} deep, a ::syncBlock mid-line.
|
||||
|
||||
:status[In review]\{color} decides.
|
||||
!adf:status[In review]\{color} decides.
|
||||
|
||||
\:status[nope]{color=red} vs :status[yep]{color=red}
|
||||
\!adf:status[nope]{color=red} vs !adf:status[yep]{color=red}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"text": "`` ``",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "code"
|
||||
}
|
||||
],
|
||||
"text": "a",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "underline"
|
||||
}
|
||||
],
|
||||
"text": "``",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": "``",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "code"
|
||||
}
|
||||
],
|
||||
"text": "a",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"text": "``\\`",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "code"
|
||||
}
|
||||
],
|
||||
"text": "`",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": "`",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
\`\` \`\``a`
|
||||
|
||||
!adf:underline[\`\`]\`\``a`
|
||||
|
||||
\`\`\\\``` ` ``\`
|
||||
@@ -1,6 +1,6 @@
|
||||
# Release 2.4
|
||||
|
||||
Shipped :emoji[🚀]{shortName=":rocket:"} on :date{timestamp=1756080000000} — :mention[@Mikael]{id=01a032c3-7a7c-775f-a730-2d79351338b4} owns the rollout, status :status[In review]{color=yellow}.
|
||||
Shipped !adf:emoji[🚀]{shortName=":rocket:"} on !adf:date{timestamp=1756080000000} — !adf:mention[@Mikael]{id=01a032c3-7a7c-775f-a730-2d79351338b4} owns the rollout, status !adf:status[In review]{color=yellow}.
|
||||
|
||||
| Part | Qty |
|
||||
| --- | --- |
|
||||
@@ -11,7 +11,7 @@ Shipped :emoji[🚀]{shortName=":rocket:"} on :date{timestamp=1756080000000} —
|
||||
- Check the collation:
|
||||
- `mysqldump --default-character-set=utf8mb4`
|
||||
|
||||
> Rolled back once, see the :underline[postmortem].
|
||||
> Rolled back once, see the !adf:underline[postmortem].
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "underline"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"href": "http://a`b"
|
||||
},
|
||||
"type": "link"
|
||||
}
|
||||
],
|
||||
"text": "http://a`b",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": "`c",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "underline"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"href": "http://d`e"
|
||||
},
|
||||
"type": "link"
|
||||
}
|
||||
],
|
||||
"text": "http://d`e",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
!adf:underline[[http://a\`b](http://a\`b)]`c
|
||||
|
||||
!adf:underline[[http://d\`e](http://d`e)]
|
||||
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"attrs": {
|
||||
"color": ""
|
||||
},
|
||||
"type": "textColor"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"href": "`"
|
||||
},
|
||||
"type": "link"
|
||||
}
|
||||
],
|
||||
"text": " ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"text": "`a",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "underline"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"href": "c",
|
||||
"title": "`"
|
||||
},
|
||||
"type": "link"
|
||||
}
|
||||
],
|
||||
"text": "b",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "code"
|
||||
}
|
||||
],
|
||||
"text": "c",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "underline"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"href": "e`f"
|
||||
},
|
||||
"type": "link"
|
||||
}
|
||||
],
|
||||
"text": "d",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
!adf:textColor[[ ](\`)]{color=""}`a
|
||||
|
||||
!adf:underline[[b](c "\`")]`c`
|
||||
|
||||
!adf:underline[[d](e`f)]
|
||||
@@ -0,0 +1,111 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "subsup"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"href": "["
|
||||
},
|
||||
"type": "link"
|
||||
}
|
||||
],
|
||||
"text": "a",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "underline"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"href": "]"
|
||||
},
|
||||
"type": "link"
|
||||
}
|
||||
],
|
||||
"text": "b",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "underline"
|
||||
}
|
||||
],
|
||||
"text": " ",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "underline"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"href": "x",
|
||||
"title": "["
|
||||
},
|
||||
"type": "link"
|
||||
}
|
||||
],
|
||||
"text": "c",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "underline"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"href": "e[f]"
|
||||
},
|
||||
"type": "link"
|
||||
}
|
||||
],
|
||||
"text": "d",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "underline"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"href": "http://a[b"
|
||||
},
|
||||
"type": "link"
|
||||
}
|
||||
],
|
||||
"text": "http://a[b",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
!adf:subsup[[a](\[)]
|
||||
|
||||
!adf:underline[[b](\]) [c](x "\[")]
|
||||
|
||||
!adf:underline[[d](e[f])]
|
||||
|
||||
!adf:underline[[http://a\[b](http://a\[b)]
|
||||
@@ -0,0 +1,127 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "underline"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"href": "!adf:a{"
|
||||
},
|
||||
"type": "link"
|
||||
}
|
||||
],
|
||||
"text": "x",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "underline"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"href": "y",
|
||||
"title": "!adf:a{"
|
||||
},
|
||||
"type": "link"
|
||||
}
|
||||
],
|
||||
"text": "x",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "underline"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"href": "!adf:a[]{"
|
||||
},
|
||||
"type": "link"
|
||||
}
|
||||
],
|
||||
"text": "x",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "underline"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"href": "https://example.com/?q=!adf:emoji{"
|
||||
},
|
||||
"type": "link"
|
||||
}
|
||||
],
|
||||
"text": "x",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "underline"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"href": "ab:!adf:c{"
|
||||
},
|
||||
"type": "link"
|
||||
}
|
||||
],
|
||||
"text": "ab:!adf:c{",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "underline"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"href": "ab:!adf:d"
|
||||
},
|
||||
"type": "link"
|
||||
}
|
||||
],
|
||||
"text": "ab:!adf:d",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
!adf:underline[[x](\!adf:a{)]
|
||||
|
||||
!adf:underline[[x](y "\!adf:a{")]
|
||||
|
||||
!adf:underline[[x](\!adf:a[]{)]
|
||||
|
||||
!adf:underline[[x](https://example.com/?q=\!adf:emoji{)]
|
||||
|
||||
!adf:underline[[ab:\!adf:c{](ab:\!adf:c{)]
|
||||
|
||||
!adf:underline[[ab:\!adf:d](ab:\!adf:d)]
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"attrs": {
|
||||
"href": "/u"
|
||||
},
|
||||
"type": "link"
|
||||
},
|
||||
{
|
||||
"type": "code"
|
||||
}
|
||||
],
|
||||
"text": "]: a",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"attrs": {
|
||||
"timestamp": "]:a"
|
||||
},
|
||||
"marks": [
|
||||
{
|
||||
"attrs": {
|
||||
"href": "/u"
|
||||
},
|
||||
"type": "link"
|
||||
}
|
||||
],
|
||||
"type": "date"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
!adf:carry{json="{\"marks\":[{\"attrs\":{\"href\":\"/u\"},\"type\":\"link\"},{\"type\":\"code\"}],\"text\":\"]: a\",\"type\":\"text\"}"}
|
||||
|
||||
!adf:carry{json="{\"attrs\":{\"timestamp\":\"]:a\"},\"marks\":[{\"attrs\":{\"href\":\"/u\"},\"type\":\"link\"}],\"type\":\"date\"}"}
|
||||
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"text": "[",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"timestamp": "]:a"
|
||||
},
|
||||
"type": "date"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"text": "[",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"type": "code"
|
||||
}
|
||||
],
|
||||
"text": "]: a",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "blockquote"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"text": "[",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"timestamp": "]:a"
|
||||
},
|
||||
"type": "date"
|
||||
},
|
||||
{
|
||||
"type": "hardBreak"
|
||||
},
|
||||
{
|
||||
"text": "b",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
}
|
||||
],
|
||||
"type": "bulletList"
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
\[!adf:date{timestamp="]:a"}
|
||||
|
||||
> \[`]: a`
|
||||
|
||||
- \[!adf:date{timestamp="]:a"}\
|
||||
b
|
||||
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"text": " \na",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "codeBlock"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
}
|
||||
],
|
||||
"type": "bulletList"
|
||||
},
|
||||
{
|
||||
"attrs": {
|
||||
"order": 1
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"text": "\t",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "codeBlock"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
}
|
||||
],
|
||||
"type": "bulletList"
|
||||
}
|
||||
],
|
||||
"type": "listItem"
|
||||
}
|
||||
],
|
||||
"type": "orderedList"
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
::::bulletList
|
||||
:::listItem
|
||||
```
|
||||
|
||||
a
|
||||
```
|
||||
:::
|
||||
::::
|
||||
|
||||
::::::orderedList {order=1}
|
||||
:::::listItem
|
||||
::::bulletList
|
||||
:::listItem
|
||||
```
|
||||
|
||||
```
|
||||
:::
|
||||
::::
|
||||
:::::
|
||||
::::::
|
||||
@@ -1 +1 @@
|
||||
un:adf{json="{\"marks\":[{\"type\":\"strong\"}],\"text\":\"-real\",\"type\":\"text\"}"}istic
|
||||
un!adf:carry{json="{\"marks\":[{\"type\":\"strong\"}],\"text\":\"-real\",\"type\":\"text\"}"}istic
|
||||
|
||||
@@ -1 +1 @@
|
||||
_**Ready**_:adf{json="{\"attrs\":{\"extensionKey\":\"status-lozenge\",\"extensionType\":\"com.atlassian.confluence.macro.core\"},\"marks\":[{\"type\":\"em\"},{\"type\":\"strong\"}],\"type\":\"inlineExtension\"}"}_**to ship**_
|
||||
_**Ready**_!adf:carry{json="{\"attrs\":{\"extensionKey\":\"status-lozenge\",\"extensionType\":\"com.atlassian.confluence.macro.core\"},\"marks\":[{\"type\":\"em\"},{\"type\":\"strong\"}],\"type\":\"inlineExtension\"}"}_**to ship**_
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
un:adf{json="{\"marks\":[{\"type\":\"em\"}],\"text\":\"a\",\"type\":\"text\"}"}:adf{json="{\"marks\":[{\"type\":\"em\"},{\"type\":\"strong\"}],\"text\":\"b\",\"type\":\"text\"}"}**c**istic
|
||||
un!adf:carry{json="{\"marks\":[{\"type\":\"em\"}],\"text\":\"a\",\"type\":\"text\"}"}!adf:carry{json="{\"marks\":[{\"type\":\"em\"},{\"type\":\"strong\"}],\"text\":\"b\",\"type\":\"text\"}"}**c**istic
|
||||
|
||||
***a*b**:adf{json="{\"marks\":[{\"type\":\"strong\"},{\"type\":\"em\"}],\"text\":\"c\",\"type\":\"text\"}"}
|
||||
***a*b**!adf:carry{json="{\"marks\":[{\"type\":\"strong\"},{\"type\":\"em\"}],\"text\":\"c\",\"type\":\"text\"}"}
|
||||
|
||||
~~un~~:adf{json="{\"marks\":[{\"type\":\"strike\"},{\"type\":\"em\"}],\"text\":\"a\",\"type\":\"text\"}"}:adf{json="{\"marks\":[{\"type\":\"strike\"},{\"type\":\"em\"},{\"type\":\"strong\"}],\"text\":\"b\",\"type\":\"text\"}"}~~**c**istic~~
|
||||
~~un~~!adf:carry{json="{\"marks\":[{\"type\":\"strike\"},{\"type\":\"em\"}],\"text\":\"a\",\"type\":\"text\"}"}!adf:carry{json="{\"marks\":[{\"type\":\"strike\"},{\"type\":\"em\"},{\"type\":\"strong\"}],\"text\":\"b\",\"type\":\"text\"}"}~~**c**istic~~
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
:textColor[Overdue since :status[Blocked]{color=red} on Tuesday]{color="#ae2e24"}
|
||||
!adf:textColor[Overdue since !adf:status[Blocked]{color=red} on Tuesday]{color="#ae2e24"}
|
||||
|
||||
_Torque the **M8 bolt** to **25 Nm**_
|
||||
|
||||
[Release notes :emoji[🚀]{shortName=":rocket:"}](https://example.com/release)
|
||||
[Release notes !adf:emoji[🚀]{shortName=":rocket:"}](https://example.com/release)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
| Cell | Note |
|
||||
| --- | --- |
|
||||
| :text{text=" "}lead | |
|
||||
| :status[ok]{style="a\u007cb"} | trail:text{text=" "} |
|
||||
| !adf:text{text=" "}lead | |
|
||||
| !adf:status[ok]{style="a\u007cb"} | trail!adf:text{text=" "} |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Line one\
|
||||
Line two
|
||||
|
||||
Trailing:hardBreak{}
|
||||
Trailing!adf:hardBreak{}
|
||||
|
||||
## Two:hardBreak{}lines
|
||||
## Two!adf:hardBreak{}lines
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"marks": [
|
||||
{
|
||||
"attrs": {
|
||||
"href": "",
|
||||
"title": ""
|
||||
},
|
||||
"type": "link"
|
||||
}
|
||||
],
|
||||
"text": "a",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
[a](<> "")
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"text": "`a ``b``",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
},
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"text": "`a",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"type": "hardBreak"
|
||||
},
|
||||
{
|
||||
"text": "```b``",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"type": "paragraph"
|
||||
}
|
||||
],
|
||||
"type": "doc",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
\`a \`\`b``
|
||||
|
||||
\`a\
|
||||
\`\`\`b``
|
||||
@@ -1,5 +1,5 @@
|
||||
:status[In review]{color=yellow} carries the text in the slot.
|
||||
!adf:status[In review]{color=yellow} carries the text in the slot.
|
||||
|
||||
:status{color=neutral} carries no text attribute.
|
||||
!adf:status{color=neutral} carries no text attribute.
|
||||
|
||||
:status[]{color=neutral} carries the empty string.
|
||||
!adf:status[]{color=neutral} carries the empty string.
|
||||
|
||||
@@ -1 +1 @@
|
||||
Due :date{timestamp=1756080000000}, reviewed :date{localId=01a033ab-781c-7f79-9c8a-fb63356c1cad timestamp=1735689600000}.
|
||||
Due !adf:date{timestamp=1756080000000}, reviewed !adf:date{localId=01a033ab-781c-7f79-9c8a-fb63356c1cad timestamp=1735689600000}.
|
||||
|
||||
@@ -1 +1 @@
|
||||
Shipped :emoji[🎉]{id=1f389 localId=01a033ad-f00e-7acb-a009-37ddd25d7949 shortName=":tada:"} on Friday.
|
||||
Shipped !adf:emoji[🎉]{id=1f389 localId=01a033ad-f00e-7acb-a009-37ddd25d7949 shortName=":tada:"} on Friday.
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
One:hardBreak{localId=01a0431d-201e-7400-a175-9c04d3628f95}Two
|
||||
One!adf:hardBreak{localId=01a0431d-201e-7400-a175-9c04d3628f95}Two
|
||||
|
||||
Three:hardBreak{text="\n"}Four
|
||||
Three!adf:hardBreak{text="\n"}Four
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
Tracking :inlineCard{url="https://example.com/browse/ABC-123"} this week.
|
||||
Tracking !adf:inlineCard{url="https://example.com/browse/ABC-123"} this week.
|
||||
|
||||
:inlineCard{data="{\"@type\":\"Link\",\"name\":\"Torque spec\",\"url\":\"https://example.com/torque\"}" localId=01a033ad-1fa5-76fa-aad0-2e56a17d76aa}
|
||||
!adf:inlineCard{data="{\"@type\":\"Link\",\"name\":\"Torque spec\",\"url\":\"https://example.com/torque\"}" localId=01a033ad-1fa5-76fa-aad0-2e56a17d76aa}
|
||||
|
||||
@@ -1 +1 @@
|
||||
**:mention[@Mikael]{id=01a032c3-7a7c-775f-a730-2d79351338b4}** owns the rebuild.
|
||||
**!adf:mention[@Mikael]{id=01a032c3-7a7c-775f-a730-2d79351338b4}** owns the rebuild.
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
_:underline[Rewired]_ at the mains.
|
||||
_!adf:underline[Rewired]_ at the mains.
|
||||
|
||||
:underline[_Rewound_] by hand.
|
||||
!adf:underline[_Rewound_] by hand.
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
:textColor[Over]{color="#ae2e24"}:textColor[due]{color="#a54800"} since the H:subsup[2]{type=sub}O test, :underline[signed by :mention[@Mikael]{id=01a032c3-7a7c-775f-a730-2d79351338b4}] on the 2:subsup[nd]{type=sup} of May.
|
||||
!adf:textColor[Over]{color="#ae2e24"}!adf:textColor[due]{color="#a54800"} since the H!adf:subsup[2]{type=sub}O test, !adf:underline[signed by !adf:mention[@Mikael]{id=01a032c3-7a7c-775f-a730-2d79351338b4}] on the 2!adf:subsup[nd]{type=sup} of May.
|
||||
|
||||
: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}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
See :mediaInline{alt="Torque chart" collection=contentId-98237 height=24 id=01a033ae-69a3-7411-b388-a37f9d103ded type=file width=24} mid-sentence.
|
||||
See !adf:mediaInline{alt="Torque chart" collection=contentId-98237 height=24 id=01a033ae-69a3-7411-b388-a37f9d103ded type=file width=24} mid-sentence.
|
||||
|
||||
Or :mediaInline{data="{\"fileName\":\"budget.pdf\",\"fileSize\":18432}" localId=01a033b1-b5a0-7a98-9e9c-48192bd30ac0 occurrenceKey=01a033ab-bab8-74b0-9ec8-b3419b000189 type=image} beside the text.
|
||||
Or !adf:mediaInline{data="{\"fileName\":\"budget.pdf\",\"fileSize\":18432}" localId=01a033b1-b5a0-7a98-9e9c-48192bd30ac0 occurrenceKey=01a033ab-bab8-74b0-9ec8-b3419b000189 type=image} beside the text.
|
||||
|
||||
@@ -1 +1 @@
|
||||
Ping :mention[@Mikael]{accessLevel=CONTAINER id=01a032c3-7a7c-775f-a730-2d79351338b4 localId=01a033ad-ecb7-7530-aa1c-ff25031e96e2 userType=DEFAULT} before merging.
|
||||
Ping !adf:mention[@Mikael]{accessLevel=CONTAINER id=01a032c3-7a7c-775f-a730-2d79351338b4 localId=01a033ad-ecb7-7530-aa1c-ff25031e96e2 userType=DEFAULT} before merging.
|
||||
|
||||
@@ -1 +1 @@
|
||||
The rebuild is :status[In review]{color=yellow localId=01a033a7-d68a-71a9-a58a-edf5757ad176 style=bold} until Friday.
|
||||
The rebuild is !adf:status[In review]{color=yellow localId=01a033a7-d68a-71a9-a58a-edf5757ad176 style=bold} until Friday.
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
: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.
|
||||
|
||||
Trailing space held:text{text=" "}
|
||||
Trailing space held!adf:text{text=" "}
|
||||
|
||||
:text{text="\t"}Tabbed at the block edge.
|
||||
!adf:text{text="\t"}Tabbed at the block edge.
|
||||
|
||||
Space before the break:text{text=" "}\
|
||||
Space before the break!adf:text{text=" "}\
|
||||
survives.
|
||||
|
||||
Space after the break\
|
||||
:text{text=" "}survives too.
|
||||
!adf:text{text=" "}survives too.
|
||||
|
||||
Kept _:text{text=" "}inside:text{text=" "}_, exactly.
|
||||
Kept _!adf:text{text=" "}inside!adf:text{text=" "}_, exactly.
|
||||
|
||||
Edged with the whitespace CommonMark keeps
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
"text": ":",
|
||||
"text": "!adf:",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Draft: :adf{json="{\"attrs\":{\"text\":\"Add a summary\"},\"type\":\"placeholder\"}"} — expected Friday.
|
||||
Draft: !adf:carry{json="{\"attrs\":{\"text\":\"Add a summary\"},\"type\":\"placeholder\"}"} — expected Friday.
|
||||
|
||||
**Blocked** on :adf{json="{\"attrs\":{\"extensionKey\":\"jira-issue\",\"extensionType\":\"com.atlassian.jira.macro\"},\"marks\":[{\"type\":\"strong\"}],\"type\":\"inlineExtension\"}"} until the rebuild lands.
|
||||
**Blocked** on !adf:carry{json="{\"attrs\":{\"extensionKey\":\"jira-issue\",\"extensionType\":\"com.atlassian.jira.macro\"},\"marks\":[{\"type\":\"strong\"}],\"type\":\"inlineExtension\"}"} until the rebuild lands.
|
||||
|
||||
\::adf{json="{\"type\":\"blockCard\"}"}
|
||||
\!adf:!adf:carry{json="{\"type\":\"blockCard\"}"}
|
||||
|
||||
**a**:adf{json="{\"marks\":[{\"type\":\"strong\"}],\"type\":\"placeholder\"}"}**b**
|
||||
**a**!adf:carry{json="{\"marks\":[{\"type\":\"strong\"}],\"type\":\"placeholder\"}"}**b**
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ node_image=node:24.19.0-alpine3.24
|
||||
in_image() {
|
||||
local image=$1 entrypoint=$2
|
||||
shift 2
|
||||
docker run --rm -u "$(id -u):$(id -g)" -e HOME=/tmp ${in_image_network:+--network "$in_image_network"} -v "$PWD:/app" -w /app --entrypoint "$entrypoint" "$image" "$@"
|
||||
docker run --rm -u "$(id -u):$(id -g)" -e HOME=/tmp ${PROPERTY_RUNS+-e PROPERTY_RUNS} ${in_image_network:+--network "$in_image_network"} -v "$PWD:/app" -w /app --entrypoint "$entrypoint" "$image" "$@"
|
||||
}
|
||||
|
||||
with_firefox() {
|
||||
|
||||
Generated
+41
@@ -10,6 +10,7 @@
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/node": "24.13.3",
|
||||
"fast-check": "4.10.0",
|
||||
"typescript": "7.0.2"
|
||||
},
|
||||
"engines": {
|
||||
@@ -366,6 +367,46 @@
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-check": {
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-check/-/fast-check-4.10.0.tgz",
|
||||
"integrity": "sha512-hhqQL+IJllZi3aM4TKvmCj3bywLEcycNTTLZeLhA9ttMxBrCqM07q7Di4kl+j9EWSTXvJH1+EpIgsDbF/+8H5Q==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/dubzzz"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fast-check"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pure-rand": "^8.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.17.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pure-rand": {
|
||||
"version": "8.4.2",
|
||||
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-8.4.2.tgz",
|
||||
"integrity": "sha512-vvuOGgcuPJAirlHvuQw1TrOiw7ptaIXXmIbNuiNOY6lNGJJH49PQ1Kj4nd783nPdQhQdicgOjVI2yI/9BD6/Ng==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/dubzzz"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fast-check"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz",
|
||||
|
||||
+2
-1
@@ -23,11 +23,12 @@
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc -p tsconfig.build.json",
|
||||
"test": "node --test --experimental-test-coverage --test-coverage-exclude=\"src/**/*.test.ts\" --test-coverage-branches=98 --test-coverage-functions=100 --test-coverage-lines=100 \"src/**/*.test.ts\"",
|
||||
"test": "node --test --experimental-test-coverage --test-coverage-exclude=\"src/**/*.test.ts\" --test-coverage-exclude=src/property-harness.ts --test-coverage-branches=98 --test-coverage-functions=100 --test-coverage-lines=100 \"src/**/*.test.ts\"",
|
||||
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.build.json"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "24.13.3",
|
||||
"fast-check": "4.10.0",
|
||||
"typescript": "7.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
Copyright 2019 Atlassian Pty Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,6 @@
|
||||
# Atlassian's ADF JSON Schemas
|
||||
|
||||
`full.json` and `stage-0.json` are byte-exact from `dist/json-schema/v1/` in
|
||||
[`@atlaskit/adf-schema` 57.4.9](https://registry.npmjs.org/@atlaskit/adf-schema/-/adf-schema-57.4.9.tgz)
|
||||
(© Atlassian Pty Ltd, Apache-2.0: `LICENSE` is the package's own notice, `LICENSE-2.0.txt` the
|
||||
licence it names).
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+140
-122
@@ -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,10 +25,11 @@ 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.
|
||||
named error. A list whose item holds a line of spaces or tabs alone, which a list item reads
|
||||
back empty, takes the directive form.
|
||||
- Blockquotes prefix lines with `> `; a blank line inside a blockquote is a bare `>`.
|
||||
- ATX headings (`#` … `######`); setext input normalizes to ATX.
|
||||
- Code fences ``` with the node's language as info string, the fence lengthened past any backtick
|
||||
@@ -42,15 +43,25 @@ 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`.
|
||||
- Links `[text](url)`; `<…>` around a destination containing spaces; 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 when the text equals the destination and
|
||||
the destination is a valid CommonMark autolink (absolute URI).
|
||||
- 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
|
||||
when the text equals the destination and the destination is a valid CommonMark autolink
|
||||
(absolute URI) — inside an inline directive's `[content]`, one holding no backtick, no
|
||||
unbalanced bracket and no inline directive opener.
|
||||
- Paragraphs on one line — no soft wrapping; a soft line break in input becomes a single space.
|
||||
- Entity references in input decode to their characters; output backslash-escapes only where text
|
||||
would otherwise parse as syntax, scanning the assembled line rather than each text node: escape
|
||||
the leading delimiter of a construct that would otherwise open, re-scan from there, and repeat.
|
||||
A backtick run escapes whole, and a bare one escapes wherever a later backtick string of its
|
||||
length forms around an escape in the same inline content — an escaped backtick alone, one joined
|
||||
to the bare run after it, or a bare run an escape splits off: CommonMark reads no escape inside a
|
||||
code span, so such a string still closes one.
|
||||
Where a paragraph opens with what reads as a link reference definition, which resolves before
|
||||
any inline construct binds (a `]` inside a code span or `{attrs}` counts), an opening text `[`
|
||||
escapes and an opening link's nodes ride the carry.
|
||||
An emphasis delimiter run in text escapes where CommonMark can open **or** close with it, so
|
||||
`*not emphasis*` is `\*not emphasis\*` — no delimiter the emitter did not write reaches the
|
||||
matching below, which is what lets the emitter decide its own pairings.
|
||||
@@ -62,56 +73,62 @@ 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 construct CommonMark binds first — a code span,
|
||||
an autolink URI, a link destination or title — holds its `!adf:` as content; a directive's
|
||||
`[content]`, which binds ahead of bracket matching, does not. 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
|
||||
@@ -121,22 +138,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 `{` 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)
|
||||
|
||||
@@ -146,16 +164,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
|
||||
|
||||
@@ -181,9 +198,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
|
||||
@@ -200,7 +217,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
|
||||
@@ -215,11 +232,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
|
||||
@@ -230,9 +247,9 @@ fn main() {}
|
||||
panels.
|
||||
|
||||
```
|
||||
:::panel warning
|
||||
!adf:panel warning
|
||||
Check the collation before importing.
|
||||
:::
|
||||
!adf:/panel
|
||||
```
|
||||
|
||||
### Expand
|
||||
@@ -241,9 +258,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
|
||||
@@ -252,19 +269,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
|
||||
@@ -307,27 +324,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`),
|
||||
@@ -351,14 +368,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
|
||||
@@ -368,14 +385,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
|
||||
@@ -387,7 +404,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
|
||||
@@ -396,18 +413,18 @@ 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
|
||||
|
||||
Attributes and the carry fallback read as in the block sections, the carry in its inline form. Of
|
||||
the nodes below, `emoji`, `mention` and `status` spell their `text` attribute in the content slot
|
||||
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 identical-mark text nodes 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 node that takes none is a
|
||||
named error.
|
||||
the nodes below, `emoji`, `mention` and `status` spell their `text` attribute in the content slot as
|
||||
plain text: `[]` is the empty string, absent content is the absent attribute, non-empty content
|
||||
parsing to anything but one text node carrying neither marks, attributes nor content — 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,
|
||||
`!adf:text` included; content on a node that takes none is a named error.
|
||||
|
||||
- `date` — Attributes: `localId` (string), `timestamp` (string, epoch milliseconds).
|
||||
- `emoji` — Attributes: `id` (string), `localId` (string), `shortName` (string, `:name:`), `text`
|
||||
@@ -424,30 +441,30 @@ 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
|
||||
(AGENTS.md §2). Input reads that spelling alone: the value is one run of spaces and tabs, or one
|
||||
run of newlines, and anything else — a mixed run, or text CommonMark carries plainly — is a named
|
||||
error.
|
||||
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
|
||||
tabs, or one run of newlines, and anything else — a mixed run, or text CommonMark carries plainly —
|
||||
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).
|
||||
@@ -458,7 +475,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
|
||||
@@ -476,7 +494,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}
|
||||
```
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import fc from 'fast-check'
|
||||
import assert from 'node:assert/strict'
|
||||
import test from 'node:test'
|
||||
|
||||
import { adfDocument, propertyRuns, propertyTimeout } from './property-harness.ts'
|
||||
import { adfToMarkdown } from './markdown/emit/adf-to-markdown.ts'
|
||||
import { markdownToAdf } from './markdown/parse/markdown-to-adf.ts'
|
||||
import { toEditorNormal } from './adf/editor-normal.ts'
|
||||
|
||||
const gateRuns = 1600
|
||||
|
||||
test('a generated document refuses to emit, or its markdown reads back to it', { timeout: propertyTimeout }, () => {
|
||||
fc.assert(
|
||||
fc.property(adfDocument, (document) => {
|
||||
const emitted = adfToMarkdown(document)
|
||||
if (!emitted.ok) return
|
||||
const read = markdownToAdf(emitted.value)
|
||||
assert.ok(read.ok, read.ok ? '' : `${read.error.code}: ${read.error.message} — reading ${JSON.stringify(emitted.value)}`)
|
||||
assert.deepEqual(toEditorNormal(read.value), document, `reading ${JSON.stringify(emitted.value)}`)
|
||||
}),
|
||||
propertyRuns(gateRuns),
|
||||
)
|
||||
})
|
||||
@@ -0,0 +1,183 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { createHash } from 'node:crypto'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { dirname, join } from 'node:path'
|
||||
import test from 'node:test'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
import type { AttributeKind, AttributeVocabulary } from './adf/attribute-vocabulary.ts'
|
||||
import { blockDirectives } from './adf/block-directives.ts'
|
||||
import { inlineDirectives } from './adf/inline-directives.ts'
|
||||
import { markAttributes } from './adf/mark-attributes.ts'
|
||||
import { blockArgument } from './markdown/block-directive-arguments.ts'
|
||||
|
||||
type Held = Map<string, Set<AttributeKind>>
|
||||
type Properties = Map<string, SchemaObject[]>
|
||||
type SchemaObject = Readonly<Record<string, unknown>>
|
||||
type Spelled = [string, Map<string, AttributeKind>]
|
||||
|
||||
const carried = ['alignment', 'annotation', 'backgroundColor', 'blockCard', 'bodiedRule', 'breakout', 'dataConsumer', 'embedCard', 'fontSize', 'fragment', 'indentation', 'inlineExtension', 'placeholder']
|
||||
const definitionReference = '#/definitions/'
|
||||
const gaps = ['layoutSection.columnRuleStyle', 'link.collection', 'link.id', 'link.occurrenceKey', 'rule.color', 'rule.style', 'rule.weight']
|
||||
const grammarOwn = ['doc', 'text']
|
||||
const readKeywords = ['$ref', 'additionalProperties', 'allOf', 'anyOf', 'enum', 'items', 'maxItems', 'maximum', 'minItems', 'minLength', 'minimum', 'pattern', 'properties', 'required', 'type']
|
||||
const root = join(dirname(fileURLToPath(import.meta.url)), '..', 'spec', 'adf-schema')
|
||||
const schemaFiles = ['full.json', 'stage-0.json']
|
||||
|
||||
test('the ADF JSON Schemas are @atlaskit/adf-schema 57.4.9, vendored byte-exact', () => {
|
||||
const digest = (name: string) => createHash('sha256').update(readFileSync(join(root, name))).digest('hex')
|
||||
assert.equal(digest('full.json'), '75f080928a970250eb8289e9cae5374e3c2a6c0ac3ca22478acaa9d3f39484a3')
|
||||
assert.equal(digest('stage-0.json'), '56747e5a71c0d5f8c58f94180d69e4482f62c08020d66aaf327333681fcc1c8f')
|
||||
})
|
||||
|
||||
test('the tables spell the attribute names and kinds the ADF JSON Schemas give each type they spell, the pinned gaps apart', () => {
|
||||
const held = schemaTypes()
|
||||
const spelledTypes = spelled()
|
||||
const found: string[] = []
|
||||
const gapsHeld = new Set<string>()
|
||||
for (const [type, spelledKinds] of spelledTypes) {
|
||||
const kinds = held.get(type)
|
||||
if (kinds === undefined) {
|
||||
found.push(`${type}: the tables spell the type, the schema holds no definition of it`)
|
||||
continue
|
||||
}
|
||||
for (const [attribute, schemaKinds] of kinds) {
|
||||
const kind = spelledKinds.get(attribute)
|
||||
const holds = [...schemaKinds].sort().join(' or ')
|
||||
if (kind === undefined && gaps.includes(`${type}.${attribute}`)) gapsHeld.add(`${type}.${attribute}`)
|
||||
else if (kind === undefined) found.push(`${type}.${attribute}: the schema holds ${holds}, the tables spell nothing and the gaps list does not name it`)
|
||||
else if (schemaKinds.size !== 1 || !schemaKinds.has(kind)) found.push(`${type}.${attribute}: the tables spell ${kind}, the schema holds ${holds}`)
|
||||
}
|
||||
for (const [attribute, kind] of spelledKinds) if (!kinds.has(attribute)) found.push(`${type}.${attribute}: the tables spell ${kind}, the schema holds nothing`)
|
||||
}
|
||||
for (const gap of gaps) {
|
||||
if (gapsHeld.has(gap)) continue
|
||||
const [type = '', attribute = ''] = gap.split('.')
|
||||
const spelledKinds = spelledTypes.find(([name]) => name === type)?.[1]
|
||||
const kind = spelledKinds?.get(attribute)
|
||||
if (spelledKinds === undefined) found.push(`${gap}: the gaps list names it, the tables spell no ${type} type`)
|
||||
else if (kind === undefined) found.push(`${gap}: the gaps list names it, the schema holds nothing`)
|
||||
else found.push(`${gap}: the gaps list names it, the tables spell ${kind}`)
|
||||
}
|
||||
assert.deepEqual(found, [])
|
||||
})
|
||||
|
||||
test("the ADF JSON Schemas hold no type the tables leave unspelled, the pinned carried ones and the grammar's own apart", () => {
|
||||
const held = schemaTypes()
|
||||
const spelledNames = new Set(spelled().map(([type]) => type))
|
||||
const found: string[] = []
|
||||
for (const type of held.keys()) {
|
||||
if (!spelledNames.has(type) && !carried.includes(type) && !grammarOwn.includes(type)) found.push(`${type}: the schema holds the type, the tables spell none of it and the carried list does not name it`)
|
||||
}
|
||||
for (const type of carried) {
|
||||
if (!held.has(type)) found.push(`${type}: the carried list names the type, the schema holds no definition of it`)
|
||||
if (spelledNames.has(type)) found.push(`${type}: the carried list names the type, and the tables spell it`)
|
||||
}
|
||||
assert.deepEqual(found, [])
|
||||
})
|
||||
|
||||
function spelled(): Spelled[] {
|
||||
return [
|
||||
...Object.entries(blockDirectives).map(([type, entry]) => spelledType(type, entry.attributes, blockArgument(type))),
|
||||
...Object.entries(inlineDirectives).map(([type, entry]) => spelledType(type, entry.attributes)),
|
||||
...Object.entries(markAttributes).map(([type, attributes]) => spelledType(type, attributes)),
|
||||
]
|
||||
}
|
||||
|
||||
function spelledType(type: string, attributes: AttributeVocabulary, argument?: string): Spelled {
|
||||
const kinds = new Map(Object.entries(attributes))
|
||||
if (argument !== undefined) kinds.set(argument, 'string')
|
||||
return [type, kinds]
|
||||
}
|
||||
|
||||
function schemaTypes(): Map<string, Held> {
|
||||
const held = new Map<string, Held>()
|
||||
for (const file of schemaFiles) {
|
||||
const definitions = schemaObject(schemaObject(JSON.parse(readFileSync(join(root, file), 'utf8')), file)['definitions'], `${file} definitions`)
|
||||
for (const [name, definition] of Object.entries(definitions)) {
|
||||
const where = `${file} ${name}`
|
||||
for (const properties of alternatives(schemaObject(definition, where), definitions, where)) {
|
||||
const attributes = (properties.get('attrs') ?? []).flatMap((attrs) => alternatives(attrs, definitions, `${where} attrs`)).flatMap((alternative) => [...alternative])
|
||||
for (const type of (properties.get('type') ?? []).flatMap((schema) => enumStrings(schema, `${where} type`))) {
|
||||
const kinds = held.get(type) ?? new Map<string, Set<AttributeKind>>()
|
||||
held.set(type, kinds)
|
||||
for (const [attribute, schemas] of attributes) {
|
||||
const attributeKinds = kinds.get(attribute) ?? new Set<AttributeKind>()
|
||||
kinds.set(attribute, attributeKinds)
|
||||
for (const schema of schemas) for (const kind of propertyKinds(schema, `${where} ${type}.${attribute}`)) attributeKinds.add(kind)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return held
|
||||
}
|
||||
|
||||
function alternatives(schema: SchemaObject, definitions: SchemaObject, where: string): Properties[] {
|
||||
const own = Object.entries(schemaObject(readSchema(schema, where)['properties'] ?? {}, `${where} properties`))
|
||||
let found: Properties[] = [new Map(own.map(([name, property]): [string, SchemaObject[]] => [name, [readSchema(schemaObject(property, `${where} ${name}`), `${where} ${name}`)]]))]
|
||||
if (schema['$ref'] !== undefined) found = intersect(found, alternatives(referenced(schema['$ref'], definitions, where), definitions, `${where} ${String(schema['$ref'])}`))
|
||||
for (const branch of branches(schema['allOf'], `${where} allOf`)) found = intersect(found, alternatives(branch, definitions, `${where} allOf`))
|
||||
if (schema['anyOf'] !== undefined) found = intersect(found, branches(schema['anyOf'], `${where} anyOf`).flatMap((branch) => alternatives(branch, definitions, `${where} anyOf`)))
|
||||
return found
|
||||
}
|
||||
|
||||
function readSchema(schema: SchemaObject, where: string): SchemaObject {
|
||||
const unread = Object.keys(schema).find((keyword) => !readKeywords.includes(keyword))
|
||||
if (unread !== undefined) return assert.fail(`${where}: the schema holds the keyword ${unread}, which the gate does not read`)
|
||||
const extra = schema['additionalProperties']
|
||||
return extra === undefined || typeof extra === 'boolean' ? schema : assert.fail(`${where}: additionalProperties holds a schema, which the gate does not read`)
|
||||
}
|
||||
|
||||
function intersect(left: readonly Properties[], right: readonly Properties[]): Properties[] {
|
||||
return left.flatMap((own) =>
|
||||
right.map((other) => {
|
||||
const merged = new Map(own)
|
||||
for (const [name, schemas] of other) merged.set(name, [...(merged.get(name) ?? []), ...schemas])
|
||||
return merged
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
function referenced(reference: unknown, definitions: SchemaObject, where: string): SchemaObject {
|
||||
const name = typeof reference === 'string' && reference.startsWith(definitionReference) ? reference.slice(definitionReference.length) : undefined
|
||||
return schemaObject(name === undefined ? undefined : definitions[name], `${where}: the reference ${String(reference)}`)
|
||||
}
|
||||
|
||||
function branches(value: unknown, where: string): SchemaObject[] {
|
||||
if (value === undefined) return []
|
||||
return Array.isArray(value) ? value.map((branch, index) => schemaObject(branch, `${where} ${index}`)) : assert.fail(`${where} is no array of schemas`)
|
||||
}
|
||||
|
||||
function enumStrings(schema: SchemaObject, where: string): string[] {
|
||||
const values = schema['enum']
|
||||
return Array.isArray(values) ? values.filter((value: unknown): value is string => typeof value === 'string') : assert.fail(`${where}: the type property holds no enum naming the type`)
|
||||
}
|
||||
|
||||
function propertyKinds(property: SchemaObject, where: string): AttributeKind[] {
|
||||
const type = property['type']
|
||||
if (type === 'boolean') return ['boolean']
|
||||
if (type === 'integer' || type === 'number') return ['number']
|
||||
if (type === 'string') return ['string']
|
||||
if (type === 'array' || type === 'object') return ['json']
|
||||
if (type !== undefined) return assert.fail(`${where}: the schema types it ${JSON.stringify(type)}, which reads as no attribute kind`)
|
||||
const combinator = ['$ref', 'allOf', 'anyOf'].find((keyword) => property[keyword] !== undefined)
|
||||
if (combinator !== undefined) return assert.fail(`${where}: the schema holds the keyword ${combinator}, which the gate reads as no attribute kind`)
|
||||
const values = property['enum']
|
||||
return Array.isArray(values) ? values.map(valueKind) : ['json']
|
||||
}
|
||||
|
||||
function valueKind(value: unknown): AttributeKind {
|
||||
if (typeof value === 'boolean') return 'boolean'
|
||||
if (typeof value === 'number') return 'number'
|
||||
if (typeof value === 'string') return 'string'
|
||||
return 'json'
|
||||
}
|
||||
|
||||
function schemaObject(value: unknown, where: string): SchemaObject {
|
||||
return isSchemaObject(value) ? value : assert.fail(`${where} is no JSON Schema object`)
|
||||
}
|
||||
|
||||
function isSchemaObject(value: unknown): value is SchemaObject {
|
||||
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
||||
}
|
||||
+21
-9
@@ -52,8 +52,8 @@ export function attributeNestingMessage(key: string, type: string, levels: numbe
|
||||
}
|
||||
|
||||
export function carriesOnly(node: AdfNode, attributes: readonly string[]): boolean {
|
||||
if ((node.marks ?? []).length > 0 || node.text !== undefined) return false
|
||||
return holdsOnly(node.attrs ?? {}, attributes)
|
||||
if (nodeMarks(node).length > 0 || node.text !== undefined) return false
|
||||
return holdsOnly(nodeAttrs(node), attributes)
|
||||
}
|
||||
|
||||
// Depth is the walks' business, not the shape's: the guard waves a deep document through as blocks and marks do.
|
||||
@@ -72,6 +72,18 @@ export function isAdfMark(value: unknown): value is AdfMark {
|
||||
return !('attrs' in value) || isAttributes(value['attrs'])
|
||||
}
|
||||
|
||||
export function nodeAttrs(node: { attrs?: AdfAttributes }): Readonly<AdfAttributes> {
|
||||
return node.attrs ?? {}
|
||||
}
|
||||
|
||||
export function nodeContent(node: { content?: AdfNode[] }): readonly AdfNode[] {
|
||||
return node.content ?? []
|
||||
}
|
||||
|
||||
export function nodeMarks(node: { marks?: AdfMark[] }): readonly AdfMark[] {
|
||||
return node.marks ?? []
|
||||
}
|
||||
|
||||
function isNodeArray(value: readonly unknown[]): value is readonly AdfNode[] {
|
||||
const pending: unknown[] = [...value]
|
||||
while (pending.length > 0) {
|
||||
@@ -95,23 +107,23 @@ function nestingFault(nodes: readonly AdfNode[]): ConvertFault | undefined {
|
||||
while (pending.length > 0) {
|
||||
const node = pending.pop()
|
||||
if (node === undefined) continue
|
||||
const fault = attributesFault(node.attrs, node.type) ?? marksFault(node.marks)
|
||||
const fault = attributesFault(nodeAttrs(node), node.type) ?? marksFault(nodeMarks(node))
|
||||
if (fault !== undefined) return fault
|
||||
pending.push(...(node.content ?? []))
|
||||
pending.push(...nodeContent(node))
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
function marksFault(marks: readonly AdfMark[] | undefined): ConvertFault | undefined {
|
||||
for (const mark of marks ?? []) {
|
||||
const fault = attributesFault(mark.attrs, mark.type, markAttributeNesting)
|
||||
function marksFault(marks: readonly AdfMark[]): ConvertFault | undefined {
|
||||
for (const mark of marks) {
|
||||
const fault = attributesFault(nodeAttrs(mark), mark.type, markAttributeNesting)
|
||||
if (fault !== undefined) return fault
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
function attributesFault(attrs: AdfAttributes | undefined, type: string, levels: number = largestNesting): ConvertFault | undefined {
|
||||
for (const [key, value] of Object.entries(attrs ?? {})) {
|
||||
function attributesFault(attrs: AdfAttributes, type: string, levels: number = largestNesting): ConvertFault | undefined {
|
||||
for (const [key, value] of Object.entries(attrs)) {
|
||||
if (overNested(value, levels)) return { code: 'unsupported-nesting-depth', message: attributeNestingMessage(key, type, levels) }
|
||||
}
|
||||
return undefined
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import test from 'node:test'
|
||||
|
||||
import type { AdfNode } from './document.ts'
|
||||
import type { JsonValue } from '../json-value.ts'
|
||||
import { toEditorNormal } from './editor-normal.ts'
|
||||
|
||||
test('merges adjacent text nodes carrying identical marks, at every level', () => {
|
||||
const content: AdfNode[] = [
|
||||
{ text: 'a', type: 'text' },
|
||||
{ text: 'b', type: 'text' },
|
||||
{ marks: [{ type: 'strong' }], text: 'c', type: 'text' },
|
||||
{ marks: [{ attrs: {}, type: 'strong' }], text: 'd', type: 'text' },
|
||||
{ type: 'hardBreak' },
|
||||
{ text: 'e', type: 'text' },
|
||||
{ attrs: { localId: '01a0a06b-5281-7f27-9022-8d3a74b0ab0d' }, text: 'f', type: 'text' },
|
||||
{ text: 'g', type: 'text' },
|
||||
{ attrs: {}, text: 'h', type: 'text' },
|
||||
]
|
||||
assert.deepEqual(toEditorNormal({ content: [{ attrs: { panelType: 'info' }, content: [{ content, type: 'paragraph' }], type: 'panel' }], type: 'doc', version: 1 }), {
|
||||
content: [
|
||||
{
|
||||
attrs: { panelType: 'info' },
|
||||
content: [
|
||||
{
|
||||
content: [
|
||||
{ text: 'ab', type: 'text' },
|
||||
{ marks: [{ type: 'strong' }], text: 'cd', type: 'text' },
|
||||
{ type: 'hardBreak' },
|
||||
{ text: 'e', type: 'text' },
|
||||
{ attrs: { localId: '01a0a06b-5281-7f27-9022-8d3a74b0ab0d' }, text: 'f', type: 'text' },
|
||||
{ text: 'gh', type: 'text' },
|
||||
],
|
||||
type: 'paragraph',
|
||||
},
|
||||
],
|
||||
type: 'panel',
|
||||
},
|
||||
],
|
||||
type: 'doc',
|
||||
version: 1,
|
||||
})
|
||||
})
|
||||
|
||||
test('reads negative zero as zero, as JSON does', () => {
|
||||
const marks = [{ attrs: { size: -0 }, type: 'border' }]
|
||||
assert.deepEqual(
|
||||
toEditorNormal({ content: [{ attrs: { a: -0, b: [{ c: -0 }, null, 'd'] }, content: [{ marks, text: 'x', type: 'text' }], type: 'paragraph' }], type: 'doc', version: -0 }),
|
||||
{ content: [{ attrs: { a: 0, b: [{ c: 0 }, null, 'd'] }, content: [{ marks: [{ attrs: { size: 0 }, type: 'border' }], text: 'x', type: 'text' }], type: 'paragraph' }], type: 'doc', version: 0 },
|
||||
)
|
||||
})
|
||||
|
||||
test('reads an empty attrs object, marks array or content array as the absent key', () => {
|
||||
const paragraph: AdfNode = { attrs: {}, content: [{ attrs: {}, marks: [], text: 'a', type: 'text' }, { marks: [{ attrs: {}, type: 'em' }], text: 'b', type: 'text' }], marks: [], type: 'paragraph' }
|
||||
assert.deepEqual(toEditorNormal({ content: [paragraph, { content: [], type: 'rule' }], type: 'doc', version: 1 }), {
|
||||
content: [{ content: [{ text: 'a', type: 'text' }, { marks: [{ type: 'em' }], text: 'b', type: 'text' }], type: 'paragraph' }, { type: 'rule' }],
|
||||
type: 'doc',
|
||||
version: 1,
|
||||
})
|
||||
assert.deepEqual(toEditorNormal({ content: [], type: 'doc', version: 1 }), { type: 'doc', version: 1 })
|
||||
})
|
||||
|
||||
test('normalizes blocks and mark attributes nesting far past the levels a recursive walk survives', () => {
|
||||
const levels = 100000
|
||||
let node: AdfNode = { content: [], type: 'paragraph' }
|
||||
for (let level = 0; level < levels; level += 1) node = { content: [node], type: 'blockquote' }
|
||||
let normal = toEditorNormal({ content: [node], type: 'doc', version: 1 }).content?.[0]
|
||||
let depth = 0
|
||||
for (; normal?.content !== undefined; depth += 1) normal = normal.content[0]
|
||||
assert.equal(depth, levels)
|
||||
assert.deepEqual(normal, { type: 'paragraph' })
|
||||
let deep: JsonValue = 1
|
||||
for (let level = 0; level < 2 * levels; level += 1) deep = [deep]
|
||||
const marks = [{ attrs: { deep }, type: 'textColor' }]
|
||||
const merged = toEditorNormal({ content: [{ content: [{ marks, text: 'a', type: 'text' }, { marks, text: 'b', type: 'text' }], type: 'paragraph' }], type: 'doc', version: 1 })
|
||||
assert.deepEqual(merged.content?.[0]?.content?.map((text) => text.text), ['ab'])
|
||||
})
|
||||
@@ -1,16 +1,21 @@
|
||||
import type { AdfMark, AdfNode } from './document.ts'
|
||||
import type { AdfAttributes, AdfDocument, AdfMark, AdfNode } from './document.ts'
|
||||
import type { JsonValue } from '../json-value.ts'
|
||||
import { nodeAttrs, nodeContent, nodeMarks } from './document.ts'
|
||||
import { serializeCanonicalJson } from '../canonical-json.ts'
|
||||
|
||||
type JsonContainer = JsonValue[] | { [key: string]: JsonValue }
|
||||
|
||||
type NodeHolder = { content?: AdfNode[] }
|
||||
|
||||
export function sameMark(candidate: AdfMark, mark: AdfMark): boolean {
|
||||
return markKey(candidate) === markKey(mark)
|
||||
}
|
||||
|
||||
// AGENTS.md §2: adjacent text nodes carrying identical marks are one node.
|
||||
export function mergeAdjacentText(nodes: readonly AdfNode[]): AdfNode[] {
|
||||
const merged: AdfNode[] = []
|
||||
for (const node of nodes) {
|
||||
const previous = merged[merged.length - 1]
|
||||
if (previous !== undefined && previous.type === 'text' && node.type === 'text' && sameMarks(previous, node)) {
|
||||
if (previous !== undefined && mergesText(previous) && mergesText(node) && sameMarks(previous, node)) {
|
||||
merged[merged.length - 1] = { ...previous, text: `${previous.text ?? ''}${node.text ?? ''}` }
|
||||
continue
|
||||
}
|
||||
@@ -19,8 +24,61 @@ export function mergeAdjacentText(nodes: readonly AdfNode[]): AdfNode[] {
|
||||
return merged
|
||||
}
|
||||
|
||||
export function toEditorNormal(document: AdfDocument): AdfDocument {
|
||||
const normal: AdfDocument = { type: document.type, version: Object.is(document.version, -0) ? 0 : document.version }
|
||||
const pending: { holder: NodeHolder; source: NodeHolder }[] = [{ holder: normal, source: document }]
|
||||
for (let entry = pending.pop(); entry !== undefined; entry = pending.pop()) {
|
||||
const content = mergeAdjacentText(nodeContent(entry.source))
|
||||
if (content.length === 0) continue
|
||||
entry.holder.content = content.map((source) => {
|
||||
const holder = normalNode(source)
|
||||
pending.push({ holder, source })
|
||||
return holder
|
||||
})
|
||||
}
|
||||
return normal
|
||||
}
|
||||
|
||||
function normalNode(node: AdfNode): AdfNode {
|
||||
const normal: AdfNode = { type: node.type }
|
||||
const attrs = normalAttributes(nodeAttrs(node))
|
||||
if (attrs !== undefined) normal.attrs = attrs
|
||||
const marks = nodeMarks(node).map(normalMark)
|
||||
if (marks.length > 0) normal.marks = marks
|
||||
if (node.text !== undefined) normal.text = node.text
|
||||
return normal
|
||||
}
|
||||
|
||||
function normalMark(mark: AdfMark): AdfMark {
|
||||
const attrs = normalAttributes(nodeAttrs(mark))
|
||||
return attrs === undefined ? { type: mark.type } : { attrs, type: mark.type }
|
||||
}
|
||||
|
||||
function normalAttributes(attrs: AdfAttributes): AdfAttributes | undefined {
|
||||
if (Object.keys(attrs).length === 0) return undefined
|
||||
const normal = { ...attrs }
|
||||
const pending: JsonContainer[] = [normal]
|
||||
for (let held = pending.pop(); held !== undefined; held = pending.pop()) {
|
||||
if (Array.isArray(held)) for (const [index, value] of held.entries()) held[index] = normalValue(value, pending)
|
||||
else for (const [key, value] of Object.entries(held)) held[key] = normalValue(value, pending)
|
||||
}
|
||||
return normal
|
||||
}
|
||||
|
||||
function normalValue(value: JsonValue, pending: JsonContainer[]): JsonValue {
|
||||
if (Object.is(value, -0)) return 0
|
||||
if (value === null || typeof value !== 'object') return value
|
||||
const copy = Array.isArray(value) ? [...value] : { ...value }
|
||||
pending.push(copy)
|
||||
return copy
|
||||
}
|
||||
|
||||
function mergesText(node: AdfNode): boolean {
|
||||
return node.type === 'text' && Object.keys(nodeAttrs(node)).length === 0
|
||||
}
|
||||
|
||||
function sameMarks(previous: AdfNode, node: AdfNode): boolean {
|
||||
return marksKey(previous.marks ?? []) === marksKey(node.marks ?? [])
|
||||
return marksKey(nodeMarks(previous)) === marksKey(nodeMarks(node))
|
||||
}
|
||||
|
||||
function marksKey(marks: readonly AdfMark[]): string {
|
||||
@@ -28,5 +86,5 @@ function marksKey(marks: readonly AdfMark[]): string {
|
||||
}
|
||||
|
||||
function markKey(mark: AdfMark): string {
|
||||
return `${mark.type} ${serializeCanonicalJson(mark.attrs ?? {}, 'compact')}`
|
||||
return `${mark.type} ${serializeCanonicalJson(nodeAttrs(mark), 'compact')}`
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import test from 'node:test'
|
||||
|
||||
import type { JsonValue } from './json-value.ts'
|
||||
import { serializeCanonicalJson } from './canonical-json.ts'
|
||||
|
||||
test('sorts object keys recursively', () => {
|
||||
@@ -39,3 +40,15 @@ test('leaves non-ASCII raw', () => {
|
||||
test('spells scalars in canonical JSON', () => {
|
||||
assert.equal(serializeCanonicalJson([null, true, false, 0, -1.5, 'a"b'], 'compact'), '[null,true,false,0,-1.5,"a\\"b"]')
|
||||
})
|
||||
|
||||
test('spells a value nesting far past the levels a recursive walk survives', () => {
|
||||
const levels = 200000
|
||||
let array: JsonValue = 1
|
||||
let object: JsonValue = 1
|
||||
for (let level = 0; level < levels; level += 1) {
|
||||
array = [array]
|
||||
object = { a: object }
|
||||
}
|
||||
assert.equal(serializeCanonicalJson(array, 'compact'), `${'['.repeat(levels)}1${']'.repeat(levels)}`)
|
||||
assert.equal(serializeCanonicalJson(object, 'compact'), `${'{"a":'.repeat(levels)}1${'}'.repeat(levels)}`)
|
||||
})
|
||||
|
||||
+33
-19
@@ -2,28 +2,42 @@ import type { JsonValue } from './json-value.ts'
|
||||
|
||||
export type JsonSpelling = 'compact' | 'two-space'
|
||||
|
||||
type Member = { label: string; value: JsonValue }
|
||||
|
||||
type Pending = string | { depth: number; value: JsonValue }
|
||||
|
||||
export function serializeCanonicalJson(value: JsonValue, spelling: JsonSpelling): string {
|
||||
return serialize(value, spelling === 'compact' ? '' : ' ', 0)
|
||||
const indent = spelling === 'compact' ? '' : ' '
|
||||
const text: string[] = []
|
||||
const pending: Pending[] = [{ depth: 0, value }]
|
||||
for (let next = pending.pop(); next !== undefined; next = pending.pop()) {
|
||||
if (typeof next === 'string') {
|
||||
text.push(next)
|
||||
continue
|
||||
}
|
||||
const { depth, value: held } = next
|
||||
if (Array.isArray(held)) schedule(pending, '[', held.map((item) => ({ label: '', value: item })), ']', indent, depth)
|
||||
else if (held !== null && typeof held === 'object') schedule(pending, '{', objectMembers(held, indent), '}', indent, depth)
|
||||
else text.push(JSON.stringify(held))
|
||||
}
|
||||
return text.join('')
|
||||
}
|
||||
|
||||
function serialize(value: JsonValue, indent: string, depth: number): string {
|
||||
if (Array.isArray(value)) {
|
||||
if (value.length === 0) return '[]'
|
||||
const items = value.map((item) => serialize(item, indent, depth + 1))
|
||||
return `[${join(items, indent, depth)}]`
|
||||
}
|
||||
if (value !== null && typeof value === 'object') {
|
||||
const keys = Object.keys(value).sort()
|
||||
if (keys.length === 0) return '{}'
|
||||
const separator = indent === '' ? ':' : ': '
|
||||
const entries = keys.map((key) => `${JSON.stringify(key)}${separator}${serialize(value[key] ?? null, indent, depth + 1)}`)
|
||||
return `{${join(entries, indent, depth)}}`
|
||||
}
|
||||
return JSON.stringify(value)
|
||||
function objectMembers(value: { [key: string]: JsonValue }, indent: string): Member[] {
|
||||
const separator = indent === '' ? ':' : ': '
|
||||
return Object.keys(value)
|
||||
.sort()
|
||||
.map((key) => ({ label: `${JSON.stringify(key)}${separator}`, value: value[key] ?? null }))
|
||||
}
|
||||
|
||||
function join(parts: readonly string[], indent: string, depth: number): string {
|
||||
if (indent === '') return parts.join(',')
|
||||
const inner = `\n${indent.repeat(depth + 1)}`
|
||||
return `${inner}${parts.join(`,${inner}`)}\n${indent.repeat(depth)}`
|
||||
function schedule(pending: Pending[], open: string, members: readonly Member[], close: string, indent: string, depth: number): void {
|
||||
if (members.length === 0) {
|
||||
pending.push(`${open}${close}`)
|
||||
return
|
||||
}
|
||||
const inner = indent === '' ? '' : `\n${indent.repeat(depth + 1)}`
|
||||
const scheduled: Pending[] = []
|
||||
for (const [index, member] of members.entries()) scheduled.push(`${index === 0 ? open : ','}${inner}${member.label}`, { depth: depth + 1, value: member.value })
|
||||
scheduled.push(indent === '' ? close : `\n${indent.repeat(depth)}${close}`)
|
||||
for (const item of scheduled.reverse()) pending.push(item)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user