Skip to content

Merge the 1.9.x line (through v1.9.1) into 2.0-dev#365

Merged
DemchaAV merged 23 commits into
2.0-devfrom
sync/main-into-2.0-dev
Jul 12, 2026
Merged

Merge the 1.9.x line (through v1.9.1) into 2.0-dev#365
DemchaAV merged 23 commits into
2.0-devfrom
sync/main-into-2.0-dev

Conversation

@DemchaAV

Copy link
Copy Markdown
Owner

Why

2.0-dev had diverged from main (the shipping 1.9.x line) by 22 commits since they last
synced at the modularization split. Before the 2.0 release, 2.0-dev must contain main's
full history so that at GA main can fast-forward to 2.0 with no divergence.

What

A merge -s ours of origin/main (through v1.9.1) into 2.0-dev. The merge records
main as an ancestor without changing the treegit diff HEAD origin/2.0-dev is empty
(0 files). That is deliberate and correct:

After this lands, git log 2.0-dev..main is empty.

⚠️ Merge method — MUST be a merge commit or fast-forward

This PR carries a merge commit whose value is its two parents. Do NOT squash or rebase
merge
— that flattens it to a single parent, drops main's ancestry, and re-opens the
divergence (the GA fast-forward of main would then be rejected). Use "Create a merge
commit"
, or fast-forward 2.0-dev to this branch directly
(git push origin sync/main-into-2.0-dev:2.0-dev).

Tests

The tree is byte-identical to 2.0-dev (0 file changes), so the existing green 2.0-dev
gate stands unchanged — there is nothing new to build. Verified: two parents
(4de541c8 2.0-dev + 8c9ce1cb main), git merge-base --is-ancestor origin/main HEAD passes.

DemchaAV and others added 23 commits June 28, 2026 18:41
* test(api): extend the engine-leak guard to all public document.* packages

PublicApiNoEngineLeakTest pinned only eight document.* packages, while
output, snapshot, theme, exceptions, and emoji are equally public and must
stay free of com.demcha.compose.engine.* imports. Add all five to
PUBLIC_API_ROOTS; each is already engine-clean so the guard passes and
locks the property in. Engine-adjacent internals (layout, backend,
templates, debug) stay excluded by design.

* docs(api): complete v1.9.0 since/deprecation metadata and date-ready CHANGELOG

- DocumentSession.pageMargins(List<PageMarginRule>) gains @SInCE 1.9.0,
  matching its sibling 1.9.0 methods (viewerPreferences, pageIndex, toImage).
- HeadlineRenderer / ContactRenderer / BannerRenderer become
  @deprecated(since = "1.9.0", forRemoval = true) with the @deprecated Javadoc
  leading "since 1.9.0; removed in 2.0.", matching CoverLetterTemplate and the
  api-stability deprecation ledger.
- CHANGELOG in-progress header "v1.9.0 - unreleased" -> "v1.9.0 - Planned" so
  the release tooling rewrites it to the ISO date at cut time.
