Build: gulp 5 / Antora 3.1.15 / Yarn 4.18, DocSearch v5, and UI preview tooling - #1729
Build: gulp 5 / Antora 3.1.15 / Yarn 4.18, DocSearch v5, and UI preview tooling#1729ammachado wants to merge 19 commits into
Conversation
Fixes a silent build bug and modernises both gulp pipelines. The UI bundle was being produced empty (22 bytes, 0 files). The build task in antora-ui-camel is an async function that returned a stream, and gulp settles an async task on its promise; a stream is not a thenable, so bundle:pack ran against a dest that had not been written yet. The pipeline is now awaited explicitly. This only affected consumers of ui-bundle.zip, since the production playbook reads the staged directory instead. Theme moved to gulp 5, vinyl-fs 4, undertaker 2, through2 5 and fs-extra 11: - vinyl-fs 4 is built on streamx, and merge-stream is a node-core PassThrough that silently drops files from streamx sources (93 files in, 3 out). Replaced with ordered-read-streams, the streamx-native merge. - gulp 5 decodes contents as UTF-8 by default, so binary sources now pass encoding: false. All 58 fonts and images verified byte-identical to the gulp 4 output. - Removed the font glob in build.js. src/font has never existed; the fonts are copied into dest/font by the postcssUrl handler. vinyl-fs 3 ignored the missing directory, vinyl-fs 4 raises ENOENT. PostCSS plugins updated to the PostCSS 8 API. cssnano 8 returns a plugin object rather than a callable, and the bare (css, result) plugins were PostCSS 7 style and were no longer being invoked. postcss-custom-properties 15 keeps the :root block after substitution, so resolved custom properties are now dropped explicitly; the generated CSS is 94 bytes smaller than before with prefixes and media queries unchanged. Root gulpfile moved to gulp 5. Replaced del with fs.rm, as del 7+ is ESM only, and fixed generate-markdown signalling completion twice by both calling done() and returning a promise. Antora 3.1.9 added a warning that fires when an extension's register function names its first parameter 'registry'. Two of the @djencks extensions do, and runtime.log.failure_level is set to warn, so the advisory warning failed the build. Those packages were last published in 2022 and cannot be fixed upstream, so they are required via thin local wrappers in extensions/. Antora now completes with no output at all. Also removes max-width: var(--static-max-width) from .blog. The variable was never declared, so the declaration was invalid at computed-value time and max-width already resolved to none. This matches .static, which constrains width only in the desktop media query. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The `checks` CI job runs `yarn check:dependencies`, which failed because 21 transitive packages could be deduped using the highest strategy. The drift is unrelated to the gulp/Antora upgrade; the lockfile simply went stale as newer versions of caniuse-lite, nanoid, end-of-stream, fastq, fs-extra, postcss, streamx, resolve, readable-stream and svgo published. Ran `yarn update:dedupe`. Both `check:cache` and `check:dedupe` now exit 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Updates the website build toolchain by upgrading both the root gulp pipeline and the Antora UI theme pipeline to gulp 5, upgrading Antora to 3.1.15, and adjusting related build steps (PostCSS 8 plugin API, binary asset handling, and Antora extension wrappers) to keep the build reliable and reproducible.
Changes:
- Upgrade Antora CLI/site-generator to 3.1.15 and wrap legacy Antora extensions to avoid warn-level build failures.
- Upgrade gulp pipelines to gulp 5 (including stream handling fixes and binary-safe asset processing).
- Update UI theme build outputs and PostCSS pipeline (cssnano/PostCSS 8 plugin API migration) and apply a small CSS cleanup.
Reviewed changes
Copilot reviewed 17 out of 19 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| package.json | Bumps Antora to 3.1.15, upgrades gulp to v5, removes del. |
| gulpfile.js | Updates task async completion patterns and replaces del with fs.rm. |
| gulp/tasks/generate-markdown.js | Fixes async completion by removing callback usage from async task. |
| extensions/asciidoctor-jsonpath.js | Adds wrapper to avoid Antora 3.1.9+ register-signature warning behavior. |
| extensions/asciidoctor-antora-indexer.js | Adds wrapper to avoid Antora 3.1.9+ register-signature warning behavior. |
| antora-ui-camel/src/css/blog.css | Removes an ineffective/invalid max-width declaration. |
| antora-ui-camel/public/_/rev-manifest | Updates compiled asset hash mapping for new CSS output. |
| antora-ui-camel/public/_/partials/head-styles.hbs | Updates stylesheet link to new hashed CSS filename. |
| antora-ui-camel/public/_/helpers/asset.js | Updates embedded manifest mapping for new hashed CSS filename. |
| antora-ui-camel/public/_/data/rev-manifest.json | Updates JSON manifest mapping for new hashed CSS filename. |
| antora-ui-camel/public/_/css/site-f06a797174.css | Removes old compiled CSS asset. |
| antora-ui-camel/public/_/css/site-00920fbc4a.css | Adds new compiled CSS asset with updated output. |
| antora-ui-camel/package.json | Upgrades theme build deps for gulp 5/PostCSS 8 and raises Node engine floor. |
| antora-ui-camel/gulp.d/tasks/remove.js | Updates through2 API usage for gulp 5 dependency stack. |
| antora-ui-camel/gulp.d/tasks/pack.js | Ensures binary assets are packed without UTF-8 decoding under gulp 5. |
| antora-ui-camel/gulp.d/tasks/build.js | Replaces merge-stream with ordered-read-streams; updates PostCSS plugin API; awaits pipeline completion to avoid empty bundles. |
| antora-ui-camel/gulp.d/tasks/build-preview-pages.js | Updates stream merging + completion semantics for gulp 5 stack. |
| antora-playbook-snippets/antora-playbook.yml | Switches extension requires to local wrappers to prevent warn-level build failures. |
Files not reviewed (1)
- antora-ui-camel/public/_/css/site-00920fbc4a.css: Generated file
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…s job The detect-drafts job never installs dependencies, so setup-node@v5's new package-manager-cache default (true) fails the post-step with "Path Validation Error" since there's no cache folder to save, failing the job despite the draft-detection logic succeeding. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
cssnano 8.x (bumped from ~4.1 as part of the gulp 5 / PostCSS 8 migration) declares engines.node >=22.11, since postcss-merge-longhand uses Set.prototype.isSubsetOf, which Node only ships unflagged from v22. Under Node 20 this threw inside the CSS build stream; the error was swallowed by an unhandled 'error' event on a source stream fed into ordered-read-streams, so the task just hung until gulp-cli's "did not complete" exit message, with no visible stack trace in CI. Verified locally: gulp bundle, yarn build-all, and yarn checks (link checker aside, which hardcodes a Linux binary and can't run on macOS) all pass under Node 22.20.0. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
🚀 Preview is available at https://pr-1729--camel.netlify.app |
Fixes the stale compat/typescript patch-hunk warning during install (patch hash no longer matched typescript 5.9.3 under the old Yarn). Also tightens approvedGitRepositories to [] (no git-protocol deps in this repo) and sets npmMinimalAgeGate to 4320 minutes (3 days) to delay trusting newly-published npm package versions. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
🚀 Preview is available at https://pr-1729--camel.netlify.app |
…handling The DocSearch v5 migration dropped the result curation the previous Algolia bundle performed, and routed @docsearch/css through postcss-custom-properties with preserve: false, which flattened away its runtime cascade. Search curation (src/js/08-docsearch.js): * restore the sub-project exclusion via transformItems * restore core-docs-over-components ranking; DocSearch groups by hierarchy.lvl0 into an insertion-ordered object, so the emitted order decides group order * cap each parent page at 2 hits. The original dedupe needed the query to spot a direct parent match and transformItems never receives it, so cap by page instead. Without this a query like "timer" fills the first group with anchors from a single document * fetch 50 hits rather than the DocSearch default of 20. The index has no attributeForDistinct and no attributesForFaceting, so both filters run after Algolia has picked its window and the default starves the result list Stylesheet delivery (gulp.d/tasks/build.js): * give @docsearch/css its own build stream that skips the custom property pass, mirroring how the DocSearch UMD bundle already ships as vendor JS. This keeps the mobile --docsearch-spacing overrides and the data-theme=dark block live. Preferred over preserve: true, which costs 3.8 KB gzip site-wide and would reverse a convention predating this branch * fail loudly when the @docsearch/js UMD path cannot be resolved Custom property plugin (gulp.d/lib/drop-resolved-custom-properties.js): * only drop definitions postcss-custom-properties actually substituted. Matching :root by selector string and recursing into at-rules emptied the @media block @docsearch/css ships, and missed resolved :root, :host list forms Tests: 16 cases covering both the plugin and the search transform. The transform suite evaluates the shipped IIFE against stubbed globals and replays captured Algolia responses, so it exercises the real file rather than a copy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Code review follow-up (d84cef1)A review pass over the DocSearch v5 migration turned up six findings; all are addressed in Search result curation was silently droppedThe deleted
That last one is a real regression fix, not tuning. Both filters run client side, so they operate on an already-truncated window. Distinct parent pages surviving the sub-project filter, measured against the live index:
At the default, Why this is worked around in the browser at allThe durable fix belongs at the index level, and there is more going on there than is obvious. Filed as CAMEL-24396. Summary of what I verified against the live index:
So the client-side workaround is currently the only lever available, and the ticket's first step is confirming whether One thing worth watching during review: DocSearch v5 uses
|
|
🚀 Preview is available at https://pr-1729--camel.netlify.app |
Both found by driving the deployed preview at pr-1729--camel.netlify.app. DocSearch renders its modal footer as a bare <footer> element, so the global `footer` rule in footer.css applied to it. DocSearch sets `height` but never `min-height`, so our `min-height: var(--footer-height)` won outright and stretched the modal footer to 23rem (391px) against an expected 52px on desktop and 48px on mobile. On mobile the modal is full screen, so the footer took 391px of 732px, over half the viewport. Scoped the rule to `body > footer`, which also stops the `color` and `font-size` from leaking; verified the site footer is a classless direct child of body on the Antora and Hugo page types. The DocSearch button is a fixed 38px, and `.navbar-search` was a block container relying on padding alone to position it. In the 73px desktop bar that left the button centred at 28px against 36.5px for the nav links. Made the container flex with `align-items: center`, which brings it to 36.5px exactly. `text-align: right` no longer applies to a flex container, so it is replaced with `justify-content: flex-end`. Mobile is unchanged: the 55px container is exactly 38px plus padding, so padding alone already centred it there. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
🚀 Preview is available at https://pr-1729--camel.netlify.app |
Search results rendered with an empty title, collapsed into a single grey
breadcrumb line. DocSearch derives the title attribute as
`hierarchy.${item.type}`, and this index is not built by DocSearch's standard
record extractor, so its records carry no `type`. The expression became the
literal 'hierarchy.undefined' and resolved to nothing.
transformItems now assigns the deepest populated hierarchy level as `type`, so
a hit on #_component_option_checkCrcs titles as "checkCrcs" with
"Camel Components > Components > Kafka" as subtext. Verified on the deployed
preview by re-initialising DocSearch with the patched config. This is a
workaround; the records should carry `type` and `anchor` - see CAMEL-24396.
Raised hitsPerPage from 50 to 75. Measured against the live index at the
current cap, this lifts "timer" from 5 to 8 distinct pages and "aggregate" from
9 to 13. 100 adds almost nothing beyond 75 (only "rest dsl", 8 to 9 pages) but
doubles the response from ~18 KB to ~36 KB gzipped.
Left MAX_HITS_PER_PAGE at 2. Raising it is counterproductive: DocSearch caps
each lvl0 group at 5 results, so a larger per-page cap spends those slots on
repeats from one document and yields fewer distinct pages, not more ("timer"
drops from 5 pages to 4 at a cap of 3).
Note that thin result sets for "Kafka" and "file" are not addressable here: 98
of the top 100 hits for "Kafka" are anchors on kafka-component.html, so no
client-side window size recovers pages that Algolia never returns. That needs
server-side distinct.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
🚀 Preview is available at https://pr-1729--camel.netlify.app |
Restyle the DocSearch v5 widget to match the site palette. Camel orange takes the accents the vendor stylesheet renders in Algolia blue, group headings pick up the ASF dark blue used for page headings, and the launch button matches the navbar buttons. The overrides are declared on .DocSearch rather than :root, and with literal values rather than var(--color-*), because the build resolves and then strips every :root custom property from site.css. Both are silent failure modes, so they are covered by tests. Also fixes the query text clipping in the search box. The vendor fixes the box at 56px while sizing its padding and font in rem, and this site's root font-size is 18px rather than the 16px DocSearch assumes, which left a 19px content box around a 25.2px line. Sizing the box in rem keeps it in step with the text. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The declaration is inherited from the default Antora UI, which positions td.icon absolutely and so loses nothing when the icon column collapses. This UI keeps the icon in flow as a vertical label bar, which needs the column to have width; under fixed layout it gets none, because td.content is width: 100%. It has never taken effect in production: extensions/table.js wraps every table in a div.table-wrapper, so the table is a grandchild of .admonitionblock and the child combinator never matches. Removing it is a no-op for the built site (verified: the only change to the compiled stylesheet is this one declaration) and it fixes gulp preview, which renders with plain Asciidoctor and no Antora extensions. It also removes a landmine: any change to extensions/table.js would otherwise collapse every admonition on the site. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…extensions The preview rendered with plain Asciidoctor while the site build registers postprocessors through antora-playbook-production.yml, so the two disagreed. table.js is the one that matters: it wraps every table in a div.table-wrapper, and without it the preview collapsed the icon column of every admonition. Register table.js and inline-styles.js so preview markup matches the built site. @asciidoctor/tabs is registered by the playbook but deliberately left out: the 1.0.0-beta.3 build targets the Opal runtime of asciidoctor.js 2 and throws on require under the @asciidoctor/core 3 this UI depends on. The site build is unaffected, since Antora supplies its own Asciidoctor. Add three example pages covering the parts of the UI that needed a closer look: admonitions, source highlighting, and tables. Content is original and Camel-flavoured rather than lorem ipsum, so it exercises realistic material. The admonitions page documents one finding: the label bar is sized by the table's auto layout, so it widens from 33px to 63px or 137px once the block sits inside a list item or a table cell. No page on the site nests an admonition that way today. No change to the shipped UI bundle; preview-src is not packaged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…heck Move the unit tests into a gulp task. gulp.d/tasks/test.js drives node:test through its run() API rather than spawning `node --test`, so gulp settles on the returned promise and a failure fails the task it is sequenced into. The task is now part of the bundle series (clean, lint, test, build, pack), and the package scripts delegate to it, so `yarn build` no longer runs the tests separately. Register @springio/antora-extensions/asciinema-extension. It supplies the [asciinema] block, injects asciinema-player's JS and CSS into the UI catalog, and adds the asciinema-styles, asciinema-load-scripts and asciinema-create-scripts partials. asciinema-player arrives as a transitive dependency, so it needs no entry of its own. Replace @bonitasoft/antora-detect-unused-media-extension with a local equivalent. That extension logs one warning per unused file and this playbook sets runtime.log.failure_level to warn, so a single stray image would have failed the site build. extensions/detect-unused-media.js logs at info instead and writes build/unused-media.txt, keeping the report without the failure. It still ignores .cast by default so asciinema recordings are not reported. Add preview pages for tabs and asciinema. Neither renders in gulp preview: @asciidoctor/tabs 1.0.0-beta.3 targets the Opal runtime of asciidoctor.js 2 and cannot load under the @asciidoctor/core 3 the UI depends on, and the asciinema block comes from an Antora extension the standalone preview does not run. Both pages say so. Also drops a committed vim swap file. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The prev/next links drew a dashed rule across the full half-width of the footer. .doc a paints its underline with a repeating background-image gradient, and nav.pagination span is a column flex container, so the anchor stretched to the container width and the gradient painted all of it. align-items keeps the anchor at its text width; the links now sit in bordered boxes. Reference the asciinema partials from head-styles.hbs and footer-scripts.hbs, guarded on the page-asciinemacasts attribute the extension sets. The guard keeps the player assets off pages with no recording, and keeps gulp preview from failing on partials that only exist during an Antora build. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Superseded by extensions/detect-unused-media.js, which reports the same thing at info level so it cannot trip runtime.log.failure_level: warn. Updates the two places that still named the package. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The [asciinema] block comes from @springio/antora-extensions, an Antora extension, so it never ran under gulp preview: the blocks stayed literal and the page could not be checked without a full site build. Reproduce the parts of it the preview needs, reusing the extension's own partials, helpers and player assets from node_modules rather than copying them: an image$ include processor so preview pages use the same include syntax a real component does, the asciinema block with the same md5-derived id and _asciinema/<id>.cast layout, and registration of the partials the UI templates already guard on. The cast ids are collected into a plain object instead of being set on the document, mirroring what the extension does with file.asciidoc.attributes. Asciidoctor rolls back attributes assigned from the body once parsing ends, so a page- attribute set from the block processor is gone by the time getAttributes() runs. Add a recorded camel-jbang TUI session as the example cast. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The recorder decoded each read() chunk on its own, so a multi-byte sequence split across a chunk boundary became U+FFFD. Sixty of them survived into the committed cast, and each one costs a column: a 2-wide emoji rendered as a 1-wide replacement box, so every row containing one drifted left and the next redraw left stale characters behind. The visible symptom was a ghost row under the tab bar. Re-recorded with an incremental decoder, which brings it to zero replacement characters and a clean header. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
🚀 Preview is available at https://pr-1729--camel.netlify.app |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 56 out of 67 changed files in this pull request and generated 2 comments.
Files not reviewed (1)
- antora-ui-camel/public/_/css/site-74543f5b13.css: Generated file
Suppressed comments (2)
antora-ui-camel/src/css/footer.css:4
- Scoping only this rule does not fully isolate the site footer. The later
@media (width <= 1024px) { footer { flex-direction: column; } }still matches DocSearch's<footer class="DocSearch-Footer">, changing its action/logo row into a column on tablet and mobile. Scope that media-query selector tobody > footeras well.
extensions/detect-unused-media.js:42 - Returning here leaves an old
build/unused-media.txtuntouched. After a build that reports unused files, a later clean result still exposes the stale list and can lead maintainers to delete assets that are now referenced. Remove or truncate the report whenunusedis empty before returning.
| }, | ||
| "engines": { | ||
| "node": ">= 8.0.0" | ||
| "node": ">= 18.0.0" |
| // NOTE the same shapes the crawler-independent extension used: image:target[] and image::target[] | ||
| // for images, video::target[] for video. Anything the reference cannot be resolved from, such as a | ||
| // target built from an attribute, is simply not matched, so this under-reports rather than | ||
| // reporting a used file as unused. |
What this does
Five related build and UI workstreams, each independently reviewable:
1. gulp 5 and Antora 3.1.15
The UI bundle was being built empty
antora-ui-camel/gulp.d/tasks/build.jsis anasyncfunction that returned a stream. gulp settles an async task on its promise, and a stream is not a thenable, sobundle:packzipped a directory that had not been written yet.Finished 'build'ui-bundle.zipThis did not affect the published site:
antora-playbook-production.ymlconsumes the stagedantora-ui-camel/public/_directory, not the zip. It did affect anything using the--ui-bundle-urlthat the gulp task prints.Theme to gulp 5
Moved to gulp 5, vinyl-fs 4, undertaker 2, through2 5 and fs-extra 11. Three things needed more than a version bump:
merge-streamhad to go. vinyl-fs 4 is built onstreamx, andmerge-streamis a node-corePassThroughthat silently drops streamx sources. Reproduced standalone: 93 files in, 3 out. Replaced withordered-read-streams, the streamx-native merge from the same org as gulp.encoding: false, since gulp 5 decodes contents as UTF-8 by default. All 58 fonts and images were checked byte-for-byte against the gulp 4 output and are identical.font/*.{ttf,woff*(2)}glob.src/fonthas never existed in this repo's history; fonts are copied intodest/fontby thepostcssUrlhandler. vinyl-fs 3 ignored the missing directory, vinyl-fs 4 raises ENOENT.PostCSS 7 plugin API to PostCSS 8
cssnano@8returns a plugin object rather than a callable, and the bare(css, result) => ...plugins were PostCSS 7 style and were no longer being invoked at all. Rewritten as visitor plugins. 97 transitive packages pruned.postcss-custom-properties@15keeps the:rootblock after substitution where v9 removed it, so resolved custom properties are now dropped explicitly by a small local plugin (gulp.d/lib/drop-resolved-custom-properties.js).Note for reviewers:
browserslist: last 2 versionsresolves to includeie 11,ie 10,ie_mobandop_mini all, none of which support CSS custom properties. Droppingpostcss-custom-propertiesentirely is therefore not an option; it would be both larger and broken on those targets.Root gulpfile to gulp 5
Replaced
delwith nativefs.rm(del 7+ is ESM only) and dropped the dependency. Fixedgenerate-markdownsignalling completion twice, by both callingdone()and returning a promise.Antora 3.1.15
Antora 3.1.9 added a heuristic that warns when an extension's
registerfunction names its first parameterregistry:@djencks/asciidoctor-jsonpathand@djencks/asciidoctor-antora-indexerboth do. Antora still registers them, the warning is advisory, but this playbook setsruntime.log.failure_level: warn, so the warning alone failed the build. Both packages were last published in 2022 and cannot be fixed upstream, so they are now required through thin wrappers inextensions/, matching the three local extensions already there.Worth knowing if anyone pins differently: 3.1.11 skips these extensions entirely rather than warning. 3.1.12 through 3.1.15 restored warn-and-register.
Unrelated CSS fix
Removed
max-width: var(--static-max-width)from.blog. That variable was never declared, only--static-max-width--desktopis, so the declaration was invalid at computed-value time andmax-widthalready resolved tonone. Removing it is a no-op and matches.static, which constrains width only inside the desktop media query.2. Yarn 4.1.0 to 4.18.0
Fixes the stale
compat/typescriptpatch-hunk warning during install: the patch hash no longer matched typescript 5.9.3 under the old Yarn.Supply-chain settings tightened at the same time:
approvedGitRepositories: [], since this repo has no git-protocol dependencies.npmMinimalAgeGate: 4320(3 days), delaying trust in newly published npm versions.3. Search: hand-rolled Algolia client to DocSearch v5
The site previously shipped
src/js/vendor/algoliasearch.bundle.js, a 365-line hand-rolled search UI built on the rawalgoliasearchv4 client, along with 169 lines of bespoke result-dropdown CSS insrc/css/header.css. Both are removed in favor of@docsearch/js5 and@docsearch/css5, which is the officially supported integration for the DocSearch-crawled index this site already uses.Result curation had to be carried across
The old bundle did three things beyond querying, and a straight swap to DocSearch would have silently dropped all of them. They are reimplemented in
src/js/08-docsearch.js:/camel-k/,/camel-quarkus/,/camel-spring-boot/,/camel-kafka-connector/,/camel-kamelets/and/camel-karaf/stay out of the main index results; those docs are browsable directly./manual/,/user-guide/,/architecture/,/getting-started/and/faq/above/components/. This works throughtransformItemsbecause DocSearch groups byhierarchy.lvl0into an insertion-ordered plain object, so the order we emit decides both group order and within-group order.deduplicateHits.transformItemsnever receives the query, and the original only used it to detect a direct parent match, so capping by page reaches an equivalent outcome without it.Why
hitsPerPage: 50The index has no
attributeForDistinctand noattributesForFaceting, so both filters must run client side, on an already-truncated window of hits. Distinct parent pages surviving the sub-project filter, measured against the live index:At the default,
timerreturned 19 usable hits spanning a single document, so the first result group was five near-identical anchors off one page.The durable fix is at the index level rather than in the browser. Filed as CAMEL-24396; with server-side
distinctthe over-fetch and most of the client-side logic can be deleted.Note that
.docsearch.config.jsonin this repo already attempts both settings, but they are not in effect on the live index, and one of them (attributeForDistinctResults) is not a real Algolia setting name. The records also lackurl_without_anchor, so there is currently no anchor-free attribute to group on. Details in the ticket; nothing there blocks this PR.@docsearch/cssgets its own build streamRouting the vendor stylesheet through
postcss-custom-propertieswithpreserve: falseflattened its runtime cascade: the@media (width<=768px):rootoverrides were inlined at desktop values,--shimmer-bgwas dead, and the 2.5 KB:root[data-theme=dark]block still shipped but could never take effect.It now builds separately, skipping the custom-property pass, revved to
css/vendor/docsearch.cssand linked ahead ofsite.cssin both the Antora and Hugo head partials. This mirrors how the DocSearch UMD bundle already ships as vendor JS.preserve: truewas rejected deliberately: it fixes the same bug but costs about 3.8 KB gzip site-wide and would reverse a flattening convention that predates this branch. Total CSS actually shrank slightly, since DocSearch's properties are no longer duplicated by inlining.4. UI fixes found while checking the theme in a browser
Admonitions lost their icon column
src/css/doc.csssettable-layout: fixedon.doc .admonitionblock > table, inherited from the default Antora UI. That UI positionstd.iconabsolutely, so collapsing the icon column costs it nothing. This UI keeps the icon in flow as a vertical label bar, and under fixed layout it gets zero width becausetd.contentiswidth: 100%.Removing the one declaration is a production no-op, confirmed by diffing the compiled stylesheet rule by rule: exactly one declaration differs. The rule matches nothing on the built site anyway, since
extensions/table.jswraps every table in adiv.table-wrapperand breaks the child combinator. It only ever bit the preview, which did not run that postprocessor. See item 5.Pagination links drew a full-width underline
nav.pagination spanstretched its anchor to half the container..doc adraws its underline with a repeating background-image gradient, so a stretched anchor painted that dashed line across the whole box rather than under the label. Fixed withalign-itemsso the anchor keeps its text width.DocSearch widget theme
The widget shipped in Algolia's default blue. It is now themed in
src/css/docsearch.css, scoped to.DocSearch, which is present on both the launch button and the modal container.Two traps are worth flagging for reviewers, both documented in the file:
var()references.postcss-custom-propertiessubstitutesvar()in ordinary declarations but not inside custom-property declarations, and the:rootpalette is stripped from the output. A first version written asvar(--color-camel-orange)compiled to an unresolvedvar()against a deleted variable, so the theme was silently dead.:root, so they resolve at their declaration site and overriding the inputs lower in the tree does not propagate.Three vendor properties are deliberately not set, because the corresponding SVGs hardcode their colors in inline
<style>blocks.--docsearch-logo-colorwas dropped rather than ship dead code, and the no-results glyph is handled with astroke: currentcolorrule instead.Also sized the searchbox in
rem: the vendor default is a fixed56px, which clipped descenders in the input against this site's 18px root font size. Ruled out the font swap as the cause by toggling tosystem-uiin the live page, where the text still measured 19px.Four unit tests cover the theme, all mutation-checked: scoped to
.DocSearchrather than:root, novar()left in custom-property declarations, searchbox height inrem, and the derived properties restated.5. Preview and test tooling
The preview diverged from the built site
gulp previewran plain Asciidoctor with none of the postprocessorsantora-playbook-production.ymlregisters, so what it rendered was not what the site rendered. That is what made the admonition bug in item 4 look like a theme regression when it was a harness artifact. Measured across four environments: local preview reportedtable-layout: fixedwith a 0px icon column, while camel.apache.org, the Netlify deploy preview and the locally built site all reportedautoand 33px.build-preview-pages.jsnow registers the same extensions as the playbook.@asciidoctor/tabsis deliberately left out and the reason is documented inline: the 1.0.0-beta.3 build targets the Opal runtime of asciidoctor.js 2 and throws on require under the@asciidoctor/core3 this UI depends on. The site build is unaffected, since Antora supplies its own Asciidoctor, so tabs render as description lists in the preview only.Example pages
preview-srcgained pages for admonitions, source code, tables, tabs and asciinema, wired into the sample nav. Content is original rather than adapted from the theme that prompted it, which is MPL-2.0 and therefore ASF Category B.Tests run from gulp
gulp testusesrun()fromnode:testso gulp settles on the returned promise and a failing test fails the task it is sequenced into, which isbundle. Verified both ways: exit 1 with a forced failure, exit 0 when restored.asciinema
@springio/antora-extensions/asciinema-extensionis registered in the playbook, with the player assets and partials guarded on the page attribute it sets, so pages without a cast load neither the script nor the stylesheet.The block is an Antora extension and never runs under
gulp preview, so the preview reproduces it locally against the extension's own partials and helpers fromnode_modules. One subtlety worth knowing if this is ever touched: the cast ids are collected into a plain object rather than set on the document, mirroring what the extension does withfile.asciidoc.attributes. Asciidoctor rolls back attributes assigned from the body once parsing ends, so apage-attribute set from a block processor is gone by the timegetAttributes()runs.Unused media detection
extensions/detect-unused-media.jsreports unreferenced images tobuild/unused-media.txt. It logs at info rather than warn on purpose: this playbook setsruntime.log.failure_level: warn, so warning once per unused file would fail the whole site build over a housekeeping issue. That is why the off-the-shelf@bonitasoft/antora-detect-unused-media-extensionwas dropped in favor of a local one.Verification
ui-bundle.zipat 697689 bytes / 124 files, and the CSS hash is reproducible across runsyarn build:antora: exit 0, 4574 HTML files across all nine components, indexer output intact (camel-kafka-connector 218 pages, camel-kamelets 505)documentation/_/js/vendor/asciinema-player.jsandcss/vendor/asciinema-player.cssexist in the output, and they get there only through itsuiLoadedhookextensions/detect-unused-media.jswrote a 30-entry report during that build and, as designed, logged no warnings, so it did not tripfailure_level: warnsitemap,htaccessandgenerate-markdown(4579 files) all pass under gulp 5gulp testand sequenced intobundle. They cover the custom-property plugin, the search transform and the DocSearch theme. The search suite evaluates the shipped IIFE against stubbed globals and replays captured Algolia responses, so it exercises the real file rather than a copy. Mutation-checked throughout: weakening the per-page cap, dropping tohitsPerPage: 20, removing the sort, or moving the theme to:rooteach fail the relevant assertions.Checked in a browser: the themed DocSearch modal, keyboard selection and the no-results screen; admonitions across four environments (see item 5); and both asciinema players mounting and playing through in the preview.
Still to verify
[asciinema]block has never been exercised in a real site build. Everything here runs against the preview reimplementation. No content source carries a cast yet, so the extension's block processor and the_asciinema/<id>.castoutput remain unproven end to end.data-theme="dark"toggle.camel tuisession recorded at 160x44. At the width of the docs content column that is small text, so the size may want revisiting.🤖 Generated with Claude Code