Skip to content

[3/3] test(painter): lock ResolvedLayout-only boundary (SD-2836)#3118

Merged
luccas-harbour merged 3 commits intotadeu/sd-2836-2-collapse-legacy-apifrom
tadeu/sd-2836-3-lock-boundary
May 5, 2026
Merged

[3/3] test(painter): lock ResolvedLayout-only boundary (SD-2836)#3118
luccas-harbour merged 3 commits intotadeu/sd-2836-2-collapse-legacy-apifrom
tadeu/sd-2836-3-lock-boundary

Conversation

@tupizz
Copy link
Copy Markdown
Contributor

@tupizz tupizz commented May 4, 2026

Stack

# PR Branch Status
1 #3116 tadeu/sd-2836-1-consume-resolved-layout open
2 #3117 tadeu/sd-2836-2-collapse-legacy-api open
3 this PR tadeu/sd-2836-3-lock-boundary open

Review in order. PR3 depends on PR1 and PR2; merge those first.

What changed

Locks the boundary established by PR1+PR2 so no future change can re-introduce legacy painter inputs or disallowed cross-package imports.

Architecture guard

  • New tests in tests/architecture-boundaries.test.ts (Guard D) forbid painter-dom from importing pm-adapter, layout-bridge, or layout-resolved at runtime.
  • New type-shape lockdown in painters/dom/src/contract-shape.test.ts pins the painter's public input/option types to ResolvedLayout via Equal<X, Y> + expectTypeOf. Any drift in the contract fails the suite.

Cleanup

  • Drops @superdoc/layout-resolved from painters/dom/package.json runtime dependencies (kept under devDependencies for the boundary tests).
  • Removes unused imports and skipped legacy tests from PR1/PR2 transition.

Why this split

The painter is now structurally dumb (PR1) and surface-collapsed (PR2). This PR makes that state ratchet-only: regressing the boundary breaks the build, not just review.

Verification

  • All package tests pass: painter-dom, pm-adapter, layout-bridge, layout-engine, super-editor.
  • HEAD-vs-origin/main corpus snapshot diff: 0 changed / 407 unchanged (rendering-neutral).

Test plan

  • CI green
  • Reviewer confirms guard tests fail when a forbidden import is added (try locally to verify)
  • No runtime dependency on @superdoc/layout-resolved in painter-dom

@tupizz tupizz requested a review from a team as a code owner May 4, 2026 16:51
@linear
Copy link
Copy Markdown

linear Bot commented May 4, 2026

@tupizz tupizz force-pushed the tadeu/sd-2836-2-collapse-legacy-api branch from 6b0c46d to befe9c0 Compare May 4, 2026 17:11
@tupizz tupizz force-pushed the tadeu/sd-2836-3-lock-boundary branch from 2fc4758 to c2370fb Compare May 4, 2026 17:11
@tupizz tupizz force-pushed the tadeu/sd-2836-2-collapse-legacy-api branch from befe9c0 to 8f18041 Compare May 4, 2026 17:48
@tupizz tupizz force-pushed the tadeu/sd-2836-3-lock-boundary branch from c2370fb to 34b7e7d Compare May 4, 2026 17:48
@tupizz tupizz force-pushed the tadeu/sd-2836-2-collapse-legacy-api branch from 8f18041 to 1c869c1 Compare May 4, 2026 18:05
@tupizz tupizz force-pushed the tadeu/sd-2836-3-lock-boundary branch from 34b7e7d to 85bd281 Compare May 4, 2026 18:05
@tupizz tupizz self-assigned this May 4, 2026
@tupizz tupizz force-pushed the tadeu/sd-2836-2-collapse-legacy-api branch from 1c869c1 to 87ddc92 Compare May 5, 2026 14:19
@tupizz tupizz force-pushed the tadeu/sd-2836-3-lock-boundary branch 2 times, most recently from 9ce9dc5 to 6e00a9f Compare May 5, 2026 15:04
tupizz added 3 commits May 5, 2026 12:23
renderer.ts: drop unused Layout, Page, Measure, FlowBlock, ParagraphBorder type imports left over from the migration. index.test.ts: delete the skipped 'decoration item synthesis' describe block (it was protecting the synthesis path that has been removed).
…me dep (SD-2836)

