Skip to content

fix(export): render gradient backgrounds correctly in exported video#672

Open
soufian3hm wants to merge 1 commit into
webadderallorg:mainfrom
soufian3hm:fix/gradient-background-export
Open

fix(export): render gradient backgrounds correctly in exported video#672
soufian3hm wants to merge 1 commit into
webadderallorg:mainfrom
soufian3hm:fix/gradient-background-export

Conversation

@soufian3hm

@soufian3hm soufian3hm commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Gradient wallpaper backgrounds render correctly in the editor preview (real CSS) but are corrupted in the exported video. Depending on the preset, the export shows either a solid black background or a mangled gradient with shifted color stops. This affects the entire built-in gradient palette (GRADIENTS in SettingsPanel.tsx, ~24 presets).

Root cause

Both frame renderers parsed the gradient string with a naive params.split(","):

const gradientMatch = wallpaper.match(/(linear|radial)-gradient\((.+)\)/);
const parts = params.split(",").map((s) => s.trim());

This is wrong in two independent ways:

  1. rgba()/hsla() colors get shredded. rgba(114,167,232,1) is split into the fragments rgba(114, 167, 232, 1). The color regex (…|[a-z]+) then matches the literal token rgba, addColorStop() throws on the invalid color, and the surrounding try/catch falls back to filling the canvas black. Every rgba(...) and radial-gradient preset (about half the palette) exports as a black background.

  2. Color-stop offsets are taken from the split-token index. A leading direction token (120deg, to right, …) occupies index 0, so the first real color is placed at offset 1/(n-1) = 0.5 instead of 0, and the explicit 0% / 100% stop positions in the string are ignored. So even pure-hex presets render with the gradient compressed into the bottom half of the frame.

Reproduction of the current behavior on real presets:

