Skip to content

feat: use forceColorScheme for ?theme= param#2645

Merged
davydkov merged 9 commits intolikec4:mainfrom
m9810223:main
Mar 2, 2026
Merged

feat: use forceColorScheme for ?theme= param#2645
davydkov merged 9 commits intolikec4:mainfrom
m9810223:main

Conversation

@m9810223
Copy link
Copy Markdown
Contributor

@m9810223 m9810223 commented Feb 21, 2026

Summary

  • Replace ThemeSync (setColorScheme → localStorage) with MantineProvider's forceColorScheme prop so ?theme= overrides appearance without persisting to localStorage
  • Extract search params schema to dedicated searchParams.ts module
  • Add E2E tests covering ?theme=, ?dynamic=, and ?relationships= observable browser behaviour

Motivation

The ?theme= search param defaulted to 'auto' and ThemeSync called setColorScheme(theme) on every mount, unconditionally writing to localStorage. This destroyed any preference the user had set via the color scheme toggle. The new approach only forces a color scheme when the URL explicitly requests one, and never touches localStorage.

Changes

File Change
searchParams.ts New — extracted schema from __root.tsx; theme is now .optional().catch(undefined) instead of .default('auto').catch('auto')
routes/__root.tsx Removed ThemeSync; moved MantineProvider here from main.tsx; uses forceColorScheme conditionally
main.tsx Removed MantineProvider wrapper (now lives in root route)
ColorSchemeToggle.tsx When forceColorScheme is active, navigates to remove ?theme= first, then applies the scheme via useEffect + pendingScheme ref; includes double-click guard
e2e/tests/search-params.spec.ts New — E2E tests for ?theme=, ?dynamic=, ?relationships=
e2e/.gitignore Whitelist search-params.spec.ts and static-navigation.spec.ts

How it works now

URL ?theme= forceColorScheme localStorage Behavior
(absent) not set respected Mantine default: localStorage > system pref
auto not set respected Same as absent
light / dark set bypassed Forced to the specified scheme
User clicks toggle written If forced: navigates to remove ?theme=, then applies via effect; otherwise toggles directly

Semantic changes to note

  • ?theme=auto no longer means "force follow system" — it now falls back to Mantine's normal priority (localStorage > system). If the user previously toggled to dark, ?theme=auto will show dark.
  • Invalid localStorage values are no longer auto-repaired — previously ThemeSync always wrote a valid value; now garbage persists until the user clicks the toggle.

Checklist

  • I've thoroughly read the latest contribution guidelines.
  • I've rebased my branch onto main before creating this PR.
  • My commit messages follow conventional spec
  • I've added tests to cover my changes (if applicable).
  • I've verified that all new and existing tests have passed locally for mobile, tablet, and desktop screen sizes.
  • My change requires documentation updates.
  • I've updated the documentation accordingly.

Summary by CodeRabbit

  • New Features

    • URL query params control exported views: ?theme (explicit light/dark enforced; others fallback), ?dynamic, ?relationships, and ?padding. Color-scheme handling now follows URL-driven behavior.
  • Improvements

    • Color-scheme toggle cooperates with URL-forced themes: it clears or defers forced theme before applying user choice and guards against rapid double-clicks.
  • Tests

    • Added end-to-end tests validating theme, dynamic, relationships, and padding URL behaviors.
  • Chores

    • Re-enabled two e2e test specs.

… tests

Replace ThemeSync (setColorScheme → localStorage) with MantineProvider's
forceColorScheme prop so ?theme= overrides appearance without persisting.
Extract search params schema to dedicated module. Add E2E tests covering
?theme=, ?dynamic=, and ?relationships= observable browser behaviour.
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Feb 21, 2026

🦋 Changeset detected