…ation guide (#254)

- README "Release status" now names v1.9.0 (codename "navigable") as Latest
  stable, and the "What's new" section covers in-document navigation, the
  native TOC / page references / bookmarks, multi-section documents, the
  per-page-margin / bleed / row-layout additions, inline chips / SVG / emoji,
  and render-to-images. Install snippets stay at 1.8.0 (the release tooling
  flips them at cut time).
- Add docs/roadmaps/migration-v1-8-to-v1-9.md — additive-only upgrade guide
  with a per-area TL;DR table, the one negative-margin behaviour note, the
  2.0-bound shim deprecations, and the upgrade snippet.
- Index the v1.8->v1.9 and the previously unlisted v1.7->v1.8 guides in
  docs/README.md.
…navigation (#256)

Four v1.9.0 features visible in a rendered document had no gallery presence.
Add a row + detailed showcase section + a committed preview render for each,
with the API names read off the runnable *Example classes:

- Inline highlight chips (RichText.code / chip / highlight) — Core DSL.
- Inline SVG icons (RichText.svgIcon) — Core DSL.
- Colour emoji by shortcode (RichText.emoji) — Core DSL.
- In-PDF navigation (anchor / linkTo / inlineLinkTo / shapeLinkTo) — Advanced SPI.

The emoji preview uses the 44 KB shortcode showcase; the full emoji-gallery
render is 3.9 MB, too large for a committed README asset.
…h 2 updates (#259)

Bumps the maven-minor-patch group with 2 updates in the / directory: [org.junit:junit-bom](https://github.com/junit-team/junit-framework) and [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback).
Bumps the maven-minor-patch group with 2 updates in the /benchmarks directory: [org.junit:junit-bom](https://github.com/junit-team/junit-framework) and [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback).
Bumps the maven-minor-patch group with 2 updates in the /examples directory: [org.junit:junit-bom](https://github.com/junit-team/junit-framework) and [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback).


Updates `org.junit:junit-bom` from 6.1.0 to 6.1.1
- [Release notes](https://github.com/junit-team/junit-framework/releases)
- [Commits](junit-team/junit-framework@r6.1.0...r6.1.1)

Updates `ch.qos.logback:logback-classic` from 1.5.34 to 1.5.37
- [Release notes](https://github.com/qos-ch/logback/releases)
- [Commits](qos-ch/logback@v_1.5.34...v_1.5.37)

Updates `org.junit:junit-bom` from 6.1.0 to 6.1.1
- [Release notes](https://github.com/junit-team/junit-framework/releases)
- [Commits](junit-team/junit-framework@r6.1.0...r6.1.1)

Updates `ch.qos.logback:logback-classic` from 1.5.34 to 1.5.37
- [Release notes](https://github.com/qos-ch/logback/releases)
- [Commits](qos-ch/logback@v_1.5.34...v_1.5.37)

Updates `org.junit:junit-bom` from 6.1.0 to 6.1.1
- [Release notes](https://github.com/junit-team/junit-framework/releases)
- [Commits](junit-team/junit-framework@r6.1.0...r6.1.1)

Updates `ch.qos.logback:logback-classic` from 1.5.34 to 1.5.37
- [Release notes](https://github.com/qos-ch/logback/releases)
- [Commits](qos-ch/logback@v_1.5.34...v_1.5.37)

---
updated-dependencies:
- dependency-name: org.junit:junit-bom
  dependency-version: 6.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: maven-minor-patch
- dependency-name: ch.qos.logback:logback-classic
  dependency-version: 1.5.37
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: maven-minor-patch
- dependency-name: org.junit:junit-bom
  dependency-version: 6.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: maven-minor-patch
- dependency-name: ch.qos.logback:logback-classic
  dependency-version: 1.5.37
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: maven-minor-patch
- dependency-name: org.junit:junit-bom
  dependency-version: 6.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: maven-minor-patch
- dependency-name: ch.qos.logback:logback-classic
  dependency-version: 1.5.37
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: maven-minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the github-actions group with 1 update: [actions/cache](https://github.com/actions/cache).


Updates `actions/cache` from 5 to 6
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](actions/cache@v5...v6)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Artem Demchyshyn <132658418+DemchaAV@users.noreply.github.com>
… release (#260)

The README hero (assets/readme/repository_showcase_render.png) carried a
hardcoded version constant and was rasterised by hand, so it drifted — it still
read v1.8.0 while the repo was on 1.9.0. Now it is single-sourced and rendered
straight from the engine:

- EngineDeckExample reads its version + codename from a Maven-filtered
  banner.properties (version=@project.version@, codename=navigable) instead of
  hardcoded constants, so the hero always carries the reactor version.
- The new ReadmeBannerRenderer writes the PNG straight from the engine via
  DocumentSession.toImage(0, dpi) — no render-to-PDF-then-PdfPageRasterizer
  round-trip. EngineDeckExample.renderBannerImage shares the banner composition
  with the (kept) PDF generateBanner via composeBannerInto.
- cut-release.ps1 re-renders the banner after the version bump and stages it in
  the release commit (gated under -not -SkipShowcase), so the committed hero
  ships in lockstep with every tag.
- VersionConsistencyGuardTest fails the build if banner.properties stops sourcing
  the version from @project.version@; ReadmeBannerRendererTest covers the render,
  the PNG write, and that filtering actually resolved.

The hero keeps its tight pageSize crop: the whole page is the banner via
pageBackground, which bleed() does not replace.
…9.0 emoji feature (#261)

The "What's new in v1.9" block names colour emoji via the independently-versioned
graph-compose-emoji module, but the install section gave no coordinate for it —
only the fonts companion artifact had a copy-paste dependency block. Add a
parallel graph-compose-emoji (1.0.0) snippet, noting emoji is opt-in (the bundle
stays fonts-only) and that an unknown shortcode falls back to literal text.
…o stamps the bumped version

Render-ReadmeBanner ran `exec:java ReadmeBannerRenderer` without compiling the
examples module. banner.properties is filtered to ${project.version} at
examples-compile time, and the cut never recompiles examples after the Step-1
version bump — so the re-rendered hero carried the PREVIOUS release version
(e.g. a v1.9.0 cut would have stamped "v1.8.0"). Add `compile` to the exec so
the module is rebuilt at the bumped version and banner.properties re-filters
before the render.
…tes guides (#263)

Add four developer-facing pages and wire them into the docs index.

- first-document.md — a five-minute path from an empty project to a
  rendered PDF: the smallest document, a multi-section custom flow, the
  built-in template shortcut, and server streaming.
- capabilities.md — a feature-to-API map giving the main call, the
  stability tier, and a guide link for each capability; the API stability
  policy stays authoritative for the contract.
- diagrams.md — Mermaid decision diagrams for the authoring path, content
  placement, output destination, and document lifecycle.
- templates/business-templates.md — the built-in InvoiceTemplateV2 and
  ProposalTemplateV2 compose-first contract, end to end, including the
  server-stream variant.

getting-started.md gains a one-line pointer to first-document.md, and the
docs README persona table and category index link the new pages.
The GitHub Pages deploy fired on both the main push and the v* release tag, but
the tag run failed every release: Pages refuses to deploy from a tag ref (the
github-pages environment only allows main), so it produced a red X without ever
deploying — and via cancel-in-progress could race-cancel the good main run. The
unconditional `push: branches: [main]` trigger (no paths filter) already covers
releases reliably, so the tag trigger is redundant. Remove it.
* test(docs): compile published Java snippets to catch API drift

DocumentationExamplesTest renders hand-kept Java copies of examples, but
nothing compiles the literal docs/*.md fences, so a published snippet can
drift from the API while the build stays green.

DocumentationSnippetCompileTest walks docs/**/*.md and, for each java fence
carrying an opt-in marker comment <!-- doc-example: id=... mode=... -->,
wraps it per mode (method / members / class) and compiles it in-memory via
ToolProvider.getSystemJavaCompiler() against the test classpath. Compiler
errors fail the test and name the snippet id + file; a second test guards
marker well-formedness, and the run asserts at least one snippet is found so
a moved docs folder cannot make the guard pass vacuously. Markers are HTML
comments, invisible on GitHub; teaching fragments stay unmarked.

Marks the five self-contained snippets in first-document.md (2) and
business-templates.md (3). Complements DocumentationExamplesTest, which
renders example copies; this guard pins the published markdown to the API.

* test(docs): self-test the snippet guard and tighten its parsing

Make DocumentationSnippetCompileTest verify its own failure path so a
regression in the parse/wrap/compile pipeline cannot pass vacuously:

- compilerReportsErrorForBrokenSnippet feeds a snippet that references a
  missing symbol and asserts the error is surfaced and attributed to its id.
- knownCanonicalTypeResolvesOnTestClasspath compiles a trivial canonical
  call, so a classpath failure is distinguishable from a documentation break.

Also harden the mechanics: attribute diagnostics to snippets by file-object
identity instead of a substring match; lift only the leading run of imports
(an import-shaped line inside a body stays put); match the java fence exactly
(no `javascript`); create the compiler output dir inside the try; guard
sanitized unit-name uniqueness. Drop the unused class mode.
#267)

The showcase site links to 85 example previews but only 53 are committed; the
other 32 — navigation, emoji, inline SVG/icons, layout, multi-section, charts,
debug overlay and the engine-deck / feature-catalog / financial-report
flagships — 404 on the live site.

Root cause: the blanket *.pdf ignore whitelists the old docs/showcase and
site/public/showcase locations but never web/showcase, where the live site now
lives, so every newly added preview was silently ignored and left out of the
release commit.

- .gitignore: whitelist web/showcase/**/*.pdf, matching the older site copies,
  so showcase previews are tracked and future examples ship automatically.
- Commit the 32 missing previews under web/showcase/pdf/** (regenerated with
  GenerateAllExamples).

Verified every pdf path in web/examples.json (85) resolves to a committed file;
the 53 already-tracked previews and examples.json are untouched.
#268)

30 showcase cards linked their "View Code" to a path that 404s. 26 examples
were never registered in ShowcaseMetadata, so the manifest fell back to the
examples-root URL with a filename-derived title and a generic description; 4
more (debug-overlay, block-align, svg-icon-gallery, vector-path) resolved
through the group→class switch to the wrong or a non-existent class. The
switch guessed the class from the group, which cannot work once a group holds
one class per id (layout, navigation, text, …).

- feature()/flagship() now take the example class explicitly, like cv() /
  letter(), dropping the brittle group→class switch.
- Register the 26 missing examples — in-document navigation, colour emoji,
  inline runs, per-page layout, charts, multi-section, the engine-deck /
  feature-catalog / financial-report flagships — each with a curated title, a
  one-line description naming the concrete DSL API, and tags.
- Point the 4 mis-resolved links at their real class (vector-path →
  VectorPathExample, etc.).
- groupLabel: add charts, navigation, structure, title, docx.
- Regenerate web/examples.json: exactly those 30 entries change, the other 55
  stay byte-identical, links kept at /blob/v1.9.0.

Verified the examples module compiles (ShowcaseSync BUILD SUCCESS) and every
code URL in web/examples.json (85/85) resolves to a committed .java file.
* fix(engine): break over-wide inline-code tokens within their column

Long code tokens with no whitespace — package coordinates, FQCNs, URLs
like org.junit.jupiter:junit-jupiter:5.10.2 — overflowed a paragraph or
table cell instead of wrapping, drawing over the neighbouring content.
All three paragraph wrap paths placed a lone over-wide token as-is, and
highlight-chip words were never broken at all.

Route an over-wide token through a new breakLongToken helper on the plain,
inline, and markdown wrap paths. It breaks at soft seams (. : / -) so
coordinates and URLs split at readable boundaries, and char-splits any
segment still too wide as a last resort. Highlight-chip words break the
same way while keeping one rounded fill per visual fragment: the split
pieces retain the run's highlight group and background, with the outer
padding on the first and last fragment only and the break seams open.

Tokens that already fit follow the unchanged fast path, so existing prose
and layouts are byte-identical (full suite green, no snapshot drift).

* feat(engine): size AUTO table columns to composed cell content

A composed table cell (DocumentTableCell.node(...)) contributed no natural
width, so an AUTO column holding only composed content collapsed toward zero
and its child — an inline-code chip, say — was laid out at a near-zero width.

Measure a composed cell's intrinsic content width in resolveNaturalColumnWidths
and feed it into the AUTO column's natural width, reusing the same
prepare-to-measure pattern as RowSlots.intrinsicColumnWidths. The child is
measured against the table's inner width, so one cell can never demand more
than the table can give. Only single-column composed cells in AUTO columns are
measured; plain-text cells, FIXED columns and spanning cells are unchanged, so
a chip in a FIXED column keeps its declared width and breaks inside it.

As with plain-text AUTO columns, a table too narrow for the summed intrinsic
width of its AUTO columns still reports "exceeds available width".

* docs(examples): add inline-code column-wrap example + CHANGELOG entry

InlineCodeColumnWrapExample renders a long inlineCode(...) coordinate breaking
at its . : / - seams inside a narrow fixed column and fitting on one line in an
auto column, registered in GenerateAllExamples with a committed preview PDF and
a README quick-reference row. CHANGELOG v1.9.1 documents the chip-overflow fix
and the auto-column sizing.
Bumps the maven-minor-patch group with 1 update in the / directory: [net.bytebuddy:byte-buddy](https://github.com/raphw/byte-buddy).


Updates `net.bytebuddy:byte-buddy` from 1.18.10 to 1.18.11
- [Release notes](https://github.com/raphw/byte-buddy/releases)
- [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md)
- [Commits](raphw/byte-buddy@byte-buddy-1.18.10...byte-buddy-1.18.11)

---
updated-dependencies:
- dependency-name: net.bytebuddy:byte-buddy
  dependency-version: 1.18.11
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: maven-minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Artem Demchyshyn <132658418+DemchaAV@users.noreply.github.com>
* refactor(engine): extract TokenBreaking from TextFlowSupport

The token-breaking family — breakLongToken, softBreakSegments, splitLongToken
and fitCharacters — was pure string logic reachable only through breakLongToken
from the three wrap loops, yet covered only end-to-end via the paragraph render
tests. Move it verbatim into a package-private TokenBreaking; TextFlowSupport
now calls TokenBreaking.breakLongToken(...) at its three wrap sites. Behaviour is
unchanged (methods lifted as-is) and the wrap file drops ~125 lines.

Add TokenBreakingTest: direct coverage of seam segmentation, greedy packing, the
char-level fallback and the min-one-char guarantee, driven by a deterministic
fixed-width measurement double so the assertions do not depend on font metrics.

* refactor(engine): harden the TokenBreaking helper surface

Guard softBreakSegments against null/empty (splitLongToken already did, and both
are now package-visible directly-callable entry points), add the @author tag to
match the sibling layout helpers, and cover the null/empty and fits-whole edges
directly in TokenBreakingTest.
#314)

Flip the Release-status blockquote to name v1.9.1 as latest stable (the
inline-code column-wrap fix) so the release commit carries it to main, and add
graph-compose-markdown to Companion projects — a Markdown input surface built on
the engine, published on Maven Central (io.github.demchaav:graph-compose-markdown),
linked to its Central artifact page since it has no public repo.
In the lightbox header the action buttons could shrink to their min-content
(the longest word) and wrap ("Open"/"PDF" on two lines) whenever the card title
was long enough to squeeze them — so the buttons looked inconsistent between
cards. Give .example-action white-space: nowrap + flex-shrink: 0 and .lightbox-
actions flex-shrink: 0 so the buttons keep a stable single-line size, and give
.lightbox-title min-width: 0 so a long title wraps instead of compressing them.
Records main as an ancestor of 2.0-dev so the branch carries the full
shipped history and can fast-forward main at the 2.0 release. The tree is
kept exactly at 2.0-dev: the 1.9.x engine work (TokenBreaking extraction,
inline-code column containment) is already present via the earlier port,
and the remaining main-only commits are release version stamps and
regenerated showcase artifacts that 2.0 rebuilds at release time.
@DemchaAV DemchaAV merged commit efd59dc into 2.0-dev Jul 12, 2026
10 checks passed
@DemchaAV DemchaAV deleted the sync/main-into-2.0-dev branch July 12, 2026 00:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant