docs/plans/2026-04-04-slate-v2-placeholder-ime.md
Supporting plan. For current queue and roadmap truth, see master-roadmap.md.
slate-browser and slate-v2 state across:
/Users/zbeyens/git/plate-2/Users/zbeyens/git/slate-v2slate-v2 surface.slate-v2 example/runtime code.slate-react-v2 component.slate-react-v2 before running.TextString primitive in slate-react-v2.TextString DOM repair behavior.SlateElement / SlateSpacer / SlateText / SlateLeaf primitives in slate-react-v2.SlatePlaceholder primitive in slate-react-v2.EditableText primitive in slate-react-v2.EditableElement and VoidElement primitives in slate-react-v2.Editable root in slate-react-v2.EditableText to split leaves from projection slices.EditableText to bind text/runtime ids from a Slate path.EditableBlocks as the first honest public v2 editor-facing surface.slate-v2 fragment semantics to one-block mixed-inline selections and inserts.EditableBlocks commits DOM text back into editor state.slate-browser nested-path selection helpers against handle-hydration and page-eval leaks.slate-v2 in the Playwright lane and prove mixed-inline select/type/copy/paste in Chromium.slate-dom-v2 clipboard transport and Chromium copy/paste.slate-dom-v2 and Chromium copy/paste.slate-dom-v2 and Chromium copy/paste.bulleted-list to compatible ordered-list containers and prove it through core, DOM, and Chromium.list-item unit types and prove it through core, DOM, and Chromium.check-list / check-list-item unit types./Users/zbeyens/git/slate-v2/playwright/integration/examples/slate-v2-placeholder-ime.test.ts/Users/zbeyens/git/slate-v2/site/examples/ts/slate-v2-placeholder.tsxslate-v2 + slate-react-v2 editor
with an empty-block placeholder path, FEFF zero-width span, , and a
native beforeinput / input reconciliation layer.slate-dom-v2 already has a two-way zero-width normalization rule and was
further patched here to map FEFF-prefixed native offsets with offset - 1 in
resolvePointForDOMPoint(...).docs/solutions/logic-errors/2026-04-03-zero-width-dom-selection-bridges-must-normalize-both-directions.mddocs/solutions/logic-errors/2026-04-03-jsdom-contenteditable-composition-is-not-a-trustworthy-ime-proof.mddocs/solutions/logic-errors/2026-04-03-legacy-line-break-placeholders-still-keep-feff-until-ime-proof-exists.mddocs/solutions/logic-errors/2026-04-04-slate-browser-playwright-helpers-must-normalize-zero-width-selection-and-wait-for-selection-sync.mdslate-browser.compositionend, not on a later friendly
insertText input.inputcompositionendinputslate-v2 placeholder proof behave like
a real committed editor surface again.slate-v2 placeholder path survive Chromium IME without FEFF?removeChild against the inner subtree and the
editable surface disappeared. DOM-owned
instead of a React child fiber.slate-v2:
FEFF is not inherently required for the empty line-break placeholder path in
Chromium, but the renderer must not let React own the IME-mutated
interior.slate-react-v2 as
ZeroWidthString, with the current split baked in:
data-slate-void="true" elementslate-react-v2 now owns TextString as the shared DOM text boundary, so
v2 proof surfaces no longer hand-roll data-slate-string spans.slate-react-v2 now owns those shapes too, so the proof surfaces are finally
depending on a minimal renderer stack instead of impersonating one.slate-react-v2 now owns that too through SlatePlaceholder, so the
placeholder proof surface no longer hand-rolls overlay attrs and style.slate-react-v2 now owns that through EditableText, so the proof surfaces
finally depend on a compositional text surface instead of juggling the
primitives by hand.slate-react-v2 now owns that through EditableElement and VoidElement,
so the proof surfaces no longer rebuild the element/void contract by hand.slate-react-v2 now owns that through Editable, so the proof surfaces no
longer keep private browser-reconciliation loops.EditableText now owns that too, and the browser/bridge stack now does
cumulative offset mapping across decorated multi-leaf text nodes.EditableText now binds itself from path={[...]} and treats zero-length
projection slices as mark placeholders, which removes more route-local glue.Editable now owns clipboard bridge wiring, and the browser helpers now
normalize FEFF out of selected text so those proofs measure editor truth
instead of renderer debris.EditableBlocks is now the first honest public
editor-facing v2 surface: narrow, but real.slate-v2 with a real shared renderer-policy seam
instead of more bespoke example DOM.Editable still reconciled DOM edits by flattening the whole root back into a
single text block.Editable accept snapshotFromDom(...) and let
EditableTextBlocks preserve the current descendant shape while replacing
only text-leaf content from DOM.slate-browser nested-path selection helpers could be correct in the page but
still fail in helper assertions because the page-eval snapshot functions
referenced SLATE_BROWSER_HANDLE_KEY without serializing it into browser
context.slate-v2 dist output because the
Playwright build lane was not rebuilding slate-v2 alongside the other v2
packages.slate-v2 too, the browser copy proof matched the green
core/DOM clipboard contracts.{ at: ... } could leave editor
selections stale even when the inserted document shape itself was correct.list-item fragments as sibling
units when the target container is a nested bulleted-list, instead of
unwrapping them into paragraph children.paragraph + nested-list did not need a new
unit-insert model.
The same sibling-unit seam also survives paragraph + quote.paragraph + nested-list + quote.paragraph + nested-list + quote child-block mix.
That still lifts cleanly to one proved wrapper-list fragment.ordered-list.
That needed the seam widened, not redesigned.check-list-item reused the
same wrapper-unit seam too.insert_fragment operation payload, we can honestly rebase
later-block mixed-inline range refs through top-level block shifts, but
same-block mixed-inline range-ref rebasing would need richer insert metadata
than the op currently carries.learnings-researcher, goal workflow, tdd, debug, react.slate-browser plan file to recover exact state.0.0:6|0.0:6.compositionend after transient insertCompositionText events..tmp/slate-v2/site/examples/ts/slate-v2-placeholder.tsx
so the proof surface ignores transient composition input and commits from DOM
on compositionend.bash ./scripts/run-slate-browser-local.sh 3100 /examples/slate-v2-placeholder "yarn build:slate-browser && yarn exec playwright test playwright/integration/examples/slate-v2-placeholder-ime.test.ts --project=chromium --workers=1"yarn lint:typescript.tmp/slate-v2/package.json so test:slate-browser:ime and
test:slate-browser:ime:local both include
playwright/integration/examples/slate-v2-placeholder-ime.test.ts.yarn test:slate-browser:ime:localdocs/solutions/logic-errors/2026-04-04-slate-v2-placeholder-ime-proofs-must-commit-on-compositionend.md..tmp/slate-v2/site/examples/ts/components/slate-v2-placeholder-surface.tsx,
kept the FEFF route in .tmp/slate-v2/site/examples/ts/slate-v2-placeholder.tsx,
and added the no-FEFF route in
.tmp/slate-v2/site/examples/ts/slate-v2-placeholder-no-feff.tsx..tmp/slate-v2/site/constants/examples.ts..tmp/slate-v2/playwright/integration/examples/slate-v2-placeholder-ime.test.ts
with a Chromium proof for the no-FEFF placeholder path.NotFoundError: Failed to execute 'removeChild' on 'Node'. child that Chromium rewrote during IME. as DOM-owned
interior.bash ./scripts/run-slate-browser-local.sh 3100 /examples/slate-v2-placeholder-no-feff "yarn build:slate-browser && yarn exec playwright test playwright/integration/examples/slate-v2-placeholder-ime.test.ts --project=chromium --workers=1"yarn test:slate-browser:ime:localdocs/solutions/logic-errors/2026-04-04-slate-v2-no-feff-line-break-placeholders-need-dom-owned-br-interiors.md..tmp/slate-v2/packages/slate-react-v2/src/components/zero-width-string.tsx
and exported it from
.tmp/slate-v2/packages/slate-react-v2/src/index.ts..tmp/slate-v2/packages/slate-react-v2/test/runtime.tsx.ZeroWidthString component in
.tmp/slate-v2/site/examples/ts/components/slate-v2-placeholder-surface.tsx..tmp/slate-v2/site/examples/ts/slate-v2-zero-width-matrix.tsx
and wired it in
.tmp/slate-v2/site/constants/examples.ts..tmp/slate-v2/playwright/integration/examples/slate-v2-zero-width-matrix.test.ts..tmp/slate-v2/package.json
so the Playwright e2e/IME lanes build
slate-react-v2 alongside slate-browser, slate-react, and
slate-dom-v2 before running.yarn lint:typescriptyarn workspace slate-react-v2 testyarn test:slate-browser:e2e:localyarn test:slate-browser:ime:local.tmp/slate-v2/packages/slate-v2/src/core.ts
so Editor.getFragment(...) and Transforms.insertFragment(...) support the
current one-block mixed-inline proof shape in addition to the earlier simple
text-block proof shape..tmp/slate-v2/packages/slate-dom-v2/src/clipboard.ts
and
.tmp/slate-v2/packages/slate-dom-v2/test/clipboard-boundary.ts
so the clipboard boundary accepts the same mixed-inline proof shape and proves
the round-trip..tmp/slate-v2/playwright/integration/examples/slate-v2-mixed-inline.test.ts
from “fail closed for now” into the real mixed-inline browser proof:
selection, typing, and copy/paste round-trip..tmp/slate-v2/packages/slate-react-v2/src/components/editable.tsx
so Editable can commit through snapshotFromDom(...) instead of only the
old flat-text snapshotForText(...) path..tmp/slate-v2/packages/slate-react-v2/src/components/editable-text-blocks.tsx
so EditableBlocks preserves the current descendant shape and only replaces
text-leaf content when DOM edits commit back into editor state..tmp/slate-v2/packages/slate-browser/src/playwright/index.ts
so nested-path selection helpers:
.tmp/slate-v2/package.json
so build:slate-browser:playwright also rebuilds slate-v2, which the site
examples consume during browser proofs.bun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/clipboard-contract.tsyarn workspace slate-dom-v2 test clipboard-boundaryyarn workspace slate-react-v2 testyarn lint:typescriptyarn test:slate-browser:e2e:localyarn test:slate-browser:ime:local.tmp/slate-v2/packages/slate-v2/src/core.ts
so mixed-inline fragment extraction can span multiple top-level blocks and
mixed-inline fragment insertion can paste multi-block fragments into the
current proved mixed-inline target shape..tmp/slate-v2/packages/slate-v2/test/clipboard-contract.ts
with multi-block mixed-inline fragment extraction and insertion proofs..tmp/slate-v2/packages/slate-dom-v2/test/clipboard-boundary.ts
with the matching cross-block clipboard boundary round-trip proof..tmp/slate-v2/site/examples/ts/components/slate-v2-mixed-inline-surface.tsx,
kept the existing one-block route in
.tmp/slate-v2/site/examples/ts/slate-v2-mixed-inline.tsx,
and added the dedicated multi-block route in
.tmp/slate-v2/site/examples/ts/slate-v2-mixed-inline-multiblock.tsx..tmp/slate-v2/site/constants/examples.ts..tmp/slate-v2/playwright/integration/examples/slate-v2-mixed-inline-multiblock.test.ts
and folded it into the local e2e lane in
.tmp/slate-v2/package.json.bun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/clipboard-contract.tsyarn workspace slate-dom-v2 test clipboard-boundaryyarn lint:typescriptyarn test:slate-browser:e2e:localyarn test:slate-browser:ime:local.tmp/slate-v2/packages/slate-v2/src/core.ts
so explicit-at mixed-inline inserts rebase editor selections through
block-relative text offsets inside the current proved mixed-inline shape..tmp/slate-v2/packages/slate-v2/src/range-ref-transform.ts
so later-block mixed-inline range refs shift correctly after explicit
multi-block fragment inserts..tmp/slate-v2/packages/slate-v2/test/clipboard-contract.ts
with explicit-at mixed-inline selection rebasing proofs..tmp/slate-v2/packages/slate-v2/test/range-ref-contract.ts
with the later-block mixed-inline range-ref rebasing proof.bun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/clipboard-contract.tsbun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/range-ref-contract.tsyarn lint:typescriptyarn test:slate-browser:e2e:localyarn test:slate-browser:ime:local.tmp/slate-v2/packages/slate-v2/src/core.ts
with a core-only mixed-inline range-ref transform path for insert_fragment
operations so same-block mixed-inline refs can rebase without bloating the
public operation shape with synthetic metadata..tmp/slate-v2/packages/slate-v2/test/range-ref-contract.ts
with same-block mixed-inline range-ref proofs for:
bun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/clipboard-contract.tsbun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/range-ref-contract.tsyarn lint:typescriptyarn test:slate-browser:e2e:localyarn test:slate-browser:ime:local.tmp/slate-v2/packages/slate-v2/src/core.ts
so simple text-block fragment extraction and insertion can operate inside a
nested sibling-block container and preserve the wrapper element in the
fragment..tmp/slate-v2/packages/slate-v2/test/clipboard-contract.ts
with the nested quote extraction and insertion proofs..tmp/slate-v2/packages/slate-dom-v2/src/clipboard.ts
so plain-text export preserves nested block breaks and the accepted proof
fragment subset includes the nested quote wrapper case..tmp/slate-v2/packages/slate-dom-v2/test/clipboard-boundary.ts
with the nested quote round-trip proof and a sharper unsupported nested shape..tmp/slate-v2/site/examples/ts/slate-v2-nested-quote.tsx
and registered it in
.tmp/slate-v2/site/constants/examples.ts..tmp/slate-v2/playwright/integration/examples/slate-v2-nested-quote.test.ts
and folded it into the local e2e lane in
.tmp/slate-v2/package.json.bun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/clipboard-contract.tsyarn workspace slate-dom-v2 test clipboard-boundaryyarn lint:typescriptyarn test:slate-browser:e2e:localyarn test:slate-browser:ime:local.tmp/slate-v2/packages/slate-v2/src/core.ts
so explicit-at editor selections inside nested quote paragraph containers
rebase by stripping the container prefix, reusing the top-level text-block
rebasing logic, and prefixing the container path back..tmp/slate-v2/packages/slate-v2/src/core.ts
with a second core-only insert-fragment range-ref transform path for the same
simple nested block-container shape..tmp/slate-v2/packages/slate-v2/test/clipboard-contract.ts
with nested quote explicit-at selection rebasing proofs..tmp/slate-v2/packages/slate-v2/test/range-ref-contract.ts
with nested quote explicit-at range-ref rebasing proofs.bun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/clipboard-contract.tsbun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/range-ref-contract.tsyarn workspace slate-dom-v2 test clipboard-boundaryyarn lint:typescriptyarn test:slate-browser:e2e:localyarn test:slate-browser:ime:local.tmp/slate-v2/site/examples/ts/components/slate-v2-inline-edge-surface.tsx
and the hidden route in
.tmp/slate-v2/site/examples/ts/slate-v2-inline-edge.tsx..tmp/slate-v2/playwright/integration/examples/slate-v2-inline-edge-ime.test.ts
and folded it into the IME lane in
.tmp/slate-v2/package.json.yarn lint:typescriptyarn test:slate-browser:ime:localyarn test:slate-browser:e2e:localdocs/solutions/logic-errors/2026-04-04-inline-edge-ime-proofs-should-set-selection-semantically-before-composition.md..tmp/slate-v2/site/examples/ts/components/slate-v2-void-edge-surface.tsx
and the hidden route in
.tmp/slate-v2/site/examples/ts/slate-v2-void-edge.tsx..tmp/slate-v2/playwright/integration/examples/slate-v2-void-edge-ime.test.ts
and folded it into the IME lane in
.tmp/slate-v2/package.json..tmp/slate-v2/site/examples/ts/components/slate-v2-void-edge-surface.tsx
so it matches the real legacy void seam:
data-slate-void="true" element, non-editable content wrapper, and separate
absolutely positioned spacer leaf.bash ./scripts/run-slate-browser-local.sh 3100 /examples/slate-v2-void-edge "yarn build:slate-browser:playwright && yarn exec playwright test playwright/integration/examples/slate-v2-void-edge-ime.test.ts --project=chromium --workers=1"docs/solutions/logic-errors/2026-04-04-void-like-zero-width-ime-proofs-need-the-real-void-spacer-structure.md.TextString primitive in
.tmp/slate-v2/packages/slate-react-v2/src/components/text-string.tsx
and exported it from
.tmp/slate-v2/packages/slate-react-v2/src/index.ts.TextString..tmp/slate-v2/packages/slate-react-v2/test/runtime.tsx
proving TextString repairs stale DOM text on rerender.yarn workspace slate-react-v2 testyarn lint:typescriptbash ./scripts/run-slate-browser-local.sh 3100 /examples/slate-v2-zero-width-matrix "yarn build:slate-browser:playwright && yarn exec playwright test playwright/integration/examples/slate-v2-zero-width-matrix.test.ts --project=chromium --workers=1"bash ./scripts/run-slate-browser-local.sh 3101 /examples/slate-v2-placeholder "yarn build:slate-browser:playwright && yarn exec playwright test playwright/integration/examples/slate-v2-placeholder-ime.test.ts playwright/integration/examples/slate-v2-inline-edge-ime.test.ts playwright/integration/examples/slate-v2-void-edge-ime.test.ts --project=chromium --workers=1"docs/solutions/logic-errors/2026-04-04-v2-text-string-primitives-should-own-the-dom-text-boundary.md..tmp/slate-v2/packages/slate-react-v2/src/components/:
slate-text.tsx, slate-leaf.tsx, slate-element.tsx, and
slate-spacer.tsx..tmp/slate-v2/packages/slate-react-v2/src/index.ts
and migrated the v2 placeholder, inline-edge, void-edge, and zero-width
matrix proof surfaces to consume them..tmp/slate-v2/packages/slate-react-v2/test/runtime.tsx.yarn workspace slate-react-v2 testyarn lint:typescriptbash ./scripts/run-slate-browser-local.sh 3100 /examples/slate-v2-zero-width-matrix "yarn build:slate-browser:playwright && yarn exec playwright test playwright/integration/examples/slate-v2-zero-width-matrix.test.ts --project=chromium --workers=1"bash ./scripts/run-slate-browser-local.sh 3101 /examples/slate-v2-placeholder "yarn build:slate-browser:playwright && yarn exec playwright test playwright/integration/examples/slate-v2-placeholder-ime.test.ts playwright/integration/examples/slate-v2-inline-edge-ime.test.ts playwright/integration/examples/slate-v2-void-edge-ime.test.ts --project=chromium --workers=1"docs/solutions/logic-errors/2026-04-04-v2-renderer-primitives-should-own-node-shapes-not-example-markup.md..tmp/slate-v2/packages/slate-react-v2/src/components/slate-placeholder.tsx
and exported it from
.tmp/slate-v2/packages/slate-react-v2/src/index.ts.SlatePlaceholder
in
.tmp/slate-v2/site/examples/ts/components/slate-v2-placeholder-surface.tsx..tmp/slate-v2/packages/slate-react-v2/test/runtime.tsx.yarn workspace slate-react-v2 testyarn lint:typescriptbash ./scripts/run-slate-browser-local.sh 3100 /examples/slate-v2-placeholder "yarn build:slate-browser:playwright && yarn exec playwright test playwright/integration/examples/slate-v2-placeholder-ime.test.ts --project=chromium --workers=1"docs/solutions/logic-errors/2026-04-04-v2-placeholder-primitives-should-own-overlay-attrs-and-style.md.EditableText primitive in
.tmp/slate-v2/packages/slate-react-v2/src/components/editable-text.tsx
and exported it from
.tmp/slate-v2/packages/slate-react-v2/src/index.ts.EditableText.EditableText in
.tmp/slate-v2/packages/slate-react-v2/test/runtime.tsx.yarn workspace slate-react-v2 testyarn lint:typescriptbash ./scripts/run-slate-browser-local.sh 3100 /examples/slate-v2-zero-width-matrix "yarn build:slate-browser:playwright && yarn exec playwright test playwright/integration/examples/slate-v2-zero-width-matrix.test.ts --project=chromium --workers=1"bash ./scripts/run-slate-browser-local.sh 3101 /examples/slate-v2-placeholder "yarn build:slate-browser:playwright && yarn exec playwright test playwright/integration/examples/slate-v2-placeholder-ime.test.ts playwright/integration/examples/slate-v2-inline-edge-ime.test.ts playwright/integration/examples/slate-v2-void-edge-ime.test.ts --project=chromium --workers=1"docs/solutions/logic-errors/2026-04-04-v2-editable-text-primitives-should-compose-leaf-text-zero-width-and-placeholder.md..tmp/slate-v2/packages/slate-react-v2/src/components/editable-element.tsx
and
.tmp/slate-v2/packages/slate-react-v2/src/components/void-element.tsx,
then exported them from
.tmp/slate-v2/packages/slate-react-v2/src/index.ts.EditableElement and
VoidElement in
.tmp/slate-v2/packages/slate-react-v2/test/runtime.tsx.yarn workspace slate-react-v2 testyarn lint:typescriptbash ./scripts/run-slate-browser-local.sh 3100 /examples/slate-v2-zero-width-matrix "yarn build:slate-browser:playwright && yarn exec playwright test playwright/integration/examples/slate-v2-zero-width-matrix.test.ts --project=chromium --workers=1"bash ./scripts/run-slate-browser-local.sh 3101 /examples/slate-v2-placeholder "yarn build:slate-browser:playwright && yarn exec playwright test playwright/integration/examples/slate-v2-placeholder-ime.test.ts playwright/integration/examples/slate-v2-inline-edge-ime.test.ts playwright/integration/examples/slate-v2-void-edge-ime.test.ts --project=chromium --workers=1"docs/solutions/logic-errors/2026-04-04-v2-element-primitives-should-compose-element-and-void-contracts.md..tmp/slate-v2/packages/slate-react-v2/src/components/editable.tsx
and exported it from
.tmp/slate-v2/packages/slate-react-v2/src/index.ts.Editable DOM-to-snapshot
reconciliation in
.tmp/slate-v2/packages/slate-react-v2/test/runtime.tsx.yarn workspace slate-react-v2 testyarn lint:typescriptbash ./scripts/run-slate-browser-local.sh 3100 /examples/slate-v2-zero-width-matrix "yarn build:slate-browser:playwright && yarn exec playwright test playwright/integration/examples/slate-v2-zero-width-matrix.test.ts --project=chromium --workers=1"bash ./scripts/run-slate-browser-local.sh 3101 /examples/slate-v2-placeholder "yarn build:slate-browser:playwright && yarn exec playwright test playwright/integration/examples/slate-v2-placeholder-ime.test.ts playwright/integration/examples/slate-v2-inline-edge-ime.test.ts playwright/integration/examples/slate-v2-void-edge-ime.test.ts --project=chromium --workers=1"docs/solutions/logic-errors/2026-04-04-v2-editable-roots-should-own-mount-selection-sync-and-dom-commit.md.EditableText so it can split one text node into
multiple rendered leaves from projection slices via runtimeId and
renderSegment(...)..tmp/slate-v2/site/examples/ts/slate-v2-highlighted-text.tsx
and the browser proof in
.tmp/slate-v2/playwright/integration/examples/slate-v2-highlighted-text.test.ts,
then folded it into
.tmp/slate-v2/package.json e2e scripts.slate-dom-v2 and slate-browser to map cumulative offsets across
multi-leaf text nodes instead of assuming one DOM text node per Slate text
node.yarn workspace slate-react-v2 testyarn lint:typescriptyarn test:slate-browser:e2e:localyarn test:slate-browser:ime:localdocs/solutions/logic-errors/2026-04-04-v2-editable-text-should-split-leaves-from-projection-slices.md
and
docs/solutions/logic-errors/2026-04-04-decorated-multi-leaf-text-needs-cumulative-offset-mapping.md.EditableText to derive text and runtimeId from a
Slate path and to render zero-length projection slices as mark-placeholder
leaves.EditableText API.EditableText and
zero-length mark-placeholder rendering in
.tmp/slate-v2/packages/slate-react-v2/test/runtime.tsx..tmp/slate-v2/playwright/integration/examples/slate-v2-mark-placeholder.test.ts
and folded it into
.tmp/slate-v2/package.json e2e scripts.yarn workspace slate-react-v2 testyarn lint:typescriptyarn test:slate-browser:e2e:localyarn test:slate-browser:ime:localdocs/solutions/logic-errors/2026-04-04-v2-text-surfaces-should-bind-runtime-ids-from-paths-and-use-zero-length-projections-for-mark-placeholders.md.Editable to the ClipboardBridge for browser-side copy
and paste semantics..tmp/slate-v2/playwright/integration/examples/slate-v2-highlighted-text.test.ts
and
.tmp/slate-v2/playwright/integration/examples/slate-v2-mark-placeholder.test.ts.slate-browser selected-text reads so FEFF sentinels
do not leak through mark-placeholder selections.yarn workspace slate-react-v2 testyarn lint:typescriptyarn test:slate-browser:e2e:localyarn test:slate-browser:ime:localdocs/solutions/logic-errors/2026-04-04-decorated-clipboard-and-selected-text-helpers-should-strip-render-only-wrappers-and-feff.md.EditableBlocks from
.tmp/slate-v2/packages/slate-react-v2/src/components/editable-blocks.tsx
as the first narrow public editor-facing surface over the proved renderer
stack.docs/solutions/logic-errors/2026-04-04-v2-editable-blocks-can-be-the-first-public-editor-surface.md..tmp/slate-v2/packages/slate-v2/src/core.ts
so nested mixed-inline quote paragraph containers can:
.tmp/slate-v2/packages/slate-v2/src/core.ts
with a fourth core-only insert-fragment range-ref transform path for nested
mixed-inline block containers..tmp/slate-v2/packages/slate-v2/test/clipboard-contract.ts
with nested mixed-inline quote extraction, insertion, and explicit-at
selection rebasing proofs..tmp/slate-v2/packages/slate-v2/test/range-ref-contract.ts
with nested mixed-inline quote explicit-at range-ref rebasing proofs..tmp/slate-v2/packages/slate-dom-v2/src/clipboard.ts
so the clipboard proof subset includes a wrapped container whose child blocks
are mixed-inline blocks..tmp/slate-v2/site/examples/ts/slate-v2-nested-mixed-inline.tsx
and registered it in
.tmp/slate-v2/site/constants/examples.ts..tmp/slate-v2/playwright/integration/examples/slate-v2-nested-mixed-inline.test.ts
and folded it into the local e2e lane in
.tmp/slate-v2/package.json.yarn exec playwright install chromiumbun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/clipboard-contract.tsbun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/range-ref-contract.tsyarn workspace slate-dom-v2 test clipboard-boundaryyarn lint:typescriptyarn test:slate-browser:e2e:localyarn test:slate-browser:ime:localdocs/slate-v2/overview.mddocs/slate-v2/cohesive-program-plan.mddocs/slate-v2/final-synthesis.md.tmp/slate-v2/packages/slate-v2/src/core.ts
from the old direct-child / one-hop assumption to recursive text-leaf entry
mapping inside a block..tmp/slate-v2/packages/slate-v2/test/clipboard-contract.ts
with richer-inline top-level and nested quote proofs for:
.tmp/slate-v2/packages/slate-v2/test/range-ref-contract.ts
with richer-inline top-level and nested quote proofs for explicit-at
range-ref rebasing..tmp/slate-v2/packages/slate-dom-v2/src/clipboard.ts
so the supported proof subset includes richer inline descendant trees inside
a block, not only the earlier direct single-text-child inline shape..tmp/slate-v2/site/examples/ts/slate-v2-rich-inline.tsx.tmp/slate-v2/site/examples/ts/slate-v2-nested-rich-inline.tsx.tmp/slate-v2/site/examples/ts/components/slate-v2-rich-inline-surface.tsx.tmp/slate-v2/playwright/integration/examples/slate-v2-rich-inline.test.ts.tmp/slate-v2/playwright/integration/examples/slate-v2-nested-rich-inline.test.ts
and folded them into the local e2e lane in
.tmp/slate-v2/package.json.bun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/clipboard-contract.tsbun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/range-ref-contract.tsyarn workspace slate-dom-v2 test clipboard-boundaryyarn lint:typescriptyarn test:slate-browser:e2e:localyarn test:slate-browser:ime:localdocs/slate-v2/overview.mddocs/slate-v2/cohesive-program-plan.mddocs/slate-v2/final-synthesis.md.tmp/slate-v2/site/examples/ts/slate-v2-list-rich-inline.tsx
and registered it in
.tmp/slate-v2/site/constants/examples.ts..tmp/slate-v2/playwright/integration/examples/slate-v2-list-rich-inline.test.ts
and folded it into the local e2e lane in
.tmp/slate-v2/package.json.bun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/clipboard-contract.tsbun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/range-ref-contract.tsyarn workspace slate-dom-v2 test clipboard-boundaryyarn lint:typescriptyarn test:slate-browser:e2e:local.tmp/slate-v2/site/examples/ts/slate-v2-nested-list-rich-inline.tsx
and registered it in
.tmp/slate-v2/site/constants/examples.ts..tmp/slate-v2/playwright/integration/examples/slate-v2-nested-list-rich-inline.test.ts
and folded it into the local e2e lane in
.tmp/slate-v2/package.json..tmp/slate-v2/packages/slate-v2/test/range-ref-contract.ts
with top-level and nested wrapper-stack range-ref rebasing proofs.bun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/clipboard-contract.tsbun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/range-ref-contract.tsyarn workspace slate-dom-v2 test clipboard-boundaryyarn lint:typescriptyarn test:slate-browser:e2e:localyarn test:slate-browser:ime:localdocs/slate-v2/overview.mddocs/slate-v2/cohesive-program-plan.mddocs/slate-v2/final-synthesis.md.tmp/slate-v2/site/examples/ts/slate-v2-list-unit-rich-inline.tsx.tmp/slate-v2/site/examples/ts/slate-v2-nested-list-unit-rich-inline.tsx.tmp/slate-v2/playwright/integration/examples/slate-v2-list-unit-rich-inline.test.ts.tmp/slate-v2/playwright/integration/examples/slate-v2-nested-list-unit-rich-inline.test.ts.tmp/slate-v2/packages/slate-dom-v2/test/clipboard-boundary.ts
with list-unit clipboard round-trip coverage.bun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/clipboard-contract.tsbun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/range-ref-contract.tsyarn workspace slate-dom-v2 test clipboard-boundaryyarn lint:typescriptyarn test:slate-browser:e2e:local.tmp/slate-v2/site/examples/ts/slate-v2-list-unit-quote-rich-inline.tsx.tmp/slate-v2/site/examples/ts/slate-v2-nested-list-unit-quote-rich-inline.tsx.tmp/slate-v2/playwright/integration/examples/slate-v2-list-unit-quote-rich-inline.test.ts.tmp/slate-v2/playwright/integration/examples/slate-v2-nested-list-unit-quote-rich-inline.test.ts.tmp/slate-v2/packages/slate-dom-v2/test/clipboard-boundary.ts
with paragraph-plus-quote list-unit round-trip coverage.bun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/clipboard-contract.tsbun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/range-ref-contract.tsyarn workspace slate-dom-v2 test clipboard-boundaryyarn lint:typescriptyarn test:slate-browser:e2e:local.tmp/slate-v2/site/examples/ts/slate-v2-list-unit-quote-rich-inline.tsx.tmp/slate-v2/site/examples/ts/slate-v2-nested-list-unit-quote-rich-inline.tsx.tmp/slate-v2/playwright/integration/examples/slate-v2-list-unit-quote-rich-inline.test.ts.tmp/slate-v2/playwright/integration/examples/slate-v2-nested-list-unit-quote-rich-inline.test.ts.tmp/slate-v2/packages/slate-v2/test/range-ref-contract.ts
and .tmp/slate-v2/packages/slate-dom-v2/test/clipboard-boundary.ts
for the paragraph-plus-list-plus-quote wrapper-unit shape.bun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/clipboard-contract.tsbun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/range-ref-contract.tsyarn workspace slate-dom-v2 test clipboard-boundaryyarn lint:typescriptyarn test:slate-browser:e2e:local.tmp/slate-v2/site/examples/ts/slate-v2-complex-list-units-rich-inline.tsx.tmp/slate-v2/site/examples/ts/slate-v2-nested-complex-list-units-rich-inline.tsx.tmp/slate-v2/playwright/integration/examples/slate-v2-complex-list-units-rich-inline.test.ts.tmp/slate-v2/playwright/integration/examples/slate-v2-nested-complex-list-units-rich-inline.test.ts.tmp/slate-v2/packages/slate-v2/test/clipboard-contract.ts
and .tmp/slate-v2/packages/slate-dom-v2/test/clipboard-boundary.ts
for multi-unit complex wrapper-list fragments.bun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/clipboard-contract.tsbun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/range-ref-contract.tsyarn workspace slate-dom-v2 test clipboard-boundaryyarn lint:typescriptyarn test:slate-browser:e2e:local.tmp/slate-v2/site/examples/ts/slate-v2-complex-list-units-rich-inline.tsx.tmp/slate-v2/site/examples/ts/slate-v2-nested-complex-list-units-rich-inline.tsx.tmp/slate-v2/playwright/integration/examples/slate-v2-complex-list-units-rich-inline.test.ts.tmp/slate-v2/playwright/integration/examples/slate-v2-nested-complex-list-units-rich-inline.test.tsbun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/clipboard-contract.tsbun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/range-ref-contract.tsyarn workspace slate-dom-v2 test clipboard-boundaryyarn lint:typescriptyarn test:slate-browser:e2e:local.tmp/slate-v2/packages/slate-v2/src/core.ts.bun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/clipboard-contract.tsbun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/range-ref-contract.tsyarn workspace slate-dom-v2 test clipboard-boundaryyarn lint:typescriptyarn test:slate-browser:e2e:local.tmp/slate-v2/site/examples/ts/slate-v2-multi-block-complex-list-units-rich-inline.tsx.tmp/slate-v2/site/examples/ts/slate-v2-nested-multi-block-complex-list-units-rich-inline.tsx.tmp/slate-v2/playwright/integration/examples/slate-v2-multi-block-complex-list-units-rich-inline.test.ts.tmp/slate-v2/playwright/integration/examples/slate-v2-nested-multi-block-complex-list-units-rich-inline.test.ts.tmp/slate-v2/packages/slate-v2/test/clipboard-contract.ts
and .tmp/slate-v2/packages/slate-dom-v2/test/clipboard-boundary.ts
for sibling complex units whose nested list and quote children each contain
multiple blocks.bun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/clipboard-contract.tsbun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/range-ref-contract.tsyarn workspace slate-dom-v2 test clipboard-boundaryyarn lint:typescriptyarn test:slate-browser:e2e:local.tmp/slate-v2/packages/slate-v2/test/clipboard-contract.ts
and .tmp/slate-v2/packages/slate-v2/test/range-ref-contract.ts
with explicit-at selection/range-ref proofs for broader sibling units whose
child containers are multi-block.bun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/clipboard-contract.tsbun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/range-ref-contract.tsyarn lint:typescript.tmp/slate-v2/packages/slate-v2/test/clipboard-contract.ts
and .tmp/slate-v2/packages/slate-v2/test/range-ref-contract.ts
with expanded cross-unit explicit-at selection/range-ref proofs.bun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/clipboard-contract.tsbun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/range-ref-contract.tsyarn lint:typescript.tmp/slate-v2/site/examples/ts/slate-v2-ordered-list-unit-rich-inline.tsx.tmp/slate-v2/playwright/integration/examples/slate-v2-ordered-list-unit-rich-inline.test.ts.tmp/slate-v2/packages/slate-v2/src/core.ts,
.tmp/slate-v2/packages/slate-v2/test/clipboard-contract.ts,
.tmp/slate-v2/packages/slate-v2/test/range-ref-contract.ts,
and .tmp/slate-v2/packages/slate-dom-v2/test/clipboard-boundary.ts
for the ordered-list wrapper-unit variant.bun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/clipboard-contract.tsbun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/range-ref-contract.tsyarn workspace slate-dom-v2 test clipboard-boundaryyarn lint:typescriptyarn test:slate-browser:e2e:local.tmp/slate-v2/packages/slate-v2/test/clipboard-contract.ts.tmp/slate-v2/packages/slate-v2/test/range-ref-contract.tsbun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/clipboard-contract.tsbun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/range-ref-contract.tsyarn lint:typescript.tmp/slate-v2/packages/slate-v2/src/core.ts
from hardcoded list-item to compatible homogeneous unit types..tmp/slate-v2/packages/slate-v2/test/clipboard-contract.ts.tmp/slate-v2/packages/slate-v2/test/range-ref-contract.tsbun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/clipboard-contract.tsbun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/range-ref-contract.tsyarn workspace slate-dom-v2 test clipboard-boundaryyarn lint:typescriptyarn test:slate-browser:e2e:local.tmp/slate-v2/site/examples/ts/slate-v2-check-list-unit-rich-inline.tsx.tmp/slate-v2/playwright/integration/examples/slate-v2-check-list-unit-rich-inline.test.tscheck-list-item variant with:
bun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/clipboard-contract.tsbun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/range-ref-contract.tsyarn workspace slate-dom-v2 test clipboard-boundaryyarn lint:typescriptyarn test:slate-browser:e2e:local.tmp/slate-v2/packages/slate-v2/src/core.ts
so compatible heterogeneous sibling unit types can reuse the same wrapper-list
path when the outer container contract still matches.bun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/clipboard-contract.tsbun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/range-ref-contract.tsyarn workspace slate-dom-v2 test clipboard-boundaryyarn lint:typescriptyarn test:slate-browser:e2e:local.tmp/slate-v2/site/examples/ts/slate-v2-deep-wrapper-rich-inline.tsx
and registered it in
.tmp/slate-v2/site/constants/examples.ts..tmp/slate-v2/playwright/integration/examples/slate-v2-deep-wrapper-rich-inline.test.ts
and folded it into the local e2e lane in
.tmp/slate-v2/package.json..tmp/slate-v2/packages/slate-v2/test/range-ref-contract.ts
with deeper wrapper-stack range-ref rebasing proofs.bun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/clipboard-contract.tsbun test /Users/zbeyens/git/slate-v2/packages/slate-v2/test/range-ref-contract.tsyarn workspace slate-dom-v2 test clipboard-boundaryyarn lint:typescriptyarn test:slate-browser:e2e:localyarn test:slate-browser:ime:localdocs/slate-v2/overview.mddocs/slate-v2/cohesive-program-plan.mddocs/slate-v2/final-synthesis.md