Latest commit: 5f6e01b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 19 packages
Name Type
likec4 Minor
@likec4/docs-astro Minor
likec4-vscode Minor
@likec4/playground Minor
@likec4/style-preset Minor
@likec4/styles Minor
@likec4/config Minor
@likec4/core Minor
@likec4/diagram Minor
@likec4/generators Minor
@likec4/language-server Minor
@likec4/language-services Minor
@likec4/layouts Minor
@likec4/log Minor
@likec4/mcp Minor
@likec4/react Minor
@likec4/tsconfig Minor
@likec4/vite-plugin Minor
@likec4/vscode-preview Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 21, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds URL-driven search-parameter parsing and theme forcing: new Zod-based searchParams module, MantineProvider moved into the root route to honor explicit ?theme= values, ColorSchemeToggle updated to defer/remove forced themes via navigation, and E2E tests for ?theme, ?dynamic, and ?relationships re-enabled.

Changes

Cohort / File(s) Summary
E2E tests & ignore
e2e/.gitignore, e2e/tests/search-params.spec.ts
Un-ignore two spec files and add an E2E test covering ?theme=, ?dynamic=, and ?relationships= with helpers for export/view URLs and canvas waiting.
Search params API
packages/likec4/app/src/searchParams.ts
New Zod schema searchParamsSchema, exported SearchParams type, and resolveForceColorScheme() mapping URL theme to `'light'
Root route & Mantine
packages/likec4/app/src/routes/__root.tsx
Moves MantineProvider into root route, imports searchParamsSchema/resolveForceColorScheme, derives forceColorScheme from URL, adjusts exported types (adds Context, removes prior inline SearchParams).
Color scheme toggle behavior
packages/likec4/app/src/components/ColorSchemeToggle.tsx
Adds pendingScheme buffering, navigation-based removal of ?theme when a forced scheme is active, a toggle guard to avoid duplicate actions, and applies buffered scheme after navigation clears the forced parameter.
App bootstrap
packages/likec4/app/src/main.tsx
Removes MantineProvider and theme imports from app bootstrap; Routes now render directly under StrictMode.
Changeset
.changeset/force-color-scheme-theme.md
Adds a patch changeset noting the behavioral change: use MantineProvider's forceColorScheme for ?theme= instead of persisting to localStorage.

Sequence Diagram

sequenceDiagram
    participant User
    participant Browser
    participant SearchParams as SearchParams Module
    participant Root as __root Route
    participant Mantine as MantineProvider
    participant ColorScheme as ColorSchemeToggle

    User->>Browser: Visit URL (e.g. ?theme=dark) or click toggle
    Browser->>SearchParams: Parse & validate URL query
    SearchParams->>Root: Return parsed SearchParams + resolved forceColorScheme
    Root->>Mantine: Provide forceColorScheme prop and render Outlet
    Mantine->>Browser: Render UI with enforced scheme

    alt Toggle while theme is forced
        ColorScheme->>ColorScheme: store pendingScheme (buffer)
        ColorScheme->>Browser: navigate to remove ?theme
        Browser->>Root: URL updated (no theme)
        Root->>Mantine: forceColorScheme = undefined
        ColorScheme->>ColorScheme: detect cleared force -> apply pendingScheme
        ColorScheme->>Mantine: set scheme directly
    else Toggle while not forced
        ColorScheme->>Mantine: apply scheme directly
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I sniffed the query and found a hue,
Zod hopped in to parse each subtle cue,
Root wraps Mantine, the URL leads the play,
Toggles stash a wish, then clear the way,
Canvas blinks awake — I nibble bugs away!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: replacing ThemeSync with MantineProvider's forceColorScheme for the ?theme= URL parameter.
Description check ✅ Passed The description is comprehensive and addresses all required checklist items. It includes a detailed summary, motivation, change table, implementation details, behavior documentation, and a completed checklist.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


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.

Copy link
Copy Markdown
Contributor

@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

🧹 Nitpick comments (2)
e2e/tests/search-params.spec.ts (1)

66-86: Consider adding a test for absent ?dynamic= on a dynamic view.

The tests cover sequence, diagram, and an invalid value (timeline), but there's no test for what happens when ?dynamic= is entirely absent on a dynamic view. Per the schema, it defaults to 'diagram', so behavior should match the diagram case — but an explicit test would lock that contract down.

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

In `@e2e/tests/search-params.spec.ts` around lines 66 - 86, Add a test to cover
the case where the query param ?dynamic= is absent: inside the existing
test.describe('?dynamic= search parameter'...) add a new test (similar to the
other cases) that calls gotoAndWaitForCanvas(page, exportUrl(DYNAMIC_VIEW)) with
no dynamic option, then assert that page.locator(SEQ_ACTOR_SELECTOR) has count 0
(or is not visible) using TIMEOUT_CANVAS; use the same helpers
(gotoAndWaitForCanvas, exportUrl) and constants (DYNAMIC_VIEW,
SEQ_ACTOR_SELECTOR, TIMEOUT_CANVAS) so the absent-param behavior is verified to
match the 'diagram' default.
packages/likec4/app/src/components/ColorSchemeToggle.tsx (1)

30-46: Consider the case where navigation resolves but computedColorScheme has already changed.

If the user has ?theme=dark (so computedColorScheme is 'dark'), clicking toggle stores next = 'light' in pendingScheme and navigates. After navigation clears the force, Mantine falls back to localStorage/system — which could already resolve to 'light'. In that case the effect still calls setColorScheme('light'), which is harmless but redundant. Not a bug, just a subtlety worth being aware of.

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

In `@packages/likec4/app/src/components/ColorSchemeToggle.tsx` around lines 30 -
46, The toggle flow can leave pendingScheme set when navigation succeeds,
causing the subsequent effect to call setColorScheme even if computedColorScheme
already matches the pending value; update the post-navigation logic so that when
navigation resolves you compare computedColorScheme to pendingScheme.current and
only call setColorScheme(pending) if they differ, then clear
pendingScheme.current (and still clear it in the navigate.catch branch). Refer
to pendingScheme, navigate({...}).catch(...), computedColorScheme, and
setColorScheme to locate and implement the check and clearing.
🤖 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/likec4/app/src/searchParams.ts`:
- Line 5: Replace the invalid Zod usage where theme is defined as
z.literal(['light', 'dark', 'auto']) by using a union of literal schemas (or
z.enum) so Zod v3 accepts it; locate the theme definition in searchParams.ts
(the theme symbol) and change it to z.union([z.literal('light'),
z.literal('dark'), z.literal('auto')]) or equivalent
z.enum(['light','dark','auto']) to validate the three allowed string values.

