diff --git a/.changeset/apply-markdown-edit.md b/.changeset/apply-markdown-edit.md deleted file mode 100644 index f54337c31..000000000 --- a/.changeset/apply-markdown-edit.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -'@portabletext/markdown': minor ---- - -feat: add `applyMarkdownEdit` for key-preserving markdown edits - -Edit Portable Text as markdown without losing identity: serialize a stored value, let anything edit the markdown, and convert it back with the stored `_key`s restored to everything the edit did not replace. - -```ts -import {applyMarkdownEdit, portableTextToMarkdown} from '@portabletext/markdown' - -const markdown = portableTextToMarkdown(storedPortableText, {schema}) -// ... the markdown gets edited ... -const edited = applyMarkdownEdit(storedPortableText, editedMarkdown, { - schema, // governs both conversion directions - serialize: {}, // must match the options that produced the markdown -}) -``` - -Keys follow the edit the way they would in an editor: unchanged and moved blocks keep their keys, rewriting a block in place keeps its key like typing over it, a split keeps the key on the first fragment, a merge on the first block, and a typo fix lands as a text change on the same span, annotation keys included. What markdown cannot express comes back on adopted content: custom fields and styles, decorators without syntax, empty paragraphs. When evidence runs out, a block gets a fresh key rather than a wrong one, and a `json:object` payload's own key never re-keys stored content. - -The options bag is the exported `ApplyMarkdownEditOptions`: one top-level `schema` governs both conversion directions, and `deserialize`/`serialize` take the two converters' remaining options, with new keys coming from `deserialize.keyGenerator`. The README's `applyMarkdownEdit` section carries the full contract: what keeps its key, what gets restored, when keys reset, and the concurrent-edit rule. diff --git a/.changeset/gate-default-renderers-on-schema.md b/.changeset/gate-default-renderers-on-schema.md deleted file mode 100644 index 347d252eb..000000000 --- a/.changeset/gate-default-renderers-on-schema.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -'@portabletext/markdown': minor ---- - -feat: gate the default markdown renderers on the schema in `portableTextToMarkdown` - -`portableTextToMarkdown` now accepts an optional `schema`. When set, each built-in type renderer (`callout`, `code`, `horizontal-rule`, `html`, `image`, `table`) runs only when the schema declares that type at the matching position (`blockObjects` for a block, `inlineObjects` for an inline object); an undeclared type falls back to `unknownType`, whose default output is a `json:object` fence (or tagged code span, inline) that reparses back to the same value under the same schema. Renderers you pass in `types` are never gated. The gate checks the type name only, so declare each type with its fields: `markdownToPortableText` cannot rebuild a value from a fieldless declaration. Omit `schema` and every default renderer stays active, as before. - - -```ts -import {compileSchema, defineSchema} from '@portabletext/schema' -import {portableTextToMarkdown} from '@portabletext/markdown' - -const schema = compileSchema( - defineSchema({ - blockObjects: [ - { - name: 'code', - fields: [ - {name: 'code', type: 'string'}, - {name: 'language', type: 'string'}, - ], - }, - ], - }), -) - -portableTextToMarkdown(blocks, {schema}) -// a `code` block renders as a fenced code block; a `table` block -// (undeclared) renders as a `json:object` fence instead -``` - -Pass the same schema to `markdownToPortableText` to keep the round trip consistent. diff --git a/.changeset/guard-unparsable-image-src.md b/.changeset/guard-unparsable-image-src.md deleted file mode 100644 index 650b9eeba..000000000 --- a/.changeset/guard-unparsable-image-src.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@portabletext/markdown': patch ---- - -fix: serialize images the parser would refuse as `json:object` fences - -An `image` whose `src` a Markdown parser would refuse (a `data:` URI outside `png`/`gif`/`jpeg`/`webp`, or a `javascript:`/`vbscript:`/`file:` URI) previously serialized as `![alt](src)` anyway; reparsing that markdown turned the image into literal text instead of an image object, destroying it. Such an image now serializes as a `json:object` fence (or, inline, a tagged code span), which reparses back to the identical image value. Images with an accepted `src` are unaffected. diff --git a/.changeset/inline-carrier-in-table-cells.md b/.changeset/inline-carrier-in-table-cells.md deleted file mode 100644 index b0662a63a..000000000 --- a/.changeset/inline-carrier-in-table-cells.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@portabletext/markdown': patch ---- - -fix: use the inline `json:object` carrier for objects inside table cells - -In `portableTextToMarkdown`, an object inside a table cell that renders through the `json:object` carrier now uses the carrier's single-line inline form instead of a block fence squashed with `
`. - -In `markdownToPortableText`, a carrier object standing alone in a table cell is placed by the schema: it comes back at block position in the cell (`cell.value`), unless the schema declares the type inline-only, in which case it stays an inline child of the cell's text block. The same placement rule now governs standalone images, so under a schema without a block-level `image`, an image alone in a cell stays inline (reported as `image-block-to-inline`) instead of being lifted to block position. - -Together the two sides make objects in table cells survive the serialize-edit-reparse round trip. diff --git a/.changeset/onreconciliation-report.md b/.changeset/onreconciliation-report.md deleted file mode 100644 index 2c9a925a5..000000000 --- a/.changeset/onreconciliation-report.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -'@portabletext/markdown': minor ---- - -feat: add `onReconciliation` report to `applyMarkdownEdit` - -`applyMarkdownEdit` can now report what it did with every key. A preserved key means "this node in the returned value wears its stored key". A renamed key means "this key was renamed to stay unique among siblings". A key fallback marks a place that got fresh keys instead of a guess. Concretely, for a stored value of two paragraphs around a custom object, where the edit fixes one typo: - -```ts -import {applyMarkdownEdit} from '@portabletext/markdown' - -// stored: [block b1 'Our pick:', product p1, block b2 'Ships tomorow.'] -// edited markdown: the same document with 'tomorow' -> 'tomorrow' -applyMarkdownEdit(stored, editedMarkdown, { - onReconciliation: (report) => { - // report is: - // { - // keyMatching: 'performed', - // preservedKeys: [ - // {basis: 'content-unchanged', key: 'b1', path: [{_key: 'b1'}]}, - // {basis: 'content-unchanged', key: 's1', path: [{_key: 'b1'}, 'children', {_key: 's1'}]}, - // {basis: 'content-unchanged', key: 'p1', path: [{_key: 'p1'}]}, - // {basis: 'same-position', key: 'b2', path: [{_key: 'b2'}]}, - // {basis: 'same-position', key: 's2', path: [{_key: 'b2'}, 'children', {_key: 's2'}]}, - // ], - // keyFallbacks: [], - // renamedKeys: [], - // } - // reading it: every stored key survived, at every depth. The - // untouched nodes matched exactly ('content-unchanged'), the edited - // paragraph kept its block and span keys the way typing over it - // in an editor would ('same-position'). Other basis values name - // the other matching methods: 'content-moved', 'content-split', - // 'content-merged', 'similar-content'. - }, -}) -``` - -What the other fields mean when they are not empty: - -- `report.keyMatching === 'skipped'`: key matching did not run, and `report.reason` says why, `'round-trip-mismatch'` (the stored value cannot survive its own serialize→parse round trip) or `'document-too-large'`. The returned value is the plain conversion, every key fresh except the ones `json:object` payloads carry, and the report carries only `reason` and `renamedKeys`. -- `keyFallbacks: [{type: 'ambiguous-region-too-large', keys: [...]}]`: one ambiguous region was too large to gather evidence for, and the listed result blocks fell back to fresh keys. `{type: 'annotation-key-conflict', path}`: one annotation definition kept its fresh key because adopting the stored one would have collided with a sibling. -- `renamedKeys: [{previousKey: 'p1', key: 'k7', path}]`: a key was rewritten to keep siblings unique, most commonly a pasted `json:object` payload duplicating a key that already exists. - -The callback fires exactly once whenever set, synchronously, right before the function returns, a skip included: that is when a caller needs it most. A node absent from `preservedKeys` was not preserved from the stored value, whether it is a fresh key or a `json:object` payload key that carried its own, and the report does not distinguish the two. Every `key` and `path` matches the returned value exactly (a path segment is a string field name, a number array index, or `{_key}` for a keyed element, the same convention as editor paths). - -Which keys survived, every `key` and `path`, `renamedKeys`, and `keyMatching` are facts of that invocation, safe to branch on. A preserved key's `basis` is advisory: near the evidence caps it can vary with machine speed, so never branch on it. The two exported types, `ReconciliationReport` and `ReconciliationKeyPath`, are `@beta`. diff --git a/.changeset/remove-placeholder-on-remote-insert-after-unset.md b/.changeset/remove-placeholder-on-remote-insert-after-unset.md deleted file mode 100644 index 44581d8ac..000000000 --- a/.changeset/remove-placeholder-on-remote-insert-after-unset.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@portabletext/editor': patch ---- - -fix: remove the local placeholder on a remote root `insert` after a self-emitted `unset` - -After clearing the field under a value-mirroring host, a collaborator's block arriving through `patches` no longer leaves an extra empty block in the editor; the editor shows only the collaborator's content. One additional change: an empty block that a host genuinely persisted in the window after the editor's own `unset` is now removed from the editor when a remote root `insert` arrives, until the next value sync restores it; distinguishing it from the editor's own placeholder takes provenance metadata, which is follow-up work. diff --git a/.changeset/root-unset-rematerialization.md b/.changeset/root-unset-rematerialization.md deleted file mode 100644 index 5c230a954..000000000 --- a/.changeset/root-unset-rematerialization.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@portabletext/editor': patch ---- - -fix: re-materialize the field before patches emitted while it is unset - -A behavior replacing the whole value in one action set (a root `unset` followed by an `insert`) previously emitted a patch stream that patch-applying stores could not apply: an `insert` into a field the preceding `unset` had removed. Consumers saw `Cannot apply deep operations on primitive values`. - -One narrow behavioral fix rides along: clearing the editor through a behavior-raised root `unset` now re-materializes the field on the next edit under a value-mirroring host, the same way clearing it with the keyboard already did. diff --git a/.changeset/skip-empty-block-output.md b/.changeset/skip-empty-block-output.md deleted file mode 100644 index 0af60dd07..000000000 --- a/.changeset/skip-empty-block-output.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@portabletext/markdown': patch ---- - -fix: emit no blank lines for empty text blocks - -A block that renders to the empty string (an empty or whitespace-only text block, or a custom renderer returning `''`) no longer leaves blank lines in `portableTextToMarkdown`'s output: `[h1 'foo', empty block, 'bar']` now serializes to `# foo\n\nbar` instead of `# foo\n\n\n\nbar`. A dropped block never survived reparsing anyway, and a custom `blockSpacing` callback now sees the pair of blocks that actually end up adjacent, never an invisible one. Two spacing consequences show in rendered HTML: two blockquotes separated only by an empty block now join into one quote with a paragraph break, and a list whose blank lines came only from empty blocks goes tight, since a skipped block no longer counts toward looseness. - -The same filter runs inside containers: callout and structured-blockquote content joins skip empty blocks, so no more blank quote-prefixed lines. In list items, the marker line goes to the first block that renders output, with two exceptions that keep the markdown reparseable: a multi-line block (a code fence, a table) keeps its later lines indented inside the item instead of escaping the list at column 0, and a nested list or, on a task item, any non-text block stays indented below a bare marker, because after `- [x] ` (or fused with `- `) it would reparse as plain words. diff --git a/.changeset/unique-minted-keys.md b/.changeset/unique-minted-keys.md deleted file mode 100644 index a9913bfb8..000000000 --- a/.changeset/unique-minted-keys.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@portabletext/markdown': patch ---- - -fix: mint sibling-unique keys even from a colliding `keyGenerator` - -`markdownToPortableText` (and everything built on it) now guards the supplied `keyGenerator`: a generator that returns an already-minted key is retried, then deterministically suffixed, so one conversion can never mint the same `_key` twice. A repeated key did not just duplicate identity, it made annotation ownership ambiguous: two mark definitions sharing a key leave every referencing span attributable to either, which no later repair can resolve. Well-behaved generators are unaffected. diff --git a/packages/editor/CHANGELOG.md b/packages/editor/CHANGELOG.md index a65a9ed6d..2e3f5000e 100644 --- a/packages/editor/CHANGELOG.md +++ b/packages/editor/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 8.1.4 + +### Patch Changes + +- [#3257](https://github.com/portabletext/editor/pull/3257) [`df4b4ac`](https://github.com/portabletext/editor/commit/df4b4aca74bda080f21a7b0272d1cb024d24a495) Thanks [@christianhg](https://github.com/christianhg)! - fix: remove the local placeholder on a remote root `insert` after a self-emitted `unset` + + After clearing the field under a value-mirroring host, a collaborator's block arriving through `patches` no longer leaves an extra empty block in the editor; the editor shows only the collaborator's content. One additional change: an empty block that a host genuinely persisted in the window after the editor's own `unset` is now removed from the editor when a remote root `insert` arrives, until the next value sync restores it; distinguishing it from the editor's own placeholder takes provenance metadata, which is follow-up work. + +- [#3253](https://github.com/portabletext/editor/pull/3253) [`a4bea35`](https://github.com/portabletext/editor/commit/a4bea35a8f0c668b83502c0fd0da0cf9f8e2693e) Thanks [@christianhg](https://github.com/christianhg)! - fix: re-materialize the field before patches emitted while it is unset + + A behavior replacing the whole value in one action set (a root `unset` followed by an `insert`) previously emitted a patch stream that patch-applying stores could not apply: an `insert` into a field the preceding `unset` had removed. Consumers saw `Cannot apply deep operations on primitive values`. + + One narrow behavioral fix rides along: clearing the editor through a behavior-raised root `unset` now re-materializes the field on the next edit under a value-mirroring host, the same way clearing it with the keyboard already did. + ## 8.1.3 ### Patch Changes diff --git a/packages/editor/package.json b/packages/editor/package.json index 2c58babcf..1dbf12144 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -1,6 +1,6 @@ { "name": "@portabletext/editor", - "version": "8.1.3", + "version": "8.1.4", "description": "Portable Text Editor made in React", "keywords": [ "collaborative", diff --git a/packages/markdown/CHANGELOG.md b/packages/markdown/CHANGELOG.md index f89d22b79..1a7f3562c 100644 --- a/packages/markdown/CHANGELOG.md +++ b/packages/markdown/CHANGELOG.md @@ -1,5 +1,128 @@ # @portabletext/markdown +## 2.2.0 + +### Minor Changes + +- [#3242](https://github.com/portabletext/editor/pull/3242) [`e4abe23`](https://github.com/portabletext/editor/commit/e4abe233743736ec15e3bbf3fcb118182beaf49d) Thanks [@christianhg](https://github.com/christianhg)! - feat: add `applyMarkdownEdit` for key-preserving markdown edits + + Edit Portable Text as markdown without losing identity: serialize a stored value, let anything edit the markdown, and convert it back with the stored `_key`s restored to everything the edit did not replace. + + ```ts + import { + applyMarkdownEdit, + portableTextToMarkdown, + } from '@portabletext/markdown' + + const markdown = portableTextToMarkdown(storedPortableText, {schema}) + // ... the markdown gets edited ... + const edited = applyMarkdownEdit(storedPortableText, editedMarkdown, { + schema, // governs both conversion directions + serialize: {}, // must match the options that produced the markdown + }) + ``` + + Keys follow the edit the way they would in an editor: unchanged and moved blocks keep their keys, rewriting a block in place keeps its key like typing over it, a split keeps the key on the first fragment, a merge on the first block, and a typo fix lands as a text change on the same span, annotation keys included. What markdown cannot express comes back on adopted content: custom fields and styles, decorators without syntax, empty paragraphs. When evidence runs out, a block gets a fresh key rather than a wrong one, and a `json:object` payload's own key never re-keys stored content. + + The options bag is the exported `ApplyMarkdownEditOptions`: one top-level `schema` governs both conversion directions, and `deserialize`/`serialize` take the two converters' remaining options, with new keys coming from `deserialize.keyGenerator`. The README's `applyMarkdownEdit` section carries the full contract: what keeps its key, what gets restored, when keys reset, and the concurrent-edit rule. + +- [#3255](https://github.com/portabletext/editor/pull/3255) [`02e2d49`](https://github.com/portabletext/editor/commit/02e2d4918e7c0a55e974f90e414a3b53aaf3b199) Thanks [@christianhg](https://github.com/christianhg)! - feat: gate the default markdown renderers on the schema in `portableTextToMarkdown` + + `portableTextToMarkdown` now accepts an optional `schema`. When set, each built-in type renderer (`callout`, `code`, `horizontal-rule`, `html`, `image`, `table`) runs only when the schema declares that type at the matching position (`blockObjects` for a block, `inlineObjects` for an inline object); an undeclared type falls back to `unknownType`, whose default output is a `json:object` fence (or tagged code span, inline) that reparses back to the same value under the same schema. Renderers you pass in `types` are never gated. The gate checks the type name only, so declare each type with its fields: `markdownToPortableText` cannot rebuild a value from a fieldless declaration. Omit `schema` and every default renderer stays active, as before. + + ```ts + import {compileSchema, defineSchema} from '@portabletext/schema' + import {portableTextToMarkdown} from '@portabletext/markdown' + + const schema = compileSchema( + defineSchema({ + blockObjects: [ + { + name: 'code', + fields: [ + {name: 'code', type: 'string'}, + {name: 'language', type: 'string'}, + ], + }, + ], + }), + ) + + portableTextToMarkdown(blocks, {schema}) + // a `code` block renders as a fenced code block; a `table` block + // (undeclared) renders as a `json:object` fence instead + ``` + + Pass the same schema to `markdownToPortableText` to keep the round trip consistent. + +- [#3266](https://github.com/portabletext/editor/pull/3266) [`fc8e2a4`](https://github.com/portabletext/editor/commit/fc8e2a4f9b3ce355bebdcf0a650aee51ea7a2001) Thanks [@christianhg](https://github.com/christianhg)! - feat: add `onReconciliation` report to `applyMarkdownEdit` + + `applyMarkdownEdit` can now report what it did with every key. A preserved key means "this node in the returned value wears its stored key". A renamed key means "this key was renamed to stay unique among siblings". A key fallback marks a place that got fresh keys instead of a guess. Concretely, for a stored value of two paragraphs around a custom object, where the edit fixes one typo: + + ```ts + import {applyMarkdownEdit} from '@portabletext/markdown' + + // stored: [block b1 'Our pick:', product p1, block b2 'Ships tomorow.'] + // edited markdown: the same document with 'tomorow' -> 'tomorrow' + applyMarkdownEdit(stored, editedMarkdown, { + onReconciliation: (report) => { + // report is: + // { + // keyMatching: 'performed', + // preservedKeys: [ + // {basis: 'content-unchanged', key: 'b1', path: [{_key: 'b1'}]}, + // {basis: 'content-unchanged', key: 's1', path: [{_key: 'b1'}, 'children', {_key: 's1'}]}, + // {basis: 'content-unchanged', key: 'p1', path: [{_key: 'p1'}]}, + // {basis: 'same-position', key: 'b2', path: [{_key: 'b2'}]}, + // {basis: 'same-position', key: 's2', path: [{_key: 'b2'}, 'children', {_key: 's2'}]}, + // ], + // keyFallbacks: [], + // renamedKeys: [], + // } + // reading it: every stored key survived, at every depth. The + // untouched nodes matched exactly ('content-unchanged'), the edited + // paragraph kept its block and span keys the way typing over it + // in an editor would ('same-position'). Other basis values name + // the other matching methods: 'content-moved', 'content-split', + // 'content-merged', 'similar-content'. + }, + }) + ``` + + What the other fields mean when they are not empty: + + - `report.keyMatching === 'skipped'`: key matching did not run, and `report.reason` says why, `'round-trip-mismatch'` (the stored value cannot survive its own serialize→parse round trip) or `'document-too-large'`. The returned value is the plain conversion, every key fresh except the ones `json:object` payloads carry, and the report carries only `reason` and `renamedKeys`. + - `keyFallbacks: [{type: 'ambiguous-region-too-large', keys: [...]}]`: one ambiguous region was too large to gather evidence for, and the listed result blocks fell back to fresh keys. `{type: 'annotation-key-conflict', path}`: one annotation definition kept its fresh key because adopting the stored one would have collided with a sibling. + - `renamedKeys: [{previousKey: 'p1', key: 'k7', path}]`: a key was rewritten to keep siblings unique, most commonly a pasted `json:object` payload duplicating a key that already exists. + + The callback fires exactly once whenever set, synchronously, right before the function returns, a skip included: that is when a caller needs it most. A node absent from `preservedKeys` was not preserved from the stored value, whether it is a fresh key or a `json:object` payload key that carried its own, and the report does not distinguish the two. Every `key` and `path` matches the returned value exactly (a path segment is a string field name, a number array index, or `{_key}` for a keyed element, the same convention as editor paths). + + Which keys survived, every `key` and `path`, `renamedKeys`, and `keyMatching` are facts of that invocation, safe to branch on. A preserved key's `basis` is advisory: near the evidence caps it can vary with machine speed, so never branch on it. The two exported types, `ReconciliationReport` and `ReconciliationKeyPath`, are `@beta`. + +### Patch Changes + +- [#3265](https://github.com/portabletext/editor/pull/3265) [`3b2d278`](https://github.com/portabletext/editor/commit/3b2d27847eb5139b2d50dc9f3d962167342c2b91) Thanks [@christianhg](https://github.com/christianhg)! - fix: serialize images the parser would refuse as `json:object` fences + + An `image` whose `src` a Markdown parser would refuse (a `data:` URI outside `png`/`gif`/`jpeg`/`webp`, or a `javascript:`/`vbscript:`/`file:` URI) previously serialized as `![alt](src)` anyway; reparsing that markdown turned the image into literal text instead of an image object, destroying it. Such an image now serializes as a `json:object` fence (or, inline, a tagged code span), which reparses back to the identical image value. Images with an accepted `src` are unaffected. + +- [#3255](https://github.com/portabletext/editor/pull/3255) [`d39146e`](https://github.com/portabletext/editor/commit/d39146ee48a23bad4a6c312863e76ff389a1bc2c) Thanks [@christianhg](https://github.com/christianhg)! - fix: use the inline `json:object` carrier for objects inside table cells + + In `portableTextToMarkdown`, an object inside a table cell that renders through the `json:object` carrier now uses the carrier's single-line inline form instead of a block fence squashed with `
`. + + In `markdownToPortableText`, a carrier object standing alone in a table cell is placed by the schema: it comes back at block position in the cell (`cell.value`), unless the schema declares the type inline-only, in which case it stays an inline child of the cell's text block. The same placement rule now governs standalone images, so under a schema without a block-level `image`, an image alone in a cell stays inline (reported as `image-block-to-inline`) instead of being lifted to block position. + + Together the two sides make objects in table cells survive the serialize-edit-reparse round trip. + +- [#3264](https://github.com/portabletext/editor/pull/3264) [`deccf84`](https://github.com/portabletext/editor/commit/deccf846fb41efbcae9b6dccafbfd6cd101930a1) Thanks [@christianhg](https://github.com/christianhg)! - fix: emit no blank lines for empty text blocks + + A block that renders to the empty string (an empty or whitespace-only text block, or a custom renderer returning `''`) no longer leaves blank lines in `portableTextToMarkdown`'s output: `[h1 'foo', empty block, 'bar']` now serializes to `# foo\n\nbar` instead of `# foo\n\n\n\nbar`. A dropped block never survived reparsing anyway, and a custom `blockSpacing` callback now sees the pair of blocks that actually end up adjacent, never an invisible one. Two spacing consequences show in rendered HTML: two blockquotes separated only by an empty block now join into one quote with a paragraph break, and a list whose blank lines came only from empty blocks goes tight, since a skipped block no longer counts toward looseness. + + The same filter runs inside containers: callout and structured-blockquote content joins skip empty blocks, so no more blank quote-prefixed lines. In list items, the marker line goes to the first block that renders output, with two exceptions that keep the markdown reparseable: a multi-line block (a code fence, a table) keeps its later lines indented inside the item instead of escaping the list at column 0, and a nested list or, on a task item, any non-text block stays indented below a bare marker, because after `- [x] ` (or fused with `- `) it would reparse as plain words. + +- [#3242](https://github.com/portabletext/editor/pull/3242) [`e4abe23`](https://github.com/portabletext/editor/commit/e4abe233743736ec15e3bbf3fcb118182beaf49d) Thanks [@christianhg](https://github.com/christianhg)! - fix: mint sibling-unique keys even from a colliding `keyGenerator` + + `markdownToPortableText` (and everything built on it) now guards the supplied `keyGenerator`: a generator that returns an already-minted key is retried, then deterministically suffixed, so one conversion can never mint the same `_key` twice. A repeated key did not just duplicate identity, it made annotation ownership ambiguous: two mark definitions sharing a key leave every referencing span attributable to either, which no later repair can resolve. Well-behaved generators are unaffected. + ## 2.1.0 ### Minor Changes diff --git a/packages/markdown/package.json b/packages/markdown/package.json index e57be44ad..52ce03c37 100644 --- a/packages/markdown/package.json +++ b/packages/markdown/package.json @@ -1,6 +1,6 @@ { "name": "@portabletext/markdown", - "version": "2.1.0", + "version": "2.2.0", "description": "Convert Portable Text to and from Markdown", "keywords": [ "markdown", diff --git a/packages/plugin-character-pair-decorator/CHANGELOG.md b/packages/plugin-character-pair-decorator/CHANGELOG.md index 2819d56c3..207cfb13b 100644 --- a/packages/plugin-character-pair-decorator/CHANGELOG.md +++ b/packages/plugin-character-pair-decorator/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 9.0.9 + +### Patch Changes + +- Updated dependencies []: + - @portabletext/plugin-input-rule@7.0.9 + ## 9.0.8 ### Patch Changes diff --git a/packages/plugin-character-pair-decorator/package.json b/packages/plugin-character-pair-decorator/package.json index e0a89841d..f5894cb06 100644 --- a/packages/plugin-character-pair-decorator/package.json +++ b/packages/plugin-character-pair-decorator/package.json @@ -1,6 +1,6 @@ { "name": "@portabletext/plugin-character-pair-decorator", - "version": "9.0.8", + "version": "9.0.9", "description": "Automatically match a pair of characters and decorate the text in between", "keywords": [ "behaviors", diff --git a/packages/plugin-dnd/CHANGELOG.md b/packages/plugin-dnd/CHANGELOG.md index bc5434975..845233672 100644 --- a/packages/plugin-dnd/CHANGELOG.md +++ b/packages/plugin-dnd/CHANGELOG.md @@ -1,5 +1,11 @@ # @portabletext/plugin-dnd +## 2.0.10 + +### Patch Changes + +- fix(deps): require `@portabletext/editor@^8.1.4` + ## 2.0.9 ### Patch Changes diff --git a/packages/plugin-dnd/package.json b/packages/plugin-dnd/package.json index c0883cfdb..81847fe73 100644 --- a/packages/plugin-dnd/package.json +++ b/packages/plugin-dnd/package.json @@ -1,6 +1,6 @@ { "name": "@portabletext/plugin-dnd", - "version": "2.0.9", + "version": "2.0.10", "description": "Track the drop position during drag and drop for custom drop indicators", "keywords": [ "dnd", diff --git a/packages/plugin-emoji-picker/CHANGELOG.md b/packages/plugin-emoji-picker/CHANGELOG.md index 6b185d5ac..7125e60a5 100644 --- a/packages/plugin-emoji-picker/CHANGELOG.md +++ b/packages/plugin-emoji-picker/CHANGELOG.md @@ -1,5 +1,12 @@ # @portabletext/plugin-emoji-picker +## 8.0.9 + +### Patch Changes + +- Updated dependencies []: + - @portabletext/plugin-input-rule@7.0.9 + ## 8.0.8 ### Patch Changes diff --git a/packages/plugin-emoji-picker/package.json b/packages/plugin-emoji-picker/package.json index bac8ee204..0b780a8af 100644 --- a/packages/plugin-emoji-picker/package.json +++ b/packages/plugin-emoji-picker/package.json @@ -1,6 +1,6 @@ { "name": "@portabletext/plugin-emoji-picker", - "version": "8.0.8", + "version": "8.0.9", "description": "Easily configure an Emoji Picker for the Portable Text Editor", "keywords": [ "emoji-picker", diff --git a/packages/plugin-input-rule/CHANGELOG.md b/packages/plugin-input-rule/CHANGELOG.md index edec49f47..b044b5cb8 100644 --- a/packages/plugin-input-rule/CHANGELOG.md +++ b/packages/plugin-input-rule/CHANGELOG.md @@ -1,5 +1,11 @@ # @portabletext/plugin-input-rule +## 7.0.9 + +### Patch Changes + +- fix(deps): require `@portabletext/editor@^8.1.4` + ## 7.0.8 ### Patch Changes diff --git a/packages/plugin-input-rule/package.json b/packages/plugin-input-rule/package.json index b52c7e18f..7d0662522 100644 --- a/packages/plugin-input-rule/package.json +++ b/packages/plugin-input-rule/package.json @@ -1,6 +1,6 @@ { "name": "@portabletext/plugin-input-rule", - "version": "7.0.8", + "version": "7.0.9", "description": "Easily configure Input Rules in the Portable Text Editor", "keywords": [ "input-rule", diff --git a/packages/plugin-list-index/CHANGELOG.md b/packages/plugin-list-index/CHANGELOG.md index 018b31d55..b5e30459f 100644 --- a/packages/plugin-list-index/CHANGELOG.md +++ b/packages/plugin-list-index/CHANGELOG.md @@ -1,5 +1,11 @@ # @portabletext/plugin-list-index +## 2.0.8 + +### Patch Changes + +- fix(deps): require `@portabletext/editor@^8.1.4` + ## 2.0.7 ### Patch Changes diff --git a/packages/plugin-list-index/package.json b/packages/plugin-list-index/package.json index 9ea520611..dd20ac062 100644 --- a/packages/plugin-list-index/package.json +++ b/packages/plugin-list-index/package.json @@ -1,6 +1,6 @@ { "name": "@portabletext/plugin-list-index", - "version": "2.0.7", + "version": "2.0.8", "description": "Compute the list index of each list item for custom list rendering", "keywords": [ "list-index", diff --git a/packages/plugin-markdown-shortcuts/CHANGELOG.md b/packages/plugin-markdown-shortcuts/CHANGELOG.md index aefcace5f..8a25ab2c1 100644 --- a/packages/plugin-markdown-shortcuts/CHANGELOG.md +++ b/packages/plugin-markdown-shortcuts/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 9.0.9 + +### Patch Changes + +- Updated dependencies []: + - @portabletext/plugin-character-pair-decorator@9.0.9 + - @portabletext/plugin-input-rule@7.0.9 + ## 9.0.8 ### Patch Changes diff --git a/packages/plugin-markdown-shortcuts/package.json b/packages/plugin-markdown-shortcuts/package.json index 9f4760bb7..689403d36 100644 --- a/packages/plugin-markdown-shortcuts/package.json +++ b/packages/plugin-markdown-shortcuts/package.json @@ -1,6 +1,6 @@ { "name": "@portabletext/plugin-markdown-shortcuts", - "version": "9.0.8", + "version": "9.0.9", "description": "Add helpful Markdown shortcuts to the editor", "keywords": [ "behaviors", diff --git a/packages/plugin-one-line/CHANGELOG.md b/packages/plugin-one-line/CHANGELOG.md index c0b784ba8..226e94413 100644 --- a/packages/plugin-one-line/CHANGELOG.md +++ b/packages/plugin-one-line/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 8.0.8 + +### Patch Changes + +- fix(deps): require `@portabletext/editor@^8.1.4` + ## 8.0.7 ### Patch Changes diff --git a/packages/plugin-one-line/package.json b/packages/plugin-one-line/package.json index dea103fdd..7bd8fda9b 100644 --- a/packages/plugin-one-line/package.json +++ b/packages/plugin-one-line/package.json @@ -1,6 +1,6 @@ { "name": "@portabletext/plugin-one-line", - "version": "8.0.7", + "version": "8.0.8", "description": "Restrict the Portable Text Editor to a single line", "keywords": [ "behaviors", diff --git a/packages/plugin-paste-link/CHANGELOG.md b/packages/plugin-paste-link/CHANGELOG.md index 470d0079a..dcccbef30 100644 --- a/packages/plugin-paste-link/CHANGELOG.md +++ b/packages/plugin-paste-link/CHANGELOG.md @@ -1,5 +1,11 @@ # @portabletext/plugin-paste-link +## 5.0.8 + +### Patch Changes + +- fix(deps): require `@portabletext/editor@^8.1.4` + ## 5.0.7 ### Patch Changes diff --git a/packages/plugin-paste-link/package.json b/packages/plugin-paste-link/package.json index b72d8f626..bbf902020 100644 --- a/packages/plugin-paste-link/package.json +++ b/packages/plugin-paste-link/package.json @@ -1,6 +1,6 @@ { "name": "@portabletext/plugin-paste-link", - "version": "5.0.7", + "version": "5.0.8", "description": "Allow pasting links in the Portable Text Editor", "keywords": [ "behaviors", diff --git a/packages/plugin-sdk-value/CHANGELOG.md b/packages/plugin-sdk-value/CHANGELOG.md index eb6dd98e0..b9c81a9d5 100644 --- a/packages/plugin-sdk-value/CHANGELOG.md +++ b/packages/plugin-sdk-value/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 8.1.3 + +### Patch Changes + +- fix(deps): require `@portabletext/editor@^8.1.4` + ## 8.1.2 ### Patch Changes diff --git a/packages/plugin-sdk-value/package.json b/packages/plugin-sdk-value/package.json index ec783318a..b9a03aba7 100644 --- a/packages/plugin-sdk-value/package.json +++ b/packages/plugin-sdk-value/package.json @@ -1,6 +1,6 @@ { "name": "@portabletext/plugin-sdk-value", - "version": "8.1.2", + "version": "8.1.3", "description": "Connect a Portable Text Editor with a Sanity document using the SDK", "keywords": [ "@sanity/sdk", diff --git a/packages/plugin-table/CHANGELOG.md b/packages/plugin-table/CHANGELOG.md index add2f903e..a57b979aa 100644 --- a/packages/plugin-table/CHANGELOG.md +++ b/packages/plugin-table/CHANGELOG.md @@ -1,5 +1,11 @@ # @portabletext/plugin-table +## 2.0.8 + +### Patch Changes + +- fix(deps): require `@portabletext/editor@^8.1.4` + ## 2.0.7 ### Patch Changes diff --git a/packages/plugin-table/package.json b/packages/plugin-table/package.json index 3aab5149f..b2ceea3e3 100644 --- a/packages/plugin-table/package.json +++ b/packages/plugin-table/package.json @@ -1,6 +1,6 @@ { "name": "@portabletext/plugin-table", - "version": "2.0.7", + "version": "2.0.8", "description": "Tables as real Portable Text, edited with spreadsheet-grade selection", "keywords": [ "plugin", diff --git a/packages/plugin-typeahead-picker/CHANGELOG.md b/packages/plugin-typeahead-picker/CHANGELOG.md index 0622e503b..06fe79810 100644 --- a/packages/plugin-typeahead-picker/CHANGELOG.md +++ b/packages/plugin-typeahead-picker/CHANGELOG.md @@ -1,5 +1,12 @@ # @portabletext/plugin-typeahead-picker +## 7.0.9 + +### Patch Changes + +- Updated dependencies []: + - @portabletext/plugin-input-rule@7.0.9 + ## 7.0.8 ### Patch Changes diff --git a/packages/plugin-typeahead-picker/package.json b/packages/plugin-typeahead-picker/package.json index 738761003..e58898f85 100644 --- a/packages/plugin-typeahead-picker/package.json +++ b/packages/plugin-typeahead-picker/package.json @@ -1,6 +1,6 @@ { "name": "@portabletext/plugin-typeahead-picker", - "version": "7.0.8", + "version": "7.0.9", "description": "Build typeahead pickers (emoji, mentions, slash commands) for the Portable Text Editor", "keywords": [ "autocomplete", diff --git a/packages/plugin-typography/CHANGELOG.md b/packages/plugin-typography/CHANGELOG.md index b1adc602a..474ad517d 100644 --- a/packages/plugin-typography/CHANGELOG.md +++ b/packages/plugin-typography/CHANGELOG.md @@ -1,5 +1,12 @@ # @portabletext/plugin-typography +## 9.0.9 + +### Patch Changes + +- Updated dependencies []: + - @portabletext/plugin-input-rule@7.0.9 + ## 9.0.8 ### Patch Changes diff --git a/packages/plugin-typography/package.json b/packages/plugin-typography/package.json index 632763795..6290a9188 100644 --- a/packages/plugin-typography/package.json +++ b/packages/plugin-typography/package.json @@ -1,6 +1,6 @@ { "name": "@portabletext/plugin-typography", - "version": "9.0.8", + "version": "9.0.9", "description": "Automatically transform text to typographic variants", "keywords": [ "plugin", diff --git a/packages/toolbar/CHANGELOG.md b/packages/toolbar/CHANGELOG.md index a930f5d75..c9dccf59e 100644 --- a/packages/toolbar/CHANGELOG.md +++ b/packages/toolbar/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 9.0.9 + +### Patch Changes + +- fix(deps): require `@portabletext/editor@^8.1.4` + ## 9.0.8 ### Patch Changes diff --git a/packages/toolbar/package.json b/packages/toolbar/package.json index 1d57a553e..17c3b048e 100644 --- a/packages/toolbar/package.json +++ b/packages/toolbar/package.json @@ -1,6 +1,6 @@ { "name": "@portabletext/toolbar", - "version": "9.0.8", + "version": "9.0.9", "description": "Utilities for building a toolbar for the Portable Text Editor", "keywords": [ "portabletext",