Moves @superdoc/layout-resolved to devDependencies in the lockfile to
match package.json, so CI's --frozen-lockfile install matches. Boundary
tests still need it under devDependencies.
@tupizz tupizz force-pushed the tadeu/sd-2836-3-lock-boundary branch from 6e00a9f to f804e44 Compare May 5, 2026 15:23
Copy link
Copy Markdown
Contributor

@luccas-harbour luccas-harbour left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@luccas-harbour luccas-harbour merged commit 7f2872d into tadeu/sd-2836-2-collapse-legacy-api May 5, 2026
43 checks passed
@luccas-harbour luccas-harbour deleted the tadeu/sd-2836-3-lock-boundary branch May 5, 2026 17:43
luccas-harbour pushed a commit that referenced this pull request May 5, 2026
* refactor(painter): collapse legacy API surface (SD-2836)

Make ResolvedLayout the only painter input contract:

* DomPainterInput collapses to `{ resolvedLayout: ResolvedLayout }`.
  sourceLayout, blocks/measures, headerBlocks/Measures,
  footerBlocks/Measures all removed.
* DomPainterOptions: drop blocks/measures.
* DomPainterHandle: drop setData, setResolvedLayout. paint takes only
  DomPainterInput. Drops the `paint(Layout)` overload across painter,
  PresentationPainterAdapter, and (transitively) PresentationEditor's
  paintInput.
* createDomPainter shrinks to a thin pass-through. Removes
  buildLegacyPaintInput, normalizeDomPainterInput, isDomPainterInput,
  wrapProvider, resolveDecorationItems, normalizeOptionalBlockMeasurePair,
  assertRequiredBlockMeasurePair, createEmptyResolvedLayout,
  LegacyDomPainterState, OptionalBlockMeasurePair.
* PageDecorationPayload.items becomes required (the synthesis path
  is gone).

Tests:
* New `_test-utils.ts` exposes a legacy-shaped `createTestPainter`
  that test files import in place of `createDomPainter`. The helper
  builds a real DomPainterInput internally and synthesizes decoration
  items so existing test bodies don't have to be rewritten.
* All 17 painter test files migrated to the helper.
* Two source-anchor tests still failing under investigation;
  remaining work is bounded and tracked.

* fix(painter): chain user onPaintSnapshot in test utility (SD-2836)

createTestPainter was overwriting the user-supplied onPaintSnapshot callback with its own, so tests that relied on the callback (source-anchor tests) saw an undefined snapshot. Chain the user callback after the internal one.

* test(pm-adapter): migrate integration tests to ResolvedLayout (SD-2836)

The three integration tests in pm-adapter were calling painter.paint(layout, mount) with raw Layout. They now resolveLayout() first and call painter.paint({ resolvedLayout }, mount). All 1794 pm-adapter tests pass.

* test(layout-bridge): migrate perf benchmark to ResolvedLayout (SD-2836)

The layout-bridge incremental-pipeline performance benchmark called painter.paint(layout, mount) and painter.setData(...). Both are removed by the API collapse. Migrate to resolveLayout() + DomPainterInput so the benchmark continues to exercise the painter under the new contract.

* chore(deps): declare @superdoc/layout-resolved as devDep where tests use it (SD-2836)

PR1 added test imports of @superdoc/layout-resolved in
pm-adapter/src/integration.test.ts and layout-bridge/test/benchmarks/index.ts
without declaring it as a devDependency. Both packages now resolve the
import locally via pnpm.

* fix(super-editor): honor PageDecorationPayload.items contract (SD-2836)

When resolveAlignedDecorationItems cannot align items 1:1 with fragments
(returns undefined), HeaderFooterSessionManager now bails out with null
instead of emitting a payload with items: undefined, which would violate
the now-required PageDecorationPayload.items contract from PR2.
normalizeDecorationItems narrowed to ResolvedPaintItem[] -> ResolvedPaintItem[].

Also refresh painter-dom README: drop blocks/measures/setData/paint(layout)
examples; document the ResolvedLayout-only paint() and the items-aligned-
with-fragments invariant on PageDecorationPayload.