---

Nitpick comments:
In `@e2e/tests/search-params.spec.ts`:
- Around line 66-86: Add a test to cover the case where the query param
?dynamic= is absent: inside the existing test.describe('?dynamic= search
parameter'...) add a new test (similar to the other cases) that calls
gotoAndWaitForCanvas(page, exportUrl(DYNAMIC_VIEW)) with no dynamic option, then
assert that page.locator(SEQ_ACTOR_SELECTOR) has count 0 (or is not visible)
using TIMEOUT_CANVAS; use the same helpers (gotoAndWaitForCanvas, exportUrl) and
constants (DYNAMIC_VIEW, SEQ_ACTOR_SELECTOR, TIMEOUT_CANVAS) so the absent-param
behavior is verified to match the 'diagram' default.

In `@packages/likec4/app/src/components/ColorSchemeToggle.tsx`:
- Around line 30-46: The toggle flow can leave pendingScheme set when navigation
succeeds, causing the subsequent effect to call setColorScheme even if
computedColorScheme already matches the pending value; update the
post-navigation logic so that when navigation resolves you compare
computedColorScheme to pendingScheme.current and only call
setColorScheme(pending) if they differ, then clear pendingScheme.current (and
still clear it in the navigate.catch branch). Refer to pendingScheme,
navigate({...}).catch(...), computedColorScheme, and setColorScheme to locate
and implement the check and clearing.

Copy link
Copy Markdown
Contributor

@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.

🧹 Nitpick comments (3)
e2e/tests/search-params.spec.ts (3)

84-84: Absence assertions lack explicit timeout — inconsistent with the rest of the file.

Lines 84 and 103 use toHaveCount(0) without a timeout while every positive assertion in the file passes { timeout: TIMEOUT_CANVAS }. If nodes or overlays are rendered asynchronously after the canvas becomes visible, an absence check without a matching wait window could pass prematurely before the element appears.

♻️ Suggested fix for consistency
-      await expect(page.locator(SEQ_ACTOR_SELECTOR)).toHaveCount(0)
+      await expect(page.locator(SEQ_ACTOR_SELECTOR)).toHaveCount(0, { timeout: TIMEOUT_CANVAS })
-    await expect(page.locator(RELATIONSHIPS_BROWSER)).toHaveCount(0)
+    await expect(page.locator(RELATIONSHIPS_BROWSER)).toHaveCount(0, { timeout: TIMEOUT_CANVAS })

Also applies to: 103-103

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

In `@e2e/tests/search-params.spec.ts` at line 84, The absence assertions using
page.locator(SEQ_ACTOR_SELECTOR).toHaveCount(0) are missing an explicit timeout
and should mirror the other checks; update both occurrences (the locator calls
at the lines referencing SEQ_ACTOR_SELECTOR) to pass { timeout: TIMEOUT_CANVAS }
into toHaveCount so the test waits the same window before asserting absence.

68-68: Hardcoded CSS class selectors couple tests to implementation details.

.react-flow__node-seq-actor and .relationships-browser are internal class names. If either is renamed or replaced with a CSS-module-scoped name, these tests will break without a compile-time signal. Where possible, prefer data-testid attributes or other stable semantic selectors for E2E assertions.

Also applies to: 94-94

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

In `@e2e/tests/search-params.spec.ts` at line 68, The test uses hardcoded
implementation-specific selectors (SEQ_ACTOR_SELECTOR =
'.react-flow__node-seq-actor' and another use of '.relationships-browser') which
makes E2E fragile; change the tests to use stable semantic selectors (e.g.,
data-testid attributes) by replacing SEQ_ACTOR_SELECTOR and the other selector
with constants that reference data-testid values (e.g., data-testid="seq-actor"
and data-testid="relationships-browser"), and add those data-testid attributes
to the corresponding components/DOM elements in the app code if they don't
exist; update any test queries that reference SEQ_ACTOR_SELECTOR or
'.relationships-browser' to query by the new data-testid selectors instead.

47-59: Misleading test name for absent label in loop-generated tests.

The template `?theme=${label}` with label = 'absent' produces the name ?theme=absent does not force — falls back to light, which reads as though the URL contained ?theme=absent. The actual scenario is that the parameter is entirely absent. The same issue occurs in the ?dynamic= block with ['absent', {}]. Consider making the label self-describing in context, e.g.:

♻️ Suggested label adjustments
-      ['auto', { theme: 'auto' }],
-      ['absent', {}],
-      ['invalid (sepia)', { theme: 'sepia' }],
+      ['?theme=auto', { theme: 'auto' }],
+      ['(absent)', {}],
+      ['?theme=sepia (invalid)', { theme: 'sepia' }],
-      ['diagram', { dynamic: 'diagram' }],
-      ['absent', {}],
-      ['invalid (timeline)', { dynamic: 'timeline' }],
+      ['?dynamic=diagram', { dynamic: 'diagram' }],
+      ['(absent)', {}],
+      ['?dynamic=timeline (invalid)', { dynamic: 'timeline' }],

Then update the test description to drop the redundant prefix:

-    test(`?theme=${label} does not force — falls back to light`, ...
+    test(`${label} does not force — falls back to light`, ...

Also applies to: 75-86

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

In `@e2e/tests/search-params.spec.ts` around lines 47 - 59, The test title
generation is misleading for the 'absent' case because the template
`?theme=${label}` yields `?theme=absent` even though the parameter is actually
omitted; update the loop that iterates over `const [label, extra]` (and the
analogous `?dynamic=` block) to use a descriptive display label (e.g., compute
displayLabel = label === 'absent' ? 'parameter absent' : `?theme=${label}`) and
use displayLabel in the test name, or simply change the label string from
'absent' to a self-describing value like 'parameter absent'/'missing' and adjust
the template accordingly; keep the assertion/logic using gotoAndWaitForCanvas,
exportUrl(STATIC_VIEW, extra) and COLOR_SCHEME_ATTR unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@e2e/tests/search-params.spec.ts`:
- Line 84: The absence assertions using
page.locator(SEQ_ACTOR_SELECTOR).toHaveCount(0) are missing an explicit timeout
and should mirror the other checks; update both occurrences (the locator calls
at the lines referencing SEQ_ACTOR_SELECTOR) to pass { timeout: TIMEOUT_CANVAS }
into toHaveCount so the test waits the same window before asserting absence.
- Line 68: The test uses hardcoded implementation-specific selectors
(SEQ_ACTOR_SELECTOR = '.react-flow__node-seq-actor' and another use of
'.relationships-browser') which makes E2E fragile; change the tests to use
stable semantic selectors (e.g., data-testid attributes) by replacing
SEQ_ACTOR_SELECTOR and the other selector with constants that reference
data-testid values (e.g., data-testid="seq-actor" and
data-testid="relationships-browser"), and add those data-testid attributes to
the corresponding components/DOM elements in the app code if they don't exist;
update any test queries that reference SEQ_ACTOR_SELECTOR or
'.relationships-browser' to query by the new data-testid selectors instead.
- Around line 47-59: The test title generation is misleading for the 'absent'
case because the template `?theme=${label}` yields `?theme=absent` even though
the parameter is actually omitted; update the loop that iterates over `const
[label, extra]` (and the analogous `?dynamic=` block) to use a descriptive
display label (e.g., compute displayLabel = label === 'absent' ? 'parameter
absent' : `?theme=${label}`) and use displayLabel in the test name, or simply
change the label string from 'absent' to a self-describing value like 'parameter
absent'/'missing' and adjust the template accordingly; keep the assertion/logic
using gotoAndWaitForCanvas, exportUrl(STATIC_VIEW, extra) and COLOR_SCHEME_ATTR
unchanged.

@m9810223 m9810223 changed the title feat: use forceColorScheme for ?theme= param and add E2E search param… feat: use forceColorScheme for ?theme= param Feb 21, 2026
@m9810223 m9810223 marked this pull request as draft February 21, 2026 15:13
@m9810223 m9810223 marked this pull request as ready for review February 22, 2026 11:26
…ests

Export route does not wire up dynamic variant switching or the
relationship browser. Switch those tests to the interactive view route
where the features are actually available.
Copy link
Copy Markdown
Contributor

@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)
e2e/tests/search-params.spec.ts (1)

79-79: Inline CSS selectors belong in the shared selectors helper.

.react-flow__node-seq-actor (line 79) and .relationships-browser (line 105) are hardcoded strings with no central registry. The project already has e2e/helpers/selectors.ts for this purpose. Moving them there keeps selector maintenance in one place and makes future refactors less error-prone.

Also applies to: 105-105

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

In `@e2e/tests/search-params.spec.ts` at line 79, The test hardcodes CSS selectors
(the constant SEQ_ACTOR_SELECTOR = '.react-flow__node-seq-actor' and the literal
'.relationships-browser') instead of using the shared selectors registry; move
these strings into the shared selectors helper (selectors.ts) as exported
constants (e.g., export const SEQ_ACTOR_SELECTOR = '.react-flow__node-seq-actor'
and export const RELATIONSHIPS_BROWSER_SELECTOR = '.relationships-browser'),
then update e2e/tests/search-params.spec.ts to import and use those exported
symbols instead of local literals, and scan other tests to replace any duplicate
hardcoded occurrences with the new shared exports.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@e2e/tests/search-params.spec.ts`:
- Line 23: The URLSearchParams initialization in the test (const params = new
URLSearchParams({ padding: '20', ...extra })) uses padding '20' which diverges
from the project's standard '22'; update the default padding value to '22' so
the test aligns with e2e/bootstrap.mjs and avoids inconsistent canvas
state—locate the URLSearchParams usage in search-params.spec.ts and change the
padding entry to '22'.
- Around line 58-70: The parameterised tests currently use the label 'absent'
which produces misleading titles like `?theme=absent` / `?dynamic=absent` even
though exportUrl(STATIC_VIEW, {}) omits the param; update the data tuples in
both the theme and dynamic loops so the absent case uses a clearer label (e.g.
'(no param)') or pull the absent case out into its own test with an explicit
title, and keep test bodies using gotoAndWaitForCanvas(page,
exportUrl(STATIC_VIEW, extra)) and assertions on COLOR_SCHEME_ATTR unchanged;
ensure you update the test(...) titles generated by the template literal so they
accurately reflect no parameter being passed.

---

Nitpick comments:
In `@e2e/tests/search-params.spec.ts`:
- Line 79: The test hardcodes CSS selectors (the constant SEQ_ACTOR_SELECTOR =
'.react-flow__node-seq-actor' and the literal '.relationships-browser') instead
of using the shared selectors registry; move these strings into the shared
selectors helper (selectors.ts) as exported constants (e.g., export const
SEQ_ACTOR_SELECTOR = '.react-flow__node-seq-actor' and export const
RELATIONSHIPS_BROWSER_SELECTOR = '.relationships-browser'), then update
e2e/tests/search-params.spec.ts to import and use those exported symbols instead
of local literals, and scan other tests to replace any duplicate hardcoded
occurrences with the new shared exports.

const DYNAMIC_VIEW = 'dynamic-view-1'

function exportUrl(viewId: string, extra?: Record<string, string>): string {
const params = new URLSearchParams({ padding: '20', ...extra })
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

padding: '20' diverges from the project's standard padding=22.

e2e/bootstrap.mjs constructs export URLs with ?padding=22. Using '20' here creates an inconsistency that could produce a different canvas state (e.g. clipping) from what the rest of the test suite uses. Align the default to '22' unless there's a deliberate reason for the difference.

🔧 Suggested fix
-  const params = new URLSearchParams({ padding: '20', ...extra })
+  const params = new URLSearchParams({ padding: '22', ...extra })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const params = new URLSearchParams({ padding: '20', ...extra })
const params = new URLSearchParams({ padding: '22', ...extra })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/tests/search-params.spec.ts` at line 23, The URLSearchParams
initialization in the test (const params = new URLSearchParams({ padding: '20',
...extra })) uses padding '20' which diverges from the project's standard '22';
update the default padding value to '22' so the test aligns with
e2e/bootstrap.mjs and avoids inconsistent canvas state—locate the
URLSearchParams usage in search-params.spec.ts and change the padding entry to
'22'.

@m9810223
Copy link
Copy Markdown
Contributor Author

@davydkov Could you please review this?

- Change exportUrl padding from 20 to 22 to match e2e/bootstrap.mjs
- Rename 'absent' labels to 'no ?theme= param' / 'no ?dynamic= param'
  so generated test titles don't misleadingly read as ?theme=absent
@davydkov
Copy link
Copy Markdown
Member

Hey @m9810223, do you mind to add changeset?

@m9810223
Copy link
Copy Markdown
Contributor Author

@davydkov changeset added!

Copy link
Copy Markdown
Contributor

@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 @.changeset/force-color-scheme-theme.md:
- Around line 1-5: The changeset currently labels 'likec4' as patch but
describes a behavioral change (URL ?theme now uses MantineProvider's
forceColorScheme and no longer persists to localStorage); update the changeset
to use at least a `minor` bump instead of `patch` and expand the description to
explicitly state that theme preferences specified via the URL will no longer be
persisted to localStorage (mentioning the move to forceColorScheme) so consumers
are aware of the breaking/behavioral change.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 67d5069 and 225d1a7.

📒 Files selected for processing (1)
  • .changeset/force-color-scheme-theme.md

Comment on lines +1 to +5
---
'likec4': patch
---

Use MantineProvider's forceColorScheme for ?theme= URL parameter instead of persisting to localStorage
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Explore the changesets directory to understand versioning patterns
echo "=== Recent changesets ==="
ls -lah .changeset/ | head -20

echo -e "\n=== Changeset file contents (last few) ==="
find .changeset -maxdepth 1 -name "*.md" -type f ! -name "README.md" | sort -r | head -5 | xargs -I {} sh -c 'echo "File: {}"; head -10 {}'

echo -e "\n=== Look for documentation on changesets ==="
cat .changeset/README.md 2>/dev/null || echo "No README found"

echo -e "\n=== Look for contributing guidelines ==="
find . -maxdepth 2 -iname "*contrib*" -o -iname "*develop*" | grep -E '\.(md|txt)$'

Repository: likec4/likec4

Length of output: 1225


🏁 Script executed:

#!/bin/bash
# Check CONTRIBUTING.md for version bump guidance
echo "=== CONTRIBUTING.md content ==="
cat CONTRIBUTING.md

echo -e "\n=== package.json version and dependencies ==="
cat package.json | grep -A 5 '"version"'

Repository: likec4/likec4

Length of output: 6821


🏁 Script executed:

#!/bin/bash
# Search for theme-related code and the changes mentioned in the review
echo "=== Search for theme-related code ==="
rg -i 'theme.*url|forceColorScheme|localStorage.*theme' --type ts --type js -B 2 -A 2 | head -50

echo -e "\n=== Search for the files modified in this changeset ==="
rg -i 'forceColorScheme|localStorage' --type ts --type jsx --max-count 20

echo -e "\n=== Check if there's a CHANGELOG ==="
find . -maxdepth 2 -iname 'CHANGELOG*' -o -iname 'HISTORY*'

Repository: likec4/likec4

Length of output: 4048


🏁 Script executed:

#!/bin/bash
# Check CHANGELOG for version bump patterns
echo "=== CHANGELOG entries (last 30 lines) ==="
tail -30 CHANGELOG.md

echo -e "\n=== searchParams.ts implementation ==="
cat -n packages/likec4/app/src/searchParams.ts

Repository: likec4/likec4

Length of output: 3203


Reconsider version bump type from patch to minor.

The changeset description—"Use MantineProvider's forceColorScheme for ?theme= URL parameter instead of persisting to localStorage"—indicates a behavior change, not a bug fix. The code confirms:

  • Theme parameters previously persisted to localStorage
  • Now using forceColorScheme instead, preventing localStorage persistence
  • URL-specified theme overrides localStorage while preserving manual user preferences

Patch versions are reserved for backwards-compatible bug fixes. Since this modifies how the theme feature behaves (no longer persisting to localStorage), it qualifies as at least a minor version bump (new/modified behavior). If existing users relied on localStorage persistence, this could warrant a major version bump.

The description could also explicitly mention that theme preferences no longer persist to localStorage when specified via URL parameter, helping users understand the behavioral change.

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

In @.changeset/force-color-scheme-theme.md around lines 1 - 5, The changeset
currently labels 'likec4' as patch but describes a behavioral change (URL ?theme
now uses MantineProvider's forceColorScheme and no longer persists to
localStorage); update the changeset to use at least a `minor` bump instead of
`patch` and expand the description to explicitly state that theme preferences
specified via the URL will no longer be persisted to localStorage (mentioning
the move to forceColorScheme) so consumers are aware of the breaking/behavioral
change.

…ption

The ?theme= URL parameter change is a behavioral/semantic change (no longer
persists to localStorage, search param default changed from 'auto' to
undefined), warranting a minor bump rather than patch.
@davydkov davydkov enabled auto-merge March 2, 2026 17:58
@davydkov davydkov merged commit 1020eea into likec4:main Mar 2, 2026
13 checks passed
@likec4-ci likec4-ci bot mentioned this pull request Mar 2, 2026
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.

2 participants