preset current export
linear-gradient( 111.6deg, rgba(114,167,232,1) 9.4%, … ) solid black
radial-gradient( circle farthest-corner at …, rgba(80,12,139,0.87) 0%, … ) solid black
linear-gradient(120deg, #d4fc79 0%, #96e6a1 100%) first color pinned at offset 0.5, top half a flat slab

Fix

Extract a parenthesis-aware, position-honoring parser into a new pure helper, src/lib/exporter/gradientBackground.ts (parseGradientBackground):

  • tokenizes the gradient respecting parentheses, so rgba()/hsla() colors stay intact;
  • strips the optional leading direction/shape descriptor (to …, <angle>deg, circle … at …) so it is not mistaken for a color;
  • honors explicit % stop positions, distributes evenly when omitted, interpolates interior gaps, and clamps offsets to a non-decreasing [0, 1] range so addColorStop never throws.

Both the modern (modernFrameRenderer.ts) and legacy (frameRenderer.ts) renderers now consume this helper. Canvas geometry is intentionally unchanged (linear stays top→bottom, radial stays centered) to keep the change minimal and low-risk — honoring the gradient angle is a separate enhancement.

Why it works

The new tokenizer keeps rgba(231, 148, 6, 1) (commas and spaces included) as a single color, and offsets come from the parsed stop positions instead of the split index, so the exported gradient matches the editor preview. Validated against all 24 built-in presets.

Risks

Low. The helper is pure and isolated; the renderer diff only swaps the inline parser for a function call while preserving the existing fallback-to-black behavior for unparseable input. No public API or geometry change.

Testing

  • Added src/lib/exporter/gradientBackground.test.ts (11 cases): rgba preservation, direction stripping, radial shape stripping, explicit/even/interpolated stop positions, clamping/ordering, named colors, and a sweep asserting every built-in preset parses into valid, ordered, non-rgba-fragment stops.
  • vitest run src/lib/exporter/gradientBackground.test.ts → 11 passed; full local timeline/exporter unit suites green (18/18 in the focused run).
  • tsc --noEmit: no errors introduced in the changed files.

Note: I could not run the native packaged build locally (the bundled Whisper runtime build needs CMake), so validation is via the unit suites and typecheck.

Summary by CodeRabbit

  • Refactor

    • Reorganized gradient background handling logic for improved code maintainability and better separation of concerns.
  • Tests

    • Added comprehensive test coverage for gradient parsing, including edge cases, color formats, and positioning calculations.

Gradient wallpapers were parsed with `params.split(",")`, which splits the
commas inside `rgba()`/`hsla()` color functions. The color regex then matched
the bare token `rgba`, `addColorStop` threw, and the outer try/catch fell back
to a solid black background — so every rgba()/radial preset exported as black.
For hex presets the leading direction token (`120deg`, `to right`, …) consumed
color-stop index 0, pushing the first color to offset 0.5 and ignoring the
explicit `0%/100%` stops, so even those gradients rendered wrong.

Extract a parenthesis-aware, position-honoring parser into a pure, unit-tested
helper (`parseGradientBackground`) and use it from both the modern and legacy
frame renderers. Canvas geometry is unchanged to keep the fix minimal.
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ This pull request has been flagged by Anti-Slop.
Our automated checks detected patterns commonly associated with
low-quality or automated/AI submissions (failure count reached).
No automatic closure — a maintainer will review it.
If this is legitimate work, please add more context, link issues, or ping us.

@github-actions github-actions Bot added the Slop label Jun 10, 2026
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f2a35a1d-eb1f-470f-b1ee-01a57ffc9cf8

📥 Commits

Reviewing files that changed from the base of the PR and between 52dd842 and 081a2c1.

📒 Files selected for processing (4)
  • src/lib/exporter/frameRenderer.ts
  • src/lib/exporter/gradientBackground.test.ts
  • src/lib/exporter/gradientBackground.ts
  • src/lib/exporter/modernFrameRenderer.ts

📝 Walkthrough

Walkthrough

A new parseGradientBackground module extracts and normalizes CSS gradient parsing logic, replacing duplicated inline regex patterns in two frame renderers with a single tested parser that produces a structured { type, stops } representation with interpolated and clamped stop positions.

Changes

Gradient parsing extraction and integration

Layer / File(s) Summary
Gradient parser implementation
src/lib/exporter/gradientBackground.ts
Exports GradientColorStop and ParsedGradientBackground interfaces. Implements parseGradientBackground() to validate gradient format, tokenize parameters while respecting nested rgba()/hsla() functions, strip direction/config tokens, extract color stops with optional positions, interpolate missing offsets, and clamp/enforce non-decreasing ordering.
Gradient parser test suite
src/lib/exporter/gradientBackground.test.ts
Validates that non-gradient strings return null, rgba(...) colors preserve internal commas and spacing, direction keywords and radial descriptors are stripped without breaking stop parsing, missing positions are interpolated or defaulted, stop positions are clamped to [0, 1] with non-decreasing ordering, named colors work, and many preset gradients parse correctly.
FrameRenderer gradient refactor
src/lib/exporter/frameRenderer.ts
Imports parseGradientBackground and refactors gradient rendering to use the parser result, constructing canvas gradients by iterating the parsed stops and calling addColorStop for each stop.
ModernFrameRenderer gradient refactor
src/lib/exporter/modernFrameRenderer.ts
Imports parseGradientBackground and replaces inline parsing with the dedicated parser, falling back to solid black on invalid parse, and populating canvas gradients by iterating parsedGradient.stops.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

Checked

🐰 A rabbit's ode to parsing clean,
Gradients once tangled, now serene!
No regex mess, just stops so true,
From linear flows to radial hue.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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
Title check ✅ Passed The title clearly and concisely describes the main change: fixing gradient background rendering in exported videos, which matches the core purpose of the PR.
Description check ✅ Passed The description is comprehensive and well-structured, covering summary, root cause, fix, testing, and risks. It exceeds template requirements with detailed technical justification and validation details.
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

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.

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ This pull request has been flagged by Anti-Slop.
Our automated checks detected patterns commonly associated with
low-quality or automated/AI submissions (failure count reached).
No automatic closure — a maintainer will review it.
If this is legitimate work, please add more context, link issues, or ping us.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant