Skip to content

fix(core): correctly render badge recipe background/text#271

Merged
RtlZeroMemory merged 4 commits intomainfrom
fix/issue-270-badge-color
Mar 7, 2026
Merged

fix(core): correctly render badge recipe background/text#271
RtlZeroMemory merged 4 commits intomainfrom
fix/issue-270-badge-color

Conversation

@RtlZeroMemory
Copy link
Owner

@RtlZeroMemory RtlZeroMemory commented Mar 7, 2026

Summary

  • Fix badge rendering to use badge recipe semantics correctly in the main renderer.
  • Preserve badge background during text draw by carrying recipe background on text style when semantic recipe tokens are present.
  • Keep style override precedence (props.style still wins over recipe values).

Root Cause

Badge rendering was taking recipe background color and using it as foreground text color, while not reliably preserving background under text draw.

Changes

  • Updated badge branch in renderTextWidgets to:
    • apply recipe text style
    • paint recipe background via fillRect
    • include recipe background on text style so draw operations keep the pill visible
  • Updated renderer regression test to assert both fill background and text foreground/background for badge recipe rendering.

Validation

  • npm run build
  • npm run lint
  • npm run typecheck
  • node --test packages/core/dist/renderer/tests/sliderRecipeRendering.test.js
  • node scripts/run-tests.mjs

Closes #270

Summary by CodeRabbit

  • Tests

    • Expanded rendering tests to validate badge/tag color propagation, explicit foreground/background overrides, styled text (including bold), and replaced time-based waits with condition-driven waits for more deterministic results.
  • Refactor

    • Consolidated badge and tag color resolution to use tokenized recipes and unified variant fallbacks, improving consistency of background/foreground styling.

@coderabbitai
Copy link

coderabbitai bot commented Mar 7, 2026

📝 Walkthrough

Walkthrough

Refactors badge/tag color resolution and rendering so recipes' bg/text tokens drive pill backgrounds and text colors; replaces resolveChipColor(..., kind) with resolveTagColor(...) and tightens tests to assert explicit bg/fg propagation and background painting.

Changes

Cohort / File(s) Summary
Tests — slider/badge rendering
packages/core/src/renderer/__tests__/sliderRecipeRendering.test.ts
Tightens assertions: extracts fillRect op and asserts its presence/kind, verifies recipe-derived background matches dsTheme.colors.info, and adds a test validating explicit badge fg/bg overrides and bold text.
Renderer — text widgets (badge/tag)
packages/core/src/renderer/renderToDrawlist/widgets/renderTextWidgets.ts
Replaced resolveChipColor(theme, variant, kind) with resolveTagColor(theme, variant). Badge/tag color resolution now consults tag/badge recipes and token-derived text/bg to compute chip styles; when recipe.bg exists a filled background is painted and recipe.text.fg is used for text. Fallback tone logic simplified; public resolver signature changed.
Tests — animation orchestration
packages/core/src/widgets/__tests__/composition.animationOrchestration.test.ts
Replaced fixed sleep-based wait with a waitFor polling that waits for a paused entry within tolerance and isAnimating === false, improving determinism of the test.

Sequence Diagram(s)

sequenceDiagram
    participant UI as UI
    participant Renderer as renderTextWidgets
    participant Recipe as badge/tag Recipe & Token Resolver
    participant Drawlist as DrawList Builder

    UI->>Renderer: render badge("LABEL", variant)
    Renderer->>Recipe: resolveTagColor / badgeRecipe(theme, variant)
    Recipe-->>Renderer: { text: {fg, bold}, bg: {bg} }  or token fallbacks
    alt recipe.bg present
        Renderer->>Drawlist: fillRect(bg)  <!-- background painted -->
    end
    Renderer->>Drawlist: drawText("( LABEL )", fg: text.fg, bold)
    Drawlist-->>Renderer: ops recorded (fillRect?, drawText)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped through tokens, found colors to wear,