* [3/3] test(painter): lock ResolvedLayout-only boundary (SD-2836) (#3118)

* test(painter): lock ResolvedLayout-only boundary (SD-2836)

* chore(painter): drop unused imports and skipped legacy tests (SD-2836)

renderer.ts: drop unused Layout, Page, Measure, FlowBlock, ParagraphBorder type imports left over from the migration. index.test.ts: delete the skipped 'decoration item synthesis' describe block (it was protecting the synthesis path that has been removed).

* chore(deps): regenerate lockfile after dropping layout-resolved runtime dep (SD-2836)

Moves @superdoc/layout-resolved to devDependencies in the lockfile to
match package.json, so CI's --frozen-lockfile install matches. Boundary
tests still need it under devDependencies.
tupizz added a commit that referenced this pull request May 5, 2026
* refactor(painter): collapse legacy API surface (SD-2836)

Make ResolvedLayout the only painter input contract:

* DomPainterInput collapses to `{ resolvedLayout: ResolvedLayout }`.
  sourceLayout, blocks/measures, headerBlocks/Measures,
  footerBlocks/Measures all removed.
* DomPainterOptions: drop blocks/measures.
* DomPainterHandle: drop setData, setResolvedLayout. paint takes only
  DomPainterInput. Drops the `paint(Layout)` overload across painter,
  PresentationPainterAdapter, and (transitively) PresentationEditor's
  paintInput.
* createDomPainter shrinks to a thin pass-through. Removes
  buildLegacyPaintInput, normalizeDomPainterInput, isDomPainterInput,
  wrapProvider, resolveDecorationItems, normalizeOptionalBlockMeasurePair,
  assertRequiredBlockMeasurePair, createEmptyResolvedLayout,
  LegacyDomPainterState, OptionalBlockMeasurePair.
* PageDecorationPayload.items becomes required (the synthesis path
  is gone).

Tests:
* New `_test-utils.ts` exposes a legacy-shaped `createTestPainter`
  that test files import in place of `createDomPainter`. The helper
  builds a real DomPainterInput internally and synthesizes decoration
  items so existing test bodies don't have to be rewritten.
* All 17 painter test files migrated to the helper.
* Two source-anchor tests still failing under investigation;
  remaining work is bounded and tracked.

* fix(painter): chain user onPaintSnapshot in test utility (SD-2836)

createTestPainter was overwriting the user-supplied onPaintSnapshot callback with its own, so tests that relied on the callback (source-anchor tests) saw an undefined snapshot. Chain the user callback after the internal one.

* test(pm-adapter): migrate integration tests to ResolvedLayout (SD-2836)

The three integration tests in pm-adapter were calling painter.paint(layout, mount) with raw Layout. They now resolveLayout() first and call painter.paint({ resolvedLayout }, mount). All 1794 pm-adapter tests pass.

* test(layout-bridge): migrate perf benchmark to ResolvedLayout (SD-2836)

The layout-bridge incremental-pipeline performance benchmark called painter.paint(layout, mount) and painter.setData(...). Both are removed by the API collapse. Migrate to resolveLayout() + DomPainterInput so the benchmark continues to exercise the painter under the new contract.

* chore(deps): declare @superdoc/layout-resolved as devDep where tests use it (SD-2836)

PR1 added test imports of @superdoc/layout-resolved in
pm-adapter/src/integration.test.ts and layout-bridge/test/benchmarks/index.ts
without declaring it as a devDependency. Both packages now resolve the
import locally via pnpm.

* fix(super-editor): honor PageDecorationPayload.items contract (SD-2836)

When resolveAlignedDecorationItems cannot align items 1:1 with fragments
(returns undefined), HeaderFooterSessionManager now bails out with null
instead of emitting a payload with items: undefined, which would violate
the now-required PageDecorationPayload.items contract from PR2.
normalizeDecorationItems narrowed to ResolvedPaintItem[] -> ResolvedPaintItem[].

Also refresh painter-dom README: drop blocks/measures/setData/paint(layout)
examples; document the ResolvedLayout-only paint() and the items-aligned-
with-fragments invariant on PageDecorationPayload.

* [3/3] test(painter): lock ResolvedLayout-only boundary (SD-2836) (#3118)

* test(painter): lock ResolvedLayout-only boundary (SD-2836)

* chore(painter): drop unused imports and skipped legacy tests (SD-2836)

renderer.ts: drop unused Layout, Page, Measure, FlowBlock, ParagraphBorder type imports left over from the migration. index.test.ts: delete the skipped 'decoration item synthesis' describe block (it was protecting the synthesis path that has been removed).

* chore(deps): regenerate lockfile after dropping layout-resolved runtime dep (SD-2836)

Moves @superdoc/layout-resolved to devDependencies in the lockfile to
match package.json, so CI's --frozen-lockfile install matches. Boundary
tests still need it under devDependencies.
luccas-harbour pushed a commit that referenced this pull request May 5, 2026
* refactor(contracts): host expandRunsForInlineNewlines (SD-2836)

Move the inline-newline run expander from @superdoc/pm-adapter into
@superdoc/contracts. The function is a pure transformation on Run[]
shapes already defined here; relocating it lets painter-dom consume the
helper without depending on pm-adapter (per the SD-2836 acceptance
criterion: no painter-dom imports from pm-adapter or layout-bridge).

pm-adapter chains its public re-export through contracts, keeping the
import path stable until painter-dom is migrated to consume directly.

* refactor(contracts): host sliceRunsForLine (SD-2836)

Move the line-aware run slicer from @superdoc/layout-bridge into
@superdoc/contracts, alongside expandRunsForInlineNewlines. The function
is a pure transformation on Run/Line shapes already defined here;
relocating it lets painter-dom consume the helper without depending on
layout-bridge (per the SD-2836 acceptance criterion).

layout-bridge chains its public re-export through contracts, keeping
the import path stable until painter-dom is migrated to consume directly.

Also restore a TrackedChangeMeta import in pm-adapter's paragraph.test.ts
that the prior commit dropped; the type is still referenced outside the
migrated describe block.

* refactor(painter): consume run helpers from contracts (SD-2836)

Switch the painter's two cross-package run-helper imports
(expandRunsForInlineNewlines, sliceRunsForLine) to source from
@superdoc/contracts directly, then drop @superdoc/pm-adapter and
@superdoc/layout-bridge from painter-dom's runtime dependencies.

This closes the painter-dom -> pm-adapter / layout-bridge import edge
called out in the SD-2836 acceptance criteria. Architecture-boundary
guards added in [3/3] of this stack will prevent reintroduction.

* refactor(layout): add fragment back-pointer to resolved items (SD-2836)

Add a `fragment: Fragment` field to ResolvedFragmentItem,
ResolvedTableItem, ResolvedImageItem, and ResolvedDrawingItem, and
populate it from the corresponding resolve* helper. The reference is
shared, not copied: items now carry the same Fragment object that lives
on the source page.

This is the precondition for stopping painter iteration over
`page.fragments`. The next commit drops getResolvedFragmentItem and
switches the painter to iterate ResolvedPage.items, reading the source
fragment via `item.fragment` instead of indexing back into the legacy
fragments array.

Includes focused tests in resolveLayout.test.ts asserting back-pointer
identity for each kind.

* refactor(painter): drop getResolvedFragmentItem; iterate resolved items (SD-2836)

Replace the three `page.fragments.forEach((fragment, index) => ...)`
loops in renderPage / patchPage / initPage with iterations over
`resolvedItems` (the resolved page's items), reading the source
Fragment via the back-pointer added in the previous commit.

Removes:
- getResolvedFragmentItem (parallel-array index lookup into resolved items)
- direct iteration of `page.fragments` from the painter render path

Updates affected hand-rolled resolved-layout tests to populate the new
required `fragment` back-pointer; the painter now treats items without
a fragment as not-yet-renderable rather than indexing back into the
legacy fragments array.

* refactor(painter): paint() body reads ResolvedLayout (SD-2836)

Switch paint()'s top-level reads from input.sourceLayout to
input.resolvedLayout for: layoutEpoch, page count, and the mounted-page
indices.

The local `layout = input.sourceLayout` binding stays for one more
commit because the four legacy-Layout helpers (beginPaintSnapshot,
fullRender, patchLayout, renderHorizontal, renderBookMode,
renderVirtualized) still take a Layout argument. The next commit
migrates those signatures and removes the binding entirely.

* refactor(painter): drop fragments param from sdt+border helpers (SD-2836)

computeSdtBoundaries and computeBetweenBorderFlags previously took both
the raw `fragments` array and the parallel resolvedItems array. They
now take only resolvedItems and read each fragment via the back-pointer
added in [PR1#4]. Updates all four call sites in renderer.ts plus the
between-borders test fixture.

This eliminates the painter's lookup into `page.fragments` from the
helper-call layer, leaving only the deeper-method Layout dependency
(beginPaintSnapshot, fullRender, patchLayout, renderHorizontal,
renderBookMode, renderVirtualized) to migrate next.

* refactor(painter): migrate internals to ResolvedLayout (SD-2836)

Switch DomPainter's internal state and helpers from raw Layout/Page to
ResolvedLayout/ResolvedPage:

* The six top-level legacy-Layout methods (beginPaintSnapshot,
  fullRender, patchLayout, renderHorizontal, renderBookMode,
  renderVirtualized) take ResolvedLayout. paint() drops the
  `const layout = input.sourceLayout` binding and calls every method
  with input.resolvedLayout.
* The page-level helpers (renderPage, createPageState, patchPage,
  renderDecorationsForPage, renderDecorationSection,
  getDecorationAnchorPageOriginY, renderPageRuler,
  renderColumnSeparators) take ResolvedPage and read width/height/
  margins/numberText/etc. directly. Drops every redundant
  `resolvedPage?: ResolvedPage | null` parameter.
* `currentLayout: Layout | null` -> `ResolvedLayout | null`. Strips
  the `(page.size ?? layout.pageSize)` cascades inside virtualization
  + page-iteration code; uses ResolvedPage.width/.height directly.
* Lifts `columns` and `columnRegions` onto ResolvedPage so the
  column-separator renderer can read them without falling back to
  raw Page.

Couples PageDecorationProvider (planned PR2 work) since the painter
now passes ResolvedPage to the third callback argument:

* `PageDecorationProvider`'s `page?` parameter is `ResolvedPage`.
* `HeaderFooterSessionManager.rebuildRegions` takes ResolvedLayout.
  `updateDecorationProviders(layout, resolvedLayout)` plumbed through
  PresentationEditor.
* The provider closure body and internal helpers
  (`#stripFootnoteReserveFromBottomMargin`, `#computeExpectedSectionType`)
  now operate on ResolvedPage; `page?.size?.h` -> `page?.height`.
* `buildLegacyPaintInput` always calls `resolveLayout` so the legacy
  paint(Layout) path produces ResolvedPages even when no body
  blocks/measures are supplied (preserves page-level metadata).

Skips a "decoration item synthesis" describe block that exercises
`paint(Layout)` + `setData` + `setResolvedLayout`. Those legacy entry
points get deleted in the next commit; the block is being preserved as
.skip so the deletion is visible in diff.

* chore(deps): regenerate lockfile after painter-dom dep cleanup (SD-2836)

Drops the now-unused @superdoc/layout-bridge and @superdoc/pm-adapter
entries from pnpm-lock.yaml so CI's --frozen-lockfile install matches
package.json.

* test(super-editor): synthesize ResolvedLayout in PresentationEditor mock (SD-2836)

rebuildRegions now iterates resolvedLayout.pages (was layout.pages). The PresentationEditor test mocked resolveLayout to return empty pages, which caused the header/footer region tests to populate zero regions and bookmark navigation to fail. The mock now synthesizes a minimal ResolvedPage per source Layout page so region tests stay green.

* refactor(painter,super-editor): address PR review feedback (SD-2836)

- Drop redundant pageSize parameter from createPageState/patchPage; read
  page.width/height directly from ResolvedPage.
- Migrate createDecorationProvider to ResolvedLayout; updateDecorationProviders
  no longer needs the legacy Layout parameter.
- Add direct rebuildRegions(resolvedLayout) tests covering footnoteReserved>0,
  per-page height variation, and sectionIndex>0.
- Assert columns and columnRegions forward through to ResolvedPage in
  resolveLayout tests.

* [2/3] refactor(painter): collapse legacy API surface (SD-2836) (#3117)

* refactor(painter): collapse legacy API surface (SD-2836)

Make ResolvedLayout the only painter input contract:

* DomPainterInput collapses to `{ resolvedLayout: ResolvedLayout }`.
  sourceLayout, blocks/measures, headerBlocks/Measures,
  footerBlocks/Measures all removed.
* DomPainterOptions: drop blocks/measures.
* DomPainterHandle: drop setData, setResolvedLayout. paint takes only
  DomPainterInput. Drops the `paint(Layout)` overload across painter,
  PresentationPainterAdapter, and (transitively) PresentationEditor's
  paintInput.
* createDomPainter shrinks to a thin pass-through. Removes
  buildLegacyPaintInput, normalizeDomPainterInput, isDomPainterInput,
  wrapProvider, resolveDecorationItems, normalizeOptionalBlockMeasurePair,
  assertRequiredBlockMeasurePair, createEmptyResolvedLayout,
  LegacyDomPainterState, OptionalBlockMeasurePair.
* PageDecorationPayload.items becomes required (the synthesis path
  is gone).

Tests:
* New `_test-utils.ts` exposes a legacy-shaped `createTestPainter`
  that test files import in place of `createDomPainter`. The helper
  builds a real DomPainterInput internally and synthesizes decoration
  items so existing test bodies don't have to be rewritten.
* All 17 painter test files migrated to the helper.
* Two source-anchor tests still failing under investigation;
  remaining work is bounded and tracked.

* fix(painter): chain user onPaintSnapshot in test utility (SD-2836)

createTestPainter was overwriting the user-supplied onPaintSnapshot callback with its own, so tests that relied on the callback (source-anchor tests) saw an undefined snapshot. Chain the user callback after the internal one.

* test(pm-adapter): migrate integration tests to ResolvedLayout (SD-2836)

The three integration tests in pm-adapter were calling painter.paint(layout, mount) with raw Layout. They now resolveLayout() first and call painter.paint({ resolvedLayout }, mount). All 1794 pm-adapter tests pass.

* test(layout-bridge): migrate perf benchmark to ResolvedLayout (SD-2836)

The layout-bridge incremental-pipeline performance benchmark called painter.paint(layout, mount) and painter.setData(...). Both are removed by the API collapse. Migrate to resolveLayout() + DomPainterInput so the benchmark continues to exercise the painter under the new contract.

* chore(deps): declare @superdoc/layout-resolved as devDep where tests use it (SD-2836)

PR1 added test imports of @superdoc/layout-resolved in
pm-adapter/src/integration.test.ts and layout-bridge/test/benchmarks/index.ts
without declaring it as a devDependency. Both packages now resolve the
import locally via pnpm.

* fix(super-editor): honor PageDecorationPayload.items contract (SD-2836)

When resolveAlignedDecorationItems cannot align items 1:1 with fragments
(returns undefined), HeaderFooterSessionManager now bails out with null
instead of emitting a payload with items: undefined, which would violate
the now-required PageDecorationPayload.items contract from PR2.
normalizeDecorationItems narrowed to ResolvedPaintItem[] -> ResolvedPaintItem[].

Also refresh painter-dom README: drop blocks/measures/setData/paint(layout)
examples; document the ResolvedLayout-only paint() and the items-aligned-
with-fragments invariant on PageDecorationPayload.

* [3/3] test(painter): lock ResolvedLayout-only boundary (SD-2836) (#3118)

* test(painter): lock ResolvedLayout-only boundary (SD-2836)

* chore(painter): drop unused imports and skipped legacy tests (SD-2836)

renderer.ts: drop unused Layout, Page, Measure, FlowBlock, ParagraphBorder type imports left over from the migration. index.test.ts: delete the skipped 'decoration item synthesis' describe block (it was protecting the synthesis path that has been removed).

* chore(deps): regenerate lockfile after dropping layout-resolved runtime dep (SD-2836)

Moves @superdoc/layout-resolved to devDependencies in the lockfile to
match package.json, so CI's --frozen-lockfile install matches. Boundary
tests still need it under devDependencies.

* fix(painter): adapt content-presence gate to ResolvedPage (SD-2836)

After rebasing PR1's painter migration onto main, the new column-separator
content-presence gate added by SD-2452 (a5ff6ed) reads page.fragments
directly. On the new architecture the painter receives a ResolvedPage whose
fragments live as page.items[]. Switch the in-region scan to iterate items
(every ResolvedPaintItem carries x/y).

Also extend the test-only createTestPainter shim to auto-synthesize minimal
ParagraphBlock/ParagraphMeasure entries for any layout fragment whose blockId
isn't covered by the supplied blocks. Tests that only exercise wrapper-level
rendering (column separators, page chrome) can keep using fragAt(...) without
boilerplate matching blocks.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants