Skip to content

docs: assets generation automation - #12

Merged
AxelUser merged 13 commits into
mainfrom
chore/docs-assets-generation-automation
Jun 2, 2026
Merged

docs: assets generation automation#12
AxelUser merged 13 commits into
mainfrom
chore/docs-assets-generation-automation

Conversation

@AxelUser

@AxelUser AxelUser commented Jun 2, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • Documentation
    • Documentation screenshots and feature previews now automatically adapt to your system's light or dark mode preference, providing optimized visuals for improved readability.

@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@AxelUser, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 24 minutes and 5 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 846bf2aa-54ae-4528-b46b-84a77ebea95c

📥 Commits

Reviewing files that changed from the base of the PR and between ecc62d1 and 990ac61.

📒 Files selected for processing (5)
  • assets-cli/src/helpers/gif.ts
  • assets-cli/src/recipes/export.ts
  • assets-cli/src/runner.ts
  • assets-cli/src/server.ts
  • test-bridge/src/index.ts
📝 Walkthrough

Walkthrough

This PR introduces a test-bridge package to share Playwright test infrastructure and builds a comprehensive documentation asset generator (assets-cli). E2E tests migrate to the new package, the app's bridge is enhanced to seed CV content via JSON, and CI is optimized to exclude asset CLI dependencies from standard builds.

Changes

Test Bridge & Assets CLI Implementation

Layer / File(s) Summary
Test Bridge Type Contract
test-bridge/package.json, test-bridge/tsconfig.json, test-bridge/src/index.ts
Defines BridgePage, MasterSeedContent, and HhTestBridge interfaces; exports useBridge(page) to wait for and adapt window.__hhTest in Playwright tests.
Assets CLI Entry & Configuration
assets-cli/package.json, assets-cli/tsconfig.json, assets-cli/bin/docs-assets.mjs, assets-cli/src/cli.ts, assets-cli/src/config.ts, assets-cli/src/paths.ts
CLI entrypoint parses generate/help commands; HarnessConfig validates theme, port, output directory, timeout, and GIF settings with buildConfig().
Assets CLI Server & Helpers
assets-cli/src/server.ts
Builds the app with VITE_E2E, starts pnpm preview, waits for port readiness, and stops the preview process with cross-platform teardown.
Playwright Automation Utilities
assets-cli/src/helpers/context.ts, assets-cli/src/helpers/mouse.ts, assets-cli/src/helpers/zoom.ts, assets-cli/src/helpers/recorder.ts, assets-cli/src/helpers/gif.ts
Browser contexts for PNG/GIF capture; animated cursor with easing and click feedback; cinematic zoom/pan with transform clamping; video recording with trim windows; ffmpeg-based GIF encoding with palette generation and dithering.
Recipe Framework
assets-cli/src/recipe.ts, assets-cli/src/registry.ts, assets-cli/src/runner.ts
Recipe interface with name + async run; RecipeContext extends capture options; recipes array lists all scenarios in order; generateAll() iterates themes/recipes with timeout wrapper and aggregates failures.
Capture Recipes & Seed Data
assets-cli/src/recipes/dashboard.ts, assets-cli/src/recipes/dashboard-search.ts, assets-cli/src/recipes/editor.ts, assets-cli/src/recipes/export.ts, assets-cli/src/recipes/tailoring.ts, assets-cli/src/helpers/dwight.ts
Five automated capture scenarios (dashboard PNG, dashboard-search GIF, editor PNG + walkthrough GIF, export GIF, tailoring GIF); each seeds test data, records interactions, and outputs theme-specific files. dwightContent provides a complete CV fixture.

E2E Test Migration & App Integration

Layer / File(s) Summary
E2E Specs Migration
e2e/dashboard.spec.ts, e2e/editor.spec.ts, e2e/export.spec.ts, e2e/tailoring.spec.ts, e2e/helpers/bridge.ts
All four E2E specs now import useBridge from @humblehire/test-bridge instead of local helper; removed e2e/helpers/bridge.ts.
App Bridge Re-export & Content Seeding
src/lib/e2e-bridge.ts
Re-exports HhTestBridge and MasterSeedContent from test-bridge; extends seedMaster to accept optional overrides.content for populating CV blocks with object IDs, parsed dates, and recomputed hashes.
Workspace & Dependency Configuration
pnpm-workspace.yaml, package.json, .github/workflows/ci.yml, .gitignore
Adds test-bridge and assets-cli to workspace; declares @humblehire/test-bridge as dev dependency; adds assets:generate script; marks ffmpeg-static as built dependency; filters assets-cli from standard CI installs; ignores assets-output directory.
Documentation Updates
assets-cli/README.md, docs/decisions/ADR-004-e2e-test-strategy.md, README.md
Assets CLI documentation covers prerequisites, flags, recipe implementation, and output artifacts; ADR-004 clarifies bridge-based seeding and content patching; README adds dark-mode-aware picture elements for hero and feature visuals.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • AxelUser/HumbleHire#11: Both PRs modify README.md visuals; this PR updates hero/feature screenshots to dark-mode variants while that PR restructured overall README messaging.