Pills now fill bravely, bold text in the air,
Recipe whispers told me which hue to show,
I painted the pill and watched the pixels glow 🎨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 title clearly and specifically describes the main fix: correcting badge recipe background and text rendering, which is the primary objective of the PR.
Linked Issues check ✅ Passed The PR comprehensively addresses all requirements from issue #270: correctly applies badgeRecipe bg as background via fillRect, uses text.fg for text color, paints an actual background, and preserves style precedence.
Out of Scope Changes check ✅ Passed Changes are scoped to badge rendering logic in renderTextWidgets.ts, test updates in sliderRecipeRendering.test.ts, and test flow improvements in composition.animationOrchestration.test.ts, all aligned with the stated objectives.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/issue-270-badge-color

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/core/src/renderer/__tests__/sliderRecipeRendering.test.ts (1)

44-59: Add the explicit props.style precedence case in this test file.

This covers the recipe-only path, but the PR also changes how badge background is carried onto drawText. A second case with ui.badge(..., { variant: "info", style: { fg, bg } }) would lock down the stated guarantee that explicit style overrides still win over recipe values.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/renderer/__tests__/sliderRecipeRendering.test.ts` around
lines 44 - 59, Add a second test that verifies explicit props.style wins over
recipe colors: call renderOps(ui.badge("Info", { variant: "info", style: { fg:
/* explicit fg */, bg: /* explicit bg */ } }), { viewport: { cols: 24, rows: 2
}, theme: dsTheme }), then locate the fillRect (ops.find(...)) and drawText
(firstDrawText(...)) as in the existing test and assert the fill.style?.bg and
text.style?.bg equal the explicit bg, text.style?.fg equals the explicit fg, the
text includes "( Info )", and bold remains true; use unique symbols ui.badge,
renderOps, dsTheme, fillRect, and firstDrawText to find the right places to
assert.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/core/src/renderer/__tests__/sliderRecipeRendering.test.ts`:
- Around line 49-57: The current test picks the first fillRect from ops which
may be unrelated; update the find for the badge fill (the variable `fill`
created from `ops.find(...)`) to match the badge's expected background (e.g.
require `op.style?.bg === dsTheme.colors.info`) and, if available, also verify
the badge geometry by matching the expected `op.rect` (or calculated badge rect)
so the test fails when the pill background or position disappears; keep the
`text` checks using `firstDrawText` as-is to confirm the "( Info )" label.

In `@packages/core/src/renderer/renderToDrawlist/widgets/renderTextWidgets.ts`:
- Around line 906-909: The background fill currently merges the entire ownStyle
into bgStyle (via mergeTextStyle(bgBaseStyle, ownStyle)), which allows text
attributes like fg, bold, or inverse to affect builder.fillRect; instead, only
merge the bg attribute from ownStyle into bgBaseStyle before calling
builder.fillRect. Locate the block that computes bgBaseStyle and bgStyle (using
mergeTextStyle, parentStyle, recipeResult.bg, and ownStyle) and change the
second merge to use an object containing only ownStyle.bg (e.g., merge only {
bg: ownStyle.bg } when ownStyle exists) so builder.fillRect receives a style
with background overrides only.

---

Nitpick comments:
In `@packages/core/src/renderer/__tests__/sliderRecipeRendering.test.ts`:
- Around line 44-59: Add a second test that verifies explicit props.style wins
over recipe colors: call renderOps(ui.badge("Info", { variant: "info", style: {
fg: /* explicit fg */, bg: /* explicit bg */ } }), { viewport: { cols: 24, rows:
2 }, theme: dsTheme }), then locate the fillRect (ops.find(...)) and drawText
(firstDrawText(...)) as in the existing test and assert the fill.style?.bg and
text.style?.bg equal the explicit bg, text.style?.fg equals the explicit fg, the
text includes "( Info )", and bold remains true; use unique symbols ui.badge,
renderOps, dsTheme, fillRect, and firstDrawText to find the right places to
assert.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8cfc8c72-ea5e-4348-9d79-0313b47a81f8

📥 Commits

Reviewing files that changed from the base of the PR and between 3e45eaa and 627a9b1.

📒 Files selected for processing (2)
  • packages/core/src/renderer/__tests__/sliderRecipeRendering.test.ts
  • packages/core/src/renderer/renderToDrawlist/widgets/renderTextWidgets.ts

@RtlZeroMemory
Copy link
Owner Author

Addressed the CodeRabbit review feedback in 3c5e448:\n- Scoped badge fill assertion to the badge background color in the regression test.\n- Added explicit props.style precedence test for badge fg/bg overrides.\n- Narrowed badge fillRect style merge to background-only overrides () to avoid carrying text attributes into fill operations.\n\nValidation rerun:\n- npm run build\n- npm run lint\n- npm run typecheck\n- node --test packages/core/dist/renderer/tests/sliderRecipeRendering.test.js\n- node scripts/run-tests.mjs

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@packages/core/src/widgets/__tests__/composition.animationOrchestration.test.ts`:
- Around line 203-210: The test currently only verifies a single paused
snapshot; update it to assert the value stays frozen across a real paused
interval by advancing frames/time and re-checking that progress does not change.
Specifically, after the initial waitFor that captures entry from
h.render((hooks) => useParallel(hooks, paused)) (referencing useParallel,
paused, pausedValue, next.pendingEffects, h.runPending), run several subsequent
frames (e.g., call h.runPending(next.pendingEffects) repeatedly or advance
timers) and assert each time that Math.abs(entry.value - pausedValue) <= 0.1 and
entry.isAnimating === false so the test proves no drift over time while paused.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c5272056-ed1c-420c-99e0-a1d07c762932

📥 Commits

Reviewing files that changed from the base of the PR and between 3c5e448 and 4eea133.

📒 Files selected for processing (1)
  • packages/core/src/widgets/__tests__/composition.animationOrchestration.test.ts

Comment on lines +203 to +210
await waitFor(() => {
const next = h.render((hooks) => useParallel(hooks, paused));
render = next;
h.runPending(next.pendingEffects);
const entry = next.result[0];
if (!entry) return false;
return Math.abs(entry.value - pausedValue) <= 0.1 && entry.isAnimating === false;
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Keep a real paused interval in this regression test.

This only proves that one render produced a paused snapshot. It no longer verifies that progress stays frozen over time while paused, so a regression that still advances for a few frames can pass here.

Suggested change
       await waitFor(() => {
         const next = h.render((hooks) => useParallel(hooks, paused));
         render = next;
         h.runPending(next.pendingEffects);
         const entry = next.result[0];
         if (!entry) return false;
         return Math.abs(entry.value - pausedValue) <= 0.1 && entry.isAnimating === false;
       });
+
+      await sleep(80);
+      render = h.render((hooks) => useParallel(hooks, paused));
+      h.runPending(render.pendingEffects);
+      assert.ok(Math.abs((render.result[0]?.value ?? 0) - pausedValue) <= 0.1);
+      assert.equal(render.result[0]?.isAnimating, false);

Based on learnings, "Read target file tests before changing behavior; expected behavior and edge cases are documented in test files".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/core/src/widgets/__tests__/composition.animationOrchestration.test.ts`
around lines 203 - 210, The test currently only verifies a single paused
snapshot; update it to assert the value stays frozen across a real paused
interval by advancing frames/time and re-checking that progress does not change.
Specifically, after the initial waitFor that captures entry from
h.render((hooks) => useParallel(hooks, paused)) (referencing useParallel,
paused, pausedValue, next.pendingEffects, h.runPending), run several subsequent
frames (e.g., call h.runPending(next.pendingEffects) repeatedly or advance
timers) and assert each time that Math.abs(entry.value - pausedValue) <= 0.1 and
entry.isAnimating === false so the test proves no drift over time while paused.

@RtlZeroMemory RtlZeroMemory merged commit 13e58c4 into main Mar 7, 2026
30 of 32 checks passed
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.

Badge renderer ignores badgeRecipe bg/text styles — no background painted

1 participant