Poem

🐰 Whiskers twitching with delight,
Screenshots born from code so bright,
Dark mode dancing, GIFs so smooth,
Bridge extracted—tests now soothe,
Docs alive with captured grace,
Automation wins the race! 🎬

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title "docs: assets generation automation" accurately summarizes the main changes, which involve adding a new CLI tool and infrastructure for automating documentation asset generation (screenshots and GIFs).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/docs-assets-generation-automation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@AxelUser AxelUser changed the title chore: docs assets generation automation docs: assets generation automation Jun 2, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (2)
assets-cli/src/recipes/editor.ts (1)

106-111: ⚖️ Poor tradeoff

GIF conversion in finally masks failures and runs on the unhappy path.

If the recorded flow throws before rec.stop() (e.g. a selector miss or timeout), this finally still calls videoToGif with a window that was never closed, which both emits a misleading/partial GIF and can throw, masking the original error. The same finally block is duplicated in dashboard-search.ts, export.ts, and tailoring.ts; consider extracting a shared withGifRecording(ctx, fn) helper that only converts on success and re-raises the underlying error otherwise.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@assets-cli/src/recipes/editor.ts` around lines 106 - 111, The finally block
in editor.ts calls videoToGif unconditionally which can run when rec.stop()
never completed and thus mask the original error; extract a shared helper like
withGifRecording(ctx, fn) that encapsulates starting/stopping the recorder and
converting the video (use page.video(), rec.stop(), context.close(), and
videoToGif), ensure the helper only invokes videoToGif when the recording
completed successfully, propagate/re-throw the original error if fn throws (and
catch conversion errors separately so they don't overwrite the original), and
replace the duplicated finally logic in editor.ts, dashboard-search.ts,
export.ts, and tailoring.ts with calls to this helper.
assets-cli/src/recipes/tailoring.ts (1)

72-72: 💤 Low value

Brittle decision-card locator.

.rounded-lg.border.p-4 couples the recipe to Tailwind utility classes; a styling tweak will silently break the camera framing. A data-testid on the decision card would be more durable if one can be added to the app.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@assets-cli/src/recipes/tailoring.ts` at line 72, The locator used to find
decision cards (the expression assigning items via
page.getByRole('dialog').locator('.rounded-lg.border.p-4')) is brittle because
it relies on Tailwind classes; change the selector to a durable attribute-based
selector (e.g., a data-testid like data-testid="decision-card") or another
semantic selector (role/text) if data attributes cannot be added, update the
locator call to use that selector, and coordinate with the app to add the
data-testid to the decision card component so the test targets
page.getByRole('dialog').locator('[data-testid="decision-card"]') (or equivalent
semantic locator) instead of the Tailwind classes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@assets-cli/src/cli.ts`:
- Around line 48-51: The preview shutdown is not guaranteed because
killTree()/stop() returns immediately after sending SIGTERM and the inherited
stdio child can keep the process alive; update the implementation of killTree
(and server.stop which calls it) to wait for the child process to actually exit
(e.g., attach an exit handler or use childProcess.wait/Promise) and if it does
not exit within a short timeout escalate to SIGKILL and then resolve; ensure
main()/generateAll still closes resources (browser.close(), server.stop()) and
rely on process.exitCode instead of forcing process.exit so the CLI can
terminate cleanly once the preview child has truly exited.
- Around line 34-37: The CLI currently allows --help because CONFIG_OPTIONS
includes help, so ensure the help branch in the main entry (the if (values.help
|| command === 'help') check in assets-cli/src/cli.ts) remains and simply prints
HELP and exits early; no further action needed there. To tighten the hang risk,
update assets-cli/src/server.ts: in the non-Windows killTree() path have it send
SIGTERM then wait for child process 'exit' (use a Promise with a reasonable
timeout) and if the process hasn't exited within that timeout send SIGKILL and
await final exit before resolving stop(); also ensure assets-cli/src/runner.ts's
finally still calls server.stop() so the improved stop() behavior prevents
lingering processes.

In `@assets-cli/src/helpers/gif.ts`:
- Line 71: The code uses a non-null assertion on ffmpegPath (const bin =
ffmpegPath!) before calling execFile which can be null; update the logic in
assets-cli/src/helpers/gif.ts to check ffmpegPath for null/undefined before
assigning to bin and before calling execFile, and handle the missing binary by
throwing a clear Error or returning/rejecting early with a helpful message
(e.g., "ffmpeg binary not found"), so references such as ffmpegPath, the const
bin, and the execFile call are guarded and won't receive a null path at runtime.
- Around line 15-20: TrimWindow is duplicated; update gif.ts to use the
TrimWindow type exported from recorder.ts instead of redefining it: remove the
local TrimWindow interface in assets-cli/src/helpers/gif.ts and import the
TrimWindow type from the recorder module that defines rec.window(); ensure
videoToGif and any uses in gif.ts reference the imported TrimWindow so the
recorder.ts definition is the single source of truth.

In `@assets-cli/src/recipes/export.ts`:
- Around line 42-45: The download waiter is registered after hoverAndClick
triggers the click, which can miss the download event; change the sequence in
the export flow so you create the download promise with
page.waitForEvent('download') before calling hoverAndClick/export button click
(the code that eventually calls locator.click()), then trigger
hoverAndClick(exportBtn) and await the previously-created download promise;
adjust references in this function to use the prepared download promise rather
than calling waitForEvent after the click.

In `@assets-cli/src/runner.ts`:
- Around line 19-51: The preview server started by startPreview is leaked if
chromium.launch() throws because both are called before the try; move the
chromium.launch() call into the try block (so browser is created after entering
try) and ensure the finally block safely closes the browser only if it exists
(use a guard around browser.close(), e.g., browser?.close()) before always
calling server.stop(); update references in runner.ts to use startPreview,
chromium.launch, browser.close, and server.stop accordingly.

In `@assets-cli/src/server.ts`:
- Around line 51-79: The POSIX teardown can orphan the actual vite process
because killTree() only sends SIGTERM to the direct pnpm child and resolves
immediately; fix by spawning the preview in startPreview with a detached process
group (set detached: true on the spawn call for 'pnpm preview') and update
killTree(child) to kill the whole group on non-Windows by calling
process.kill(-child.pid, 'SIGTERM') (or SIGKILL as fallback), and wait for the
child's 'exit' event before resolving; keep Windows behavior using taskkill
as-is.

In `@test-bridge/src/index.ts`:
- Line 76: The call to page.waitForFunction currently passes the options object
as the second positional argument, so the timeout is treated as the function arg
and ignored; update the call to pass undefined as the second argument so the
options (e.g., { timeout: 5000 }) become the third argument and the timeout is
applied when waiting for (window as any).__hhTest inside page.waitForFunction.

---

Nitpick comments:
In `@assets-cli/src/recipes/editor.ts`:
- Around line 106-111: The finally block in editor.ts calls videoToGif
unconditionally which can run when rec.stop() never completed and thus mask the
original error; extract a shared helper like withGifRecording(ctx, fn) that
encapsulates starting/stopping the recorder and converting the video (use
page.video(), rec.stop(), context.close(), and videoToGif), ensure the helper
only invokes videoToGif when the recording completed successfully,
propagate/re-throw the original error if fn throws (and catch conversion errors
separately so they don't overwrite the original), and replace the duplicated
finally logic in editor.ts, dashboard-search.ts, export.ts, and tailoring.ts
with calls to this helper.

In `@assets-cli/src/recipes/tailoring.ts`:
- Line 72: The locator used to find decision cards (the expression assigning
items via page.getByRole('dialog').locator('.rounded-lg.border.p-4')) is brittle
because it relies on Tailwind classes; change the selector to a durable
attribute-based selector (e.g., a data-testid like data-testid="decision-card")
or another semantic selector (role/text) if data attributes cannot be added,
update the locator call to use that selector, and coordinate with the app to add
the data-testid to the decision card component so the test targets
page.getByRole('dialog').locator('[data-testid="decision-card"]') (or equivalent
semantic locator) instead of the Tailwind classes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9c8ed354-06e4-4ea1-a6f6-af5ee7283c72

📥 Commits

Reviewing files that changed from the base of the PR and between f2c7002 and ecc62d1.

⛔ Files ignored due to path filters (14)
  • docs/assets/dashboard-search.dark.gif is excluded by !**/*.gif
  • docs/assets/dashboard-search.light.gif is excluded by !**/*.gif
  • docs/assets/dashboard.dark.png is excluded by !**/*.png
  • docs/assets/dashboard.light.png is excluded by !**/*.png
  • docs/assets/editor.dark.gif is excluded by !**/*.gif
  • docs/assets/editor.dark.png is excluded by !**/*.png
  • docs/assets/editor.light.gif is excluded by !**/*.gif
  • docs/assets/editor.light.png is excluded by !**/*.png
  • docs/assets/editor.png is excluded by !**/*.png
  • docs/assets/export.dark.gif is excluded by !**/*.gif
  • docs/assets/export.light.gif is excluded by !**/*.gif
  • docs/assets/tailoring.dark.gif is excluded by !**/*.gif
  • docs/assets/tailoring.light.gif is excluded by !**/*.gif
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (37)
  • .github/workflows/ci.yml
  • .gitignore
  • README.md
  • assets-cli/README.md
  • assets-cli/bin/docs-assets.mjs
  • assets-cli/package.json
  • assets-cli/src/cli.ts
  • assets-cli/src/config.ts
  • assets-cli/src/helpers/context.ts
  • assets-cli/src/helpers/dwight.ts
  • assets-cli/src/helpers/gif.ts
  • assets-cli/src/helpers/mouse.ts
  • assets-cli/src/helpers/recorder.ts
  • assets-cli/src/helpers/zoom.ts
  • assets-cli/src/paths.ts
  • assets-cli/src/recipe.ts
  • assets-cli/src/recipes/dashboard-search.ts
  • assets-cli/src/recipes/dashboard.ts
  • assets-cli/src/recipes/editor.ts
  • assets-cli/src/recipes/export.ts
  • assets-cli/src/recipes/tailoring.ts
  • assets-cli/src/registry.ts
  • assets-cli/src/runner.ts
  • assets-cli/src/server.ts
  • assets-cli/tsconfig.json
  • docs/decisions/ADR-004-e2e-test-strategy.md
  • e2e/dashboard.spec.ts
  • e2e/editor.spec.ts
  • e2e/export.spec.ts
  • e2e/helpers/bridge.ts
  • e2e/tailoring.spec.ts
  • package.json
  • pnpm-workspace.yaml
  • src/lib/e2e-bridge.ts
  • test-bridge/package.json
  • test-bridge/src/index.ts
  • test-bridge/tsconfig.json
💤 Files with no reviewable changes (1)
  • e2e/helpers/bridge.ts

Comment thread assets-cli/src/cli.ts
Comment thread assets-cli/src/cli.ts
Comment on lines +48 to +51
main().catch((err) => {
process.stderr.write(`${err instanceof Error ? (err.stack ?? err.message) : String(err)}\n`);
process.exitCode = 1;
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect generateAll cleanup on error paths.
fd -t f 'runner.ts' assets-cli | xargs -r cat

Repository: AxelUser/HumbleHire

Length of output: 1949


🏁 Script executed:

#!/bin/bash
# Inspect CLI and preview server stop/teardown.
fd -t f 'cli.ts' assets-cli | xargs -r cat
fd -t f 'server.ts' assets-cli | xargs -r cat

Repository: AxelUser/HumbleHire

Length of output: 4645


Finalize preview shutdown so exitCode is sufficient.

generateAll already tears down resources in finally (await browser.close(); await server.stop();). The remaining risk is server.stop() on non-Windows: killTree() sends SIGTERM and resolves immediately without waiting for the preview child to actually exit. Because the preview is spawned with stdio: 'inherit', the CLI process can stay alive on failure. Update killTree/stop() to wait for the child to exit (and escalate to a stronger kill if it doesn’t).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@assets-cli/src/cli.ts` around lines 48 - 51, The preview shutdown is not
guaranteed because killTree()/stop() returns immediately after sending SIGTERM
and the inherited stdio child can keep the process alive; update the
implementation of killTree (and server.stop which calls it) to wait for the
child process to actually exit (e.g., attach an exit handler or use
childProcess.wait/Promise) and if it does not exit within a short timeout
escalate to SIGKILL and then resolve; ensure main()/generateAll still closes
resources (browser.close(), server.stop()) and rely on process.exitCode instead
of forcing process.exit so the CLI can terminate cleanly once the preview child
has truly exited.

Comment thread assets-cli/src/helpers/gif.ts Outdated
Comment thread assets-cli/src/helpers/gif.ts Outdated
Comment thread assets-cli/src/recipes/export.ts Outdated
Comment thread assets-cli/src/runner.ts
Comment thread assets-cli/src/server.ts Outdated
Comment thread test-bridge/src/index.ts Outdated
@AxelUser
AxelUser merged commit 159c7ce into main Jun 2, 2026
6 checks passed
@AxelUser
AxelUser deleted the chore/docs-assets-generation-automation branch June 2, 2026 21:45
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