-
Notifications
You must be signed in to change notification settings - Fork 13
fix: enhance dark mode support in theme handling #1808
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughThis pull request introduces a comprehensive theme management system featuring dark mode support, GraphQL-backed theme mutations, multi-source theme initialization from CSS variables and DOM state, and server-client synchronization via a new Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Areas requiring extra attention:
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (2)
🧰 Additional context used📓 Path-based instructions (10)**/*.{ts,tsx,js,jsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
**/*.test.{ts,tsx,js,jsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
web/**/*📄 CodeRabbit inference engine (CLAUDE.md)
Files:
web/__test__/**/*.test.{ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
**/*.{ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
**/*📄 CodeRabbit inference engine (.cursor/rules/default.mdc)
Files:
**/*.test.ts📄 CodeRabbit inference engine (.cursor/rules/web-testing-rules.mdc)
Files:
**/__test__/store/**/*.ts📄 CodeRabbit inference engine (.cursor/rules/web-testing-rules.mdc)
Files:
**/store/**/*.ts📄 CodeRabbit inference engine (.cursor/rules/web-testing-rules.mdc)
Files:
web/src/**/*.ts📄 CodeRabbit inference engine (CLAUDE.md)
Files:
🧠 Learnings (41)📓 Common learnings📚 Learning: 2025-11-24T17:52:26.908ZApplied to files:
📚 Learning: 2025-11-24T17:52:26.908ZApplied to files:
📚 Learning: 2025-11-24T17:52:26.908ZApplied to files:
📚 Learning: 2025-11-24T17:52:26.907ZApplied to files:
📚 Learning: 2025-11-24T17:52:26.908ZApplied to files:
📚 Learning: 2025-11-24T17:52:26.907ZApplied to files:
📚 Learning: 2025-11-24T17:52:26.907ZApplied to files:
📚 Learning: 2025-11-24T17:51:37.915ZApplied to files:
📚 Learning: 2025-11-24T17:51:37.915ZApplied to files:
📚 Learning: 2025-11-24T17:52:26.907ZApplied to files:
📚 Learning: 2025-02-20T15:52:58.297ZApplied to files:
📚 Learning: 2025-11-24T17:52:26.908ZApplied to files:
📚 Learning: 2025-11-24T17:52:26.908ZApplied to files:
📚 Learning: 2025-11-24T17:52:26.908ZApplied to files:
📚 Learning: 2025-11-24T17:52:26.907ZApplied to files:
📚 Learning: 2025-11-24T17:52:26.907ZApplied to files:
📚 Learning: 2025-11-24T17:52:26.907ZApplied to files:
📚 Learning: 2025-11-24T17:51:37.915ZApplied to files:
📚 Learning: 2025-08-09T01:03:29.676ZApplied to files:
📚 Learning: 2025-11-24T17:52:26.907ZApplied to files:
📚 Learning: 2025-11-24T17:52:26.907ZApplied to files:
📚 Learning: 2025-02-20T15:52:56.733ZApplied to files:
📚 Learning: 2025-11-24T17:52:26.907ZApplied to files:
📚 Learning: 2025-11-24T17:52:26.908ZApplied to files:
📚 Learning: 2025-11-24T17:52:26.907ZApplied to files:
📚 Learning: 2025-11-24T17:52:26.908ZApplied to files:
📚 Learning: 2025-11-24T17:52:26.908ZApplied to files:
📚 Learning: 2025-11-24T17:52:26.908ZApplied to files:
📚 Learning: 2024-12-17T14:59:32.458ZApplied to files:
📚 Learning: 2025-02-24T14:51:21.328ZApplied to files:
📚 Learning: 2025-02-21T18:40:10.810ZApplied to files:
📚 Learning: 2025-11-24T17:51:46.348ZApplied to files:
📚 Learning: 2025-11-24T17:52:26.908ZApplied to files:
📚 Learning: 2025-11-24T17:52:26.907ZApplied to files:
📚 Learning: 2025-03-27T23:52:57.888ZApplied to files:
📚 Learning: 2025-03-27T23:52:57.888ZApplied to files:
📚 Learning: 2025-03-27T23:33:13.215ZApplied to files:
📚 Learning: 2025-03-27T23:33:13.215ZApplied to files:
📚 Learning: 2025-06-13T17:14:21.739ZApplied to files:
📚 Learning: 2024-12-09T15:45:46.492ZApplied to files:
🧬 Code graph analysis (1)web/__test__/store/theme.test.ts (1)
🔇 Additional comments (7)
Comment |
There was a problem hiding this 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
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/include/web-components-extractor.php(1 hunks)web/src/store/theme.ts(2 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*
📄 CodeRabbit inference engine (.cursor/rules/default.mdc)
Never add comments unless they are needed for clarity of function
Files:
plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/include/web-components-extractor.phpweb/src/store/theme.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,js,jsx}: Always use TypeScript imports with.jsextensions for ESM compatibility
Never add comments unless they are needed for clarity of function
Never add comments for obvious things, and avoid commenting when starting and ending code blocks
Files:
web/src/store/theme.ts
web/**/*
📄 CodeRabbit inference engine (CLAUDE.md)
Always run
pnpm codegenfor GraphQL code generation in the web directory
Files:
web/src/store/theme.ts
web/src/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Ensure Vue reactivity imports are added to store files (computed, ref, watchEffect)
Files:
web/src/store/theme.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Never use theanytype. Always prefer proper typing
Avoid using casting whenever possible, prefer proper typing from the start
Files:
web/src/store/theme.ts
**/store/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/web-testing-rules.mdc)
Ensure Vue reactivity imports like
computed,ref, andwatchEffectare added to store files even with Nuxt auto-imports enabled
Files:
web/src/store/theme.ts
🧠 Learnings (5)
📓 Common learnings
Learnt from: elibosley
Repo: unraid/api PR: 972
File: web/store/theme.ts:46-49
Timestamp: 2024-12-17T14:59:32.458Z
Learning: In the `web/store/theme.ts` file of the Unraid web application, the header is intentionally designed to have a light background with dark text in dark mode, and a dark background with light text in light mode.
Learnt from: elibosley
Repo: unraid/api PR: 1181
File: web/store/theme.ts:210-216
Timestamp: 2025-02-21T18:40:10.810Z
Learning: When updating theme-related CSS variables via `cssText`, preserve existing non-theme styles by filtering out only theme-related rules (those starting with '--') and combining them with the new theme styles.
Learnt from: elibosley
Repo: unraid/api PR: 1181
File: web/store/theme.ts:0-0
Timestamp: 2025-02-24T14:51:21.328Z
Learning: In the Unraid API project's theme system, exact TypeScript type definitions are preferred over index signatures for theme variables to ensure better type safety.
📚 Learning: 2024-12-17T14:59:32.458Z
Learnt from: elibosley
Repo: unraid/api PR: 972
File: web/store/theme.ts:46-49
Timestamp: 2024-12-17T14:59:32.458Z
Learning: In the `web/store/theme.ts` file of the Unraid web application, the header is intentionally designed to have a light background with dark text in dark mode, and a dark background with light text in light mode.
Applied to files:
plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/include/web-components-extractor.phpweb/src/store/theme.ts
📚 Learning: 2025-02-21T18:40:10.810Z
Learnt from: elibosley
Repo: unraid/api PR: 1181
File: web/store/theme.ts:210-216
Timestamp: 2025-02-21T18:40:10.810Z
Learning: When updating theme-related CSS variables via `cssText`, preserve existing non-theme styles by filtering out only theme-related rules (those starting with '--') and combining them with the new theme styles.
Applied to files:
plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/include/web-components-extractor.phpweb/src/store/theme.ts
📚 Learning: 2025-02-24T14:51:21.328Z
Learnt from: elibosley
Repo: unraid/api PR: 1181
File: web/store/theme.ts:0-0
Timestamp: 2025-02-24T14:51:21.328Z
Learning: In the Unraid API project's theme system, exact TypeScript type definitions are preferred over index signatures for theme variables to ensure better type safety.
Applied to files:
plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/include/web-components-extractor.phpweb/src/store/theme.ts
📚 Learning: 2025-02-20T15:52:56.733Z
Learnt from: elibosley
Repo: unraid/api PR: 1155
File: web/store/theme.ts:49-50
Timestamp: 2025-02-20T15:52:56.733Z
Learning: CSS variable names in the theme store should be concise and follow established patterns. For example, prefer '--gradient-start' over '--color-customgradient-start' to maintain consistency with other variable names.
Applied to files:
web/src/store/theme.ts
🧬 Code graph analysis (1)
plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/include/web-components-extractor.php (2)
api/src/unraid-api/graph/resolvers/customization/customization.resolver.ts (1)
theme(58-60)api/src/unraid-api/graph/resolvers/vars/vars.resolver.ts (1)
vars(18-23)
🔇 Additional comments (3)
web/src/store/theme.ts (2)
53-60: LGTM!The function correctly reads the CSS variable set by PHP and converts it to a boolean, with appropriate SSR handling.
67-67: LGTM!Setting the CSS variable alongside the dark class creates a bidirectional sync, allowing the variable to stay in sync with runtime theme changes.
plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/include/web-components-extractor.php (1)
212-214: Dark theme lists are synchronized.Verification confirms both the PHP (
web-components-extractor.php, line 212) and TypeScript (web/src/store/theme.ts, line 13) dark theme lists contain identical values['gray', 'black']. The lists are currently in sync, and dark mode detection is consistent between backend and frontend.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #1808 +/- ##
==========================================
- Coverage 52.04% 51.84% -0.20%
==========================================
Files 876 878 +2
Lines 50509 50848 +339
Branches 5023 5053 +30
==========================================
+ Hits 26285 26361 +76
- Misses 24149 24411 +262
- Partials 75 76 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
web/__test__/store/theme.test.ts (1)
200-225: Dark‑mode init test looks solid; consider simplifying mock cleanup and commentsThe new test correctly verifies initialization from
--theme-dark-modeand the resultingdarkclasses ondocumentElementandbody. Two small cleanups:
- Since
afterEachalready callsvi.restoreAllMocks(), the explicitvi.restoreAllMocks()inside this test is redundant and can unexpectedly reset other spies (likerequestAnimationFrame) mid‑test. Relying on the suite‑level cleanup keeps the lifecycle clearer.- The inline comments here add little beyond what the code already conveys and slightly conflict with the “no unnecessary comments” guideline; you can safely drop them.
Example minimal version:
- it('should initialize dark mode from CSS variable on store creation', () => { - // Mock getComputedStyle to return dark mode + it('should initialize dark mode from CSS variable on store creation', () => { const originalGetComputedStyle = window.getComputedStyle; vi.spyOn(window, 'getComputedStyle').mockImplementation((el) => { const style = originalGetComputedStyle(el); if (el === document.documentElement) { return { ...style, getPropertyValue: (prop: string) => { if (prop === '--theme-dark-mode') { return '1'; } return style.getPropertyValue(prop); }, } as CSSStyleDeclaration; } return style; }); createStore(); - // Should have added dark class to documentElement expect(document.documentElement.classList.add).toHaveBeenCalledWith('dark'); expect(document.body.classList.add).toHaveBeenCalledWith('dark'); - - vi.restoreAllMocks(); });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
web/__test__/store/theme.test.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,js,jsx}: Always use TypeScript imports with.jsextensions for ESM compatibility
Never add comments unless they are needed for clarity of function
Never add comments for obvious things, and avoid commenting when starting and ending code blocks
Files:
web/__test__/store/theme.test.ts
**/*.test.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.test.{ts,tsx,js,jsx}: Use VITEST for test suite, not jest
Use.rejects.toThrow()without arguments to test that functions throw errors, not exact error message strings
Files:
web/__test__/store/theme.test.ts
web/**/*
📄 CodeRabbit inference engine (CLAUDE.md)
Always run
pnpm codegenfor GraphQL code generation in the web directory
Files:
web/__test__/store/theme.test.ts
web/__test__/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/__test__/**/*.test.{ts,tsx}: Test component behavior and output, not implementation details
Usemountfrom Vue Test Utils for component testing
Stub complex child components that aren't the focus of the test
Find elements with semantic queries likefind('button')rather than data-test IDs
UsecreatePinia()andsetActivePiniawhen testing Store files, only usecreateTestingPiniaif you specifically need its testing features
Files:
web/__test__/store/theme.test.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Never use theanytype. Always prefer proper typing
Avoid using casting whenever possible, prefer proper typing from the start
Files:
web/__test__/store/theme.test.ts
**/*
📄 CodeRabbit inference engine (.cursor/rules/default.mdc)
Never add comments unless they are needed for clarity of function
Files:
web/__test__/store/theme.test.ts
**/*.test.ts
📄 CodeRabbit inference engine (.cursor/rules/web-testing-rules.mdc)
**/*.test.ts: Use.rejects.toThrow()without arguments to test that functions throw errors. Don't test exact error message strings unless the message format is specifically what you're testing
Test what the code does, not implementation details like exact error message wording
Mock external services and API calls
Usevi.mock()for module-level mocks
Specify return values for component methods withvi.spyOn()
Reset mocks between tests withvi.clearAllMocks()
Always await async operations before making assertions
Files:
web/__test__/store/theme.test.ts
**/__test__/store/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/web-testing-rules.mdc)
**/__test__/store/**/*.ts: UsecreatePiniaandsetActivePiniawhen testing Pinia store files
UsecreatePinia()instead ofcreateTestingPinia()for most Pinia store tests
Only usecreateTestingPiniaif you specifically need its testing features for Pinia stores
Let stores initialize with their natural default state instead of forcing initial state
Do not mock the store being tested in the test file when usingcreatePinia()
Place all mock declarations at the top level in Pinia store tests
Use factory functions for module mocks in Pinia store tests to avoid hoisting issues
Test Pinia action side effects and state changes
Verify Pinia actions are called with correct parameters
Mock external dependencies appropriately in Pinia store tests
Test computed properties in Pinia stores by accessing them directly
Verify state changes after Pinia store actions
Test Pinia store getter dependencies are properly mocked
Test Pinia store interactions with other stores
Verify proper error handling in Pinia store tests
Test async operations completely in Pinia store tests
Override specific Pinia action implementations when needed in tests
Set initial state for focused Pinia store testing
Files:
web/__test__/store/theme.test.ts
**/store/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/web-testing-rules.mdc)
Ensure Vue reactivity imports like
computed,ref, andwatchEffectare added to store files even with Nuxt auto-imports enabled
Files:
web/__test__/store/theme.test.ts
🧠 Learnings (18)
📓 Common learnings
Learnt from: elibosley
Repo: unraid/api PR: 972
File: web/store/theme.ts:46-49
Timestamp: 2024-12-17T14:59:32.458Z
Learning: In the `web/store/theme.ts` file of the Unraid web application, the header is intentionally designed to have a light background with dark text in dark mode, and a dark background with light text in light mode.
Learnt from: elibosley
Repo: unraid/api PR: 1181
File: web/store/theme.ts:210-216
Timestamp: 2025-02-21T18:40:10.810Z
Learning: When updating theme-related CSS variables via `cssText`, preserve existing non-theme styles by filtering out only theme-related rules (those starting with '--') and combining them with the new theme styles.
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Test Pinia store getter dependencies are properly mocked
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Mock external dependencies appropriately in Pinia store tests
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Use factory functions for module mocks in Pinia store tests to avoid hoisting issues
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Place all mock declarations at the top level in Pinia store tests
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Test Pinia action side effects and state changes
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Test Pinia store interactions with other stores
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Do not mock the store being tested in the test file when using `createPinia()`
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:51:37.915Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:51:37.915Z
Learning: Applies to web/__test__/**/*.test.{ts,tsx} : Use `createPinia()` and `setActivePinia` when testing Store files, only use `createTestingPinia` if you specifically need its testing features
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Test component behavior and output, not implementation details
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Verify state changes after Pinia store actions
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/*.test.ts : Specify return values for component methods with `vi.spyOn()`
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/*.test.ts : Reset mocks between tests with `vi.clearAllMocks()`
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/*.test.ts : Use `vi.mock()` for module-level mocks
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Verify element existence with `expect(element.exists()).toBe(true)`
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Use `createTestingPinia()` for mocking stores in Vue components
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Verify that the expected elements are rendered in Vue components
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Set initial state for focused Pinia store testing
Applied to files:
web/__test__/store/theme.test.ts
|
🚀 Storybook has been deployed to staging: https://unraid-ui-storybook-staging.unraid-workers.workers.dev |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // Lazy query - only executes when explicitly called | ||
| const { load, onResult, onError } = useLazyQuery<GetThemeQuery>(GET_THEME_QUERY, null, { | ||
| fetchPolicy: 'cache-and-network', | ||
| nextFetchPolicy: 'cache-first', | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trigger theme query after switching to useLazyQuery
useThemeStore now instantiates useLazyQuery for GET_THEME_QUERY, but load()/fetchTheme() are never called anywhere (rg "fetchTheme" web only returns this definition), so the theme query no longer runs on startup. This regresses the previous eager fetch and leaves server-provided theme values (colors, banner flags, etc.) at defaults unless some other code happens to reload the page, meaning users’ configured theme is never applied in a single-page session.
Useful? React with 👍 / 👎.
There was a problem hiding this 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 (4)
unraid-ui/src/composables/useTeleport.ts (1)
12-19: Dark class is applied at container creation but won't update dynamically.The virtual container is a singleton created once. If the theme changes after the container is created (e.g., via dev tools), the dark class won't be updated. This may be acceptable if theme changes always trigger a page refresh in production, but could cause visual inconsistencies during development.
Consider adding a MutationObserver or exposing a method to sync the dark class if dynamic theme switching without refresh is needed.
web/src/store/theme.ts (1)
243-247: Consider extracting DOM initialization to a named function.The inline initialization logic works, but extracting it to a named function (similar to
bootstrapDarkClass) would improve readability and make the initialization sequence clearer.+ const initializeThemeFromDom = () => { + const domThemeName = themeName.value; + if (domThemeName && domThemeName !== DEFAULT_THEME.name) { + theme.value.name = domThemeName; + } + }; + // Initialize dark mode from CSS variable set by PHP or any pre-applied .dark class bootstrapDarkClass(); + initializeThemeFromDom(); - // Initialize theme from DOM on store creation - const domThemeName = themeName.value; - if (domThemeName && domThemeName !== DEFAULT_THEME.name) { - theme.value.name = domThemeName; - }web/__test__/store/theme.test.ts (2)
21-27: MockonResultshould accept and store a callback.The store calls
onResult(callback)to register a result handler. The current mock returnsvi.fn()which accepts any args but doesn't do anything with them. For tests that need to simulate query results, you'll need a more sophisticated mock.If you need to test query result handling in the future:
useLazyQuery: () => { const callbacks: Array<(result: { data: unknown }) => void> = []; return { load: vi.fn(), result: ref(null), loading: ref(false), onResult: vi.fn((cb) => callbacks.push(cb)), onError: vi.fn(), // Helper for tests to trigger result callbacks _triggerResult: (data: unknown) => callbacks.forEach(cb => cb({ data })), }; },
174-196: Test passes for the wrong reason - tests flag check, not CSS variable behavior.This test expects
undefinedwhen the CSS variable is empty, but it actually returnsundefinedbecausetheme.value.bannerandtheme.value.bannerGradientare bothfalseby default. The CSS variable mock is never reached.Consider clarifying the test name or adjusting to actually test the CSS variable fallback behavior.
- it('should return undefined when bannerGradient CSS variable is not set', () => { + it('should return undefined when banner flags are disabled', () => { + const store = createStore(); + // Default theme has banner: false, bannerGradient: false + expect(store.bannerGradient).toBeUndefined(); + }); + + it('should return undefined when CSS variable is not set but banner flags are enabled', () => { const originalGetComputedStyle = window.getComputedStyle; vi.spyOn(window, 'getComputedStyle').mockImplementation((el) => { // ... existing mock that returns empty string }); const store = createStore(); + store.setTheme({ banner: true, bannerGradient: true }); expect(store.bannerGradient).toBeUndefined(); vi.restoreAllMocks(); });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/include/web-components-extractor.php(2 hunks)unraid-ui/src/composables/useTeleport.ts(1 hunks)web/__test__/store/theme.test.ts(3 hunks)web/src/components/Wrapper/mount-engine.ts(1 hunks)web/src/store/theme.ts(5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/include/web-components-extractor.php
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,js,jsx}: Always use TypeScript imports with.jsextensions for ESM compatibility
Never add comments unless they are needed for clarity of function
Never add comments for obvious things, and avoid commenting when starting and ending code blocks
Files:
web/src/components/Wrapper/mount-engine.tsunraid-ui/src/composables/useTeleport.tsweb/__test__/store/theme.test.tsweb/src/store/theme.ts
web/**/*
📄 CodeRabbit inference engine (CLAUDE.md)
Always run
pnpm codegenfor GraphQL code generation in the web directory
Files:
web/src/components/Wrapper/mount-engine.tsweb/__test__/store/theme.test.tsweb/src/store/theme.ts
web/src/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Ensure Vue reactivity imports are added to store files (computed, ref, watchEffect)
Files:
web/src/components/Wrapper/mount-engine.tsweb/src/store/theme.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Never use theanytype. Always prefer proper typing
Avoid using casting whenever possible, prefer proper typing from the start
Files:
web/src/components/Wrapper/mount-engine.tsunraid-ui/src/composables/useTeleport.tsweb/__test__/store/theme.test.tsweb/src/store/theme.ts
**/*
📄 CodeRabbit inference engine (.cursor/rules/default.mdc)
Never add comments unless they are needed for clarity of function
Files:
web/src/components/Wrapper/mount-engine.tsunraid-ui/src/composables/useTeleport.tsweb/__test__/store/theme.test.tsweb/src/store/theme.ts
**/*.test.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.test.{ts,tsx,js,jsx}: Use VITEST for test suite, not jest
Use.rejects.toThrow()without arguments to test that functions throw errors, not exact error message strings
Files:
web/__test__/store/theme.test.ts
web/__test__/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/__test__/**/*.test.{ts,tsx}: Test component behavior and output, not implementation details
Usemountfrom Vue Test Utils for component testing
Stub complex child components that aren't the focus of the test
Find elements with semantic queries likefind('button')rather than data-test IDs
UsecreatePinia()andsetActivePiniawhen testing Store files, only usecreateTestingPiniaif you specifically need its testing features
Files:
web/__test__/store/theme.test.ts
**/*.test.ts
📄 CodeRabbit inference engine (.cursor/rules/web-testing-rules.mdc)
**/*.test.ts: Use.rejects.toThrow()without arguments to test that functions throw errors. Don't test exact error message strings unless the message format is specifically what you're testing
Test what the code does, not implementation details like exact error message wording
Mock external services and API calls
Usevi.mock()for module-level mocks
Specify return values for component methods withvi.spyOn()
Reset mocks between tests withvi.clearAllMocks()
Always await async operations before making assertions
Files:
web/__test__/store/theme.test.ts
**/__test__/store/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/web-testing-rules.mdc)
**/__test__/store/**/*.ts: UsecreatePiniaandsetActivePiniawhen testing Pinia store files
UsecreatePinia()instead ofcreateTestingPinia()for most Pinia store tests
Only usecreateTestingPiniaif you specifically need its testing features for Pinia stores
Let stores initialize with their natural default state instead of forcing initial state
Do not mock the store being tested in the test file when usingcreatePinia()
Place all mock declarations at the top level in Pinia store tests
Use factory functions for module mocks in Pinia store tests to avoid hoisting issues
Test Pinia action side effects and state changes
Verify Pinia actions are called with correct parameters
Mock external dependencies appropriately in Pinia store tests
Test computed properties in Pinia stores by accessing them directly
Verify state changes after Pinia store actions
Test Pinia store getter dependencies are properly mocked
Test Pinia store interactions with other stores
Verify proper error handling in Pinia store tests
Test async operations completely in Pinia store tests
Override specific Pinia action implementations when needed in tests
Set initial state for focused Pinia store testing
Files:
web/__test__/store/theme.test.ts
**/store/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/web-testing-rules.mdc)
Ensure Vue reactivity imports like
computed,ref, andwatchEffectare added to store files even with Nuxt auto-imports enabled
Files:
web/__test__/store/theme.test.tsweb/src/store/theme.ts
🧠 Learnings (36)
📓 Common learnings
Learnt from: elibosley
Repo: unraid/api PR: 972
File: web/store/theme.ts:46-49
Timestamp: 2024-12-17T14:59:32.458Z
Learning: In the `web/store/theme.ts` file of the Unraid web application, the header is intentionally designed to have a light background with dark text in dark mode, and a dark background with light text in light mode.
Learnt from: elibosley
Repo: unraid/api PR: 1181
File: web/store/theme.ts:210-216
Timestamp: 2025-02-21T18:40:10.810Z
Learning: When updating theme-related CSS variables via `cssText`, preserve existing non-theme styles by filtering out only theme-related rules (those starting with '--') and combining them with the new theme styles.
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Use `mount` from Vue Test Utils for component testing
Applied to files:
web/src/components/Wrapper/mount-engine.ts
📚 Learning: 2025-11-24T17:51:37.915Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:51:37.915Z
Learning: Applies to web/__test__/**/*.test.{ts,tsx} : Use `mount` from Vue Test Utils for component testing
Applied to files:
web/src/components/Wrapper/mount-engine.ts
📚 Learning: 2024-12-17T14:59:32.458Z
Learnt from: elibosley
Repo: unraid/api PR: 972
File: web/store/theme.ts:46-49
Timestamp: 2024-12-17T14:59:32.458Z
Learning: In the `web/store/theme.ts` file of the Unraid web application, the header is intentionally designed to have a light background with dark text in dark mode, and a dark background with light text in light mode.
Applied to files:
unraid-ui/src/composables/useTeleport.tsweb/src/store/theme.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Mock external dependencies appropriately in Pinia store tests
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Test Pinia store getter dependencies are properly mocked
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Use factory functions for module mocks in Pinia store tests to avoid hoisting issues
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Place all mock declarations at the top level in Pinia store tests
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Test component behavior and output, not implementation details
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:51:37.915Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:51:37.915Z
Learning: Applies to web/__test__/**/*.test.{ts,tsx} : Test component behavior and output, not implementation details
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Test Pinia action side effects and state changes
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:51:37.915Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:51:37.915Z
Learning: Applies to web/__test__/**/*.test.{ts,tsx} : Use `createPinia()` and `setActivePinia` when testing Store files, only use `createTestingPinia` if you specifically need its testing features
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Test Pinia store interactions with other stores
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Do not mock the store being tested in the test file when using `createPinia()`
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Use `createTestingPinia()` for mocking stores in Vue components
Applied to files:
web/__test__/store/theme.test.tsweb/src/store/theme.ts
📚 Learning: 2025-02-20T15:52:58.297Z
Learnt from: elibosley
Repo: unraid/api PR: 1155
File: web/store/theme.ts:161-172
Timestamp: 2025-02-20T15:52:58.297Z
Learning: The banner gradient implementation in web/store/theme.ts doesn't require explicit error handling for hexToRgba as CSS gracefully handles invalid values by ignoring them.
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Test computed properties in Pinia stores by accessing them directly
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Verify state changes after Pinia store actions
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-02-20T15:52:56.733Z
Learnt from: elibosley
Repo: unraid/api PR: 1155
File: web/store/theme.ts:49-50
Timestamp: 2025-02-20T15:52:56.733Z
Learning: CSS variable names in the theme store should be concise and follow established patterns. For example, prefer '--gradient-start' over '--color-customgradient-start' to maintain consistency with other variable names.
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/*.test.ts : Specify return values for component methods with `vi.spyOn()`
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/*.test.ts : Reset mocks between tests with `vi.clearAllMocks()`
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/*.test.ts : Use `vi.mock()` for module-level mocks
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Verify element existence with `expect(element.exists()).toBe(true)`
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Set initial state for focused Pinia store testing
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Verify that the expected elements are rendered in Vue components
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-02-24T14:51:21.328Z
Learnt from: elibosley
Repo: unraid/api PR: 1181
File: web/store/theme.ts:0-0
Timestamp: 2025-02-24T14:51:21.328Z
Learning: In the Unraid API project's theme system, exact TypeScript type definitions are preferred over index signatures for theme variables to ensure better type safety.
Applied to files:
web/src/store/theme.ts
📚 Learning: 2025-02-21T18:40:10.810Z
Learnt from: elibosley
Repo: unraid/api PR: 1181
File: web/store/theme.ts:210-216
Timestamp: 2025-02-21T18:40:10.810Z
Learning: When updating theme-related CSS variables via `cssText`, preserve existing non-theme styles by filtering out only theme-related rules (those starting with '--') and combining them with the new theme styles.
Applied to files:
web/src/store/theme.ts
📚 Learning: 2025-06-13T17:14:21.739Z
Learnt from: pujitm
Repo: unraid/api PR: 1417
File: web/components/ConnectSettings/ConnectSettings.ce.vue:11-18
Timestamp: 2025-06-13T17:14:21.739Z
Learning: The project’s build tooling auto-imports common Vue/Pinia helpers such as `storeToRefs`, so explicit import statements for them are not required.
Applied to files:
web/src/store/theme.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Nuxt is set to auto import so some Vue files may need `computed` or `ref` imported explicitly in store files
Applied to files:
web/src/store/theme.ts
📚 Learning: 2025-03-27T23:52:57.888Z
Learnt from: zackspear
Repo: unraid/api PR: 0
File: :0-0
Timestamp: 2025-03-27T23:52:57.888Z
Learning: In the unraid/api project, Vue components are compiled into web components. Using `setActivePinia(createPinia())` in store files ensures that all web components share a single Pinia store instance, which is the desired behavior. Without this initialization, each web component would have its own isolated store, breaking the intended architecture.
Applied to files:
web/src/store/theme.ts
📚 Learning: 2025-03-27T23:52:57.888Z
Learnt from: zackspear
Repo: unraid/api PR: 0
File: :0-0
Timestamp: 2025-03-27T23:52:57.888Z
Learning: In the unraid/api project, Vue components are compiled into web components. The `setActivePinia(createPinia())` call at the module level in store files is intentional and ensures all web components share a single Pinia store instance, which is the desired behavior. This shared state approach is critical for the application's architecture to function correctly.
Applied to files:
web/src/store/theme.ts
📚 Learning: 2025-03-27T23:33:13.215Z
Learnt from: zackspear
Repo: unraid/api PR: 0
File: :0-0
Timestamp: 2025-03-27T23:33:13.215Z
Learning: In the unraid/api project, Vue components are compiled into web components. Using `setActivePinia(createPinia())` in store files would break the build by causing all web components to share a singular Pinia store instance. Each web component needs its own Pinia store instance to maintain proper isolation and encapsulation.
Applied to files:
web/src/store/theme.ts
📚 Learning: 2025-03-27T23:33:13.215Z
Learnt from: zackspear
Repo: unraid/api PR: 0
File: :0-0
Timestamp: 2025-03-27T23:33:13.215Z
Learning: In the unraid/api project, Vue components are compiled into web components. Using `setActivePinia(createPinia())` in store files would break the build by causing all web components to share a singular Pinia store instance. Each web component needs its own Pinia store instance to function correctly.
Applied to files:
web/src/store/theme.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/store/**/*.ts : Ensure Vue reactivity imports like `computed`, `ref`, and `watchEffect` are added to store files even with Nuxt auto-imports enabled
Applied to files:
web/src/store/theme.ts
📚 Learning: 2024-12-09T15:45:46.492Z
Learnt from: pujitm
Repo: unraid/api PR: 975
File: web/components/Notifications/TabList.vue:1-4
Timestamp: 2024-12-09T15:45:46.492Z
Learning: In our Nuxt.js setup for the `web` project, it's not necessary to explicitly import `computed` from `vue` in Vue components, as it's globally available.
Applied to files:
web/src/store/theme.ts
📚 Learning: 2025-11-24T17:51:37.915Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:51:37.915Z
Learning: Applies to web/src/**/*.ts : Ensure Vue reactivity imports are added to store files (computed, ref, watchEffect)
Applied to files:
web/src/store/theme.ts
🧬 Code graph analysis (1)
web/__test__/store/theme.test.ts (1)
api/src/store/index.ts (1)
store(10-16)
🪛 GitHub Actions: CI - Main (API)
web/__test__/store/theme.test.ts
[error] 150-170: Assertion failed: expected bannerGradient to have a gradient string, but received undefined in theme store tests.
🪛 GitHub Check: Test API
web/src/store/theme.ts
[failure] 117-117: test/components/ColorSwitcher.test.ts > ColorSwitcher > enables gradient automatically when banner is enabled
Error: [vitest] No "useLazyQuery" export is defined on the "@vue/apollo-composable" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "importOriginal" helper inside:
vi.mock(import("@vue/apollo-composable"), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
// your mocked methods
}
})
❯ src/store/theme.ts:117:39
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:98
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:88
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:54
❯ fallbackRunWithContext ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1156:40
❯ createSetupStore ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:24
[failure] 117-117: test/components/ColorSwitcher.test.ts > ColorSwitcher > updates theme store when switches change
Error: [vitest] No "useLazyQuery" export is defined on the "@vue/apollo-composable" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "importOriginal" helper inside:
vi.mock(import("@vue/apollo-composable"), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
// your mocked methods
}
})
❯ src/store/theme.ts:117:39
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:98
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:88
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:54
❯ fallbackRunWithContext ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1156:40
❯ createSetupStore ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:24
[failure] 117-117: test/components/ColorSwitcher.test.ts > ColorSwitcher > updates theme store when color inputs change
Error: [vitest] No "useLazyQuery" export is defined on the "@vue/apollo-composable" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "importOriginal" helper inside:
vi.mock(import("@vue/apollo-composable"), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
// your mocked methods
}
})
❯ src/store/theme.ts:117:39
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:98
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:88
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:54
❯ fallbackRunWithContext ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1156:40
❯ createSetupStore ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:24
[failure] 117-117: test/components/ColorSwitcher.test.ts > ColorSwitcher > updates theme store when theme selection changes
Error: [vitest] No "useLazyQuery" export is defined on the "@vue/apollo-composable" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "importOriginal" helper inside:
vi.mock(import("@vue/apollo-composable"), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
// your mocked methods
}
})
❯ src/store/theme.ts:117:39
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:98
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:88
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:54
❯ fallbackRunWithContext ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1156:40
❯ createSetupStore ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:24
[failure] 117-117: test/components/ColorSwitcher.test.ts > ColorSwitcher > renders all form elements correctly
Error: [vitest] No "useLazyQuery" export is defined on the "@vue/apollo-composable" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "importOriginal" helper inside:
vi.mock(import("@vue/apollo-composable"), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
// your mocked methods
}
})
❯ src/store/theme.ts:117:39
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:98
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:88
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:54
❯ fallbackRunWithContext ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1156:40
❯ createSetupStore ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:24
🔇 Additional comments (4)
web/src/store/theme.ts (2)
180-196: Computed correctly guards CSS variable access with theme flags.The logic properly checks
bannerandbannerGradientflags before reading the CSS variable. The pipeline failure in tests is due to tests not setting these flags totruebefore asserting the gradient value.
232-241: Good:immediate: falseresolves the timing issue.The watch no longer triggers immediately on initialization, which prevents the race condition flagged in the past review. The
bootstrapDarkClass()call at line 114 handles the initial dark mode setup separately.web/src/components/Wrapper/mount-engine.ts (1)
182-189: LGTM - Dark mode class propagation is correctly applied to mounted elements.The detection logic correctly checks both the DOM class and CSS variable, consistent with the patterns in
theme.ts. This ensures mounted web components inherit the dark mode styling.web/__test__/store/theme.test.ts (1)
37-38: Variablestoreis correctly used throughout tests.The past review comment about an unused variable appears to be outdated or a false positive. The
storevariable is properly used for cleanup inafterEachand populated viacreateStore().
| const { load, onResult, onError } = useLazyQuery<GetThemeQuery>(GET_THEME_QUERY, null, { | ||
| fetchPolicy: 'cache-and-network', | ||
| nextFetchPolicy: 'cache-first', | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update existing Apollo mocks to include useLazyQuery.
The static analysis shows test failures in ColorSwitcher.test.ts because the Apollo mock doesn't export useLazyQuery. Any test file that mocks @vue/apollo-composable needs to include both useQuery and useLazyQuery.
The affected test file needs to update its mock:
vi.mock('@vue/apollo-composable', () => ({
useQuery: () => ({
result: ref(null),
loading: ref(false),
onResult: vi.fn(),
onError: vi.fn(),
}),
+ useLazyQuery: () => ({
+ load: vi.fn(),
+ result: ref(null),
+ loading: ref(false),
+ onResult: vi.fn(),
+ onError: vi.fn(),
+ }),
}));Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 GitHub Check: Test API
[failure] 117-117: test/components/ColorSwitcher.test.ts > ColorSwitcher > enables gradient automatically when banner is enabled
Error: [vitest] No "useLazyQuery" export is defined on the "@vue/apollo-composable" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "importOriginal" helper inside:
vi.mock(import("@vue/apollo-composable"), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
// your mocked methods
}
})
❯ src/store/theme.ts:117:39
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:98
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:88
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:54
❯ fallbackRunWithContext ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1156:40
❯ createSetupStore ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:24
[failure] 117-117: test/components/ColorSwitcher.test.ts > ColorSwitcher > updates theme store when switches change
Error: [vitest] No "useLazyQuery" export is defined on the "@vue/apollo-composable" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "importOriginal" helper inside:
vi.mock(import("@vue/apollo-composable"), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
// your mocked methods
}
})
❯ src/store/theme.ts:117:39
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:98
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:88
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:54
❯ fallbackRunWithContext ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1156:40
❯ createSetupStore ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:24
[failure] 117-117: test/components/ColorSwitcher.test.ts > ColorSwitcher > updates theme store when color inputs change
Error: [vitest] No "useLazyQuery" export is defined on the "@vue/apollo-composable" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "importOriginal" helper inside:
vi.mock(import("@vue/apollo-composable"), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
// your mocked methods
}
})
❯ src/store/theme.ts:117:39
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:98
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:88
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:54
❯ fallbackRunWithContext ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1156:40
❯ createSetupStore ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:24
[failure] 117-117: test/components/ColorSwitcher.test.ts > ColorSwitcher > updates theme store when theme selection changes
Error: [vitest] No "useLazyQuery" export is defined on the "@vue/apollo-composable" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "importOriginal" helper inside:
vi.mock(import("@vue/apollo-composable"), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
// your mocked methods
}
})
❯ src/store/theme.ts:117:39
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:98
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:88
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:54
❯ fallbackRunWithContext ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1156:40
❯ createSetupStore ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:24
[failure] 117-117: test/components/ColorSwitcher.test.ts > ColorSwitcher > renders all form elements correctly
Error: [vitest] No "useLazyQuery" export is defined on the "@vue/apollo-composable" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "importOriginal" helper inside:
vi.mock(import("@vue/apollo-composable"), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
// your mocked methods
}
})
❯ src/store/theme.ts:117:39
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:98
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:88
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:54
❯ fallbackRunWithContext ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1156:40
❯ createSetupStore ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:24
🤖 Prompt for AI Agents
In web/src/store/theme.ts around lines 117 to 120 the code uses useLazyQuery
from @vue/apollo-composable but the test mocks for that module only export
useQuery; update the test mocks that stub @vue/apollo-composable (e.g.,
ColorSwitcher.test.ts or the shared test mock file) to export both useQuery and
useLazyQuery with equivalent mock implementations and return shapes (including
load, onResult, onError, and reactive result/error/loading properties) so tests
that import useLazyQuery behave like the real composable.
There was a problem hiding this 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
♻️ Duplicate comments (2)
unraid-ui/src/composables/useTeleport.ts (1)
12-19: Inconsistent dark mode detection across files.This detection logic differs from
web/src/components/Wrapper/mount-engine.ts(lines 184-186), which checks the--theme-dark-modeCSS variable in addition to thedocumentElementclass. The--theme-dark-modevariable is set by the theme store and serves as the source of truth for dark mode state.Not checking the CSS variable here could cause the teleported container to miss dark mode in scenarios where the variable is set but the DOM classes haven't been applied yet.
Update the detection logic to align with mount-engine.ts:
- const isDark = - document.documentElement.classList.contains('dark') || - document.body?.classList.contains('dark') || - Boolean(document.querySelector('.unapi.dark')); + const isDark = + document.documentElement.classList.contains('dark') || + getComputedStyle(document.documentElement).getPropertyValue('--theme-dark-mode').trim() === '1';web/src/store/theme.ts (1)
116-120: Critical: Theme query never executes.The switch to
useLazyQueryrequires an explicit call toload()(exposed asfetchTheme), butfetchThemeis never invoked anywhere in the store initialization or by external code. This means the server theme is never fetched, and users will only see the default white theme regardless of their configured preferences.Call
fetchTheme()after the store initialization completes:// Initialize theme from DOM on store creation const domThemeName = themeName.value; if (domThemeName && domThemeName !== DEFAULT_THEME.name) { theme.value.name = domThemeName; } + + // Fetch server theme to sync with backend + fetchTheme(); return {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/include/web-components-extractor.php(2 hunks)unraid-ui/src/composables/useTeleport.ts(1 hunks)web/__test__/store/theme.test.ts(3 hunks)web/src/components/Wrapper/mount-engine.ts(1 hunks)web/src/store/theme.ts(5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/include/web-components-extractor.php
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,js,jsx}: Always use TypeScript imports with.jsextensions for ESM compatibility
Never add comments unless they are needed for clarity of function
Never add comments for obvious things, and avoid commenting when starting and ending code blocks
Files:
web/src/components/Wrapper/mount-engine.tsunraid-ui/src/composables/useTeleport.tsweb/src/store/theme.tsweb/__test__/store/theme.test.ts
web/**/*
📄 CodeRabbit inference engine (CLAUDE.md)
Always run
pnpm codegenfor GraphQL code generation in the web directory
Files:
web/src/components/Wrapper/mount-engine.tsweb/src/store/theme.tsweb/__test__/store/theme.test.ts
web/src/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Ensure Vue reactivity imports are added to store files (computed, ref, watchEffect)
Files:
web/src/components/Wrapper/mount-engine.tsweb/src/store/theme.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Never use theanytype. Always prefer proper typing
Avoid using casting whenever possible, prefer proper typing from the start
Files:
web/src/components/Wrapper/mount-engine.tsunraid-ui/src/composables/useTeleport.tsweb/src/store/theme.tsweb/__test__/store/theme.test.ts
**/*
📄 CodeRabbit inference engine (.cursor/rules/default.mdc)
Never add comments unless they are needed for clarity of function
Files:
web/src/components/Wrapper/mount-engine.tsunraid-ui/src/composables/useTeleport.tsweb/src/store/theme.tsweb/__test__/store/theme.test.ts
**/store/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/web-testing-rules.mdc)
Ensure Vue reactivity imports like
computed,ref, andwatchEffectare added to store files even with Nuxt auto-imports enabled
Files:
web/src/store/theme.tsweb/__test__/store/theme.test.ts
**/*.test.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.test.{ts,tsx,js,jsx}: Use VITEST for test suite, not jest
Use.rejects.toThrow()without arguments to test that functions throw errors, not exact error message strings
Files:
web/__test__/store/theme.test.ts
web/__test__/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/__test__/**/*.test.{ts,tsx}: Test component behavior and output, not implementation details
Usemountfrom Vue Test Utils for component testing
Stub complex child components that aren't the focus of the test
Find elements with semantic queries likefind('button')rather than data-test IDs
UsecreatePinia()andsetActivePiniawhen testing Store files, only usecreateTestingPiniaif you specifically need its testing features
Files:
web/__test__/store/theme.test.ts
**/*.test.ts
📄 CodeRabbit inference engine (.cursor/rules/web-testing-rules.mdc)
**/*.test.ts: Use.rejects.toThrow()without arguments to test that functions throw errors. Don't test exact error message strings unless the message format is specifically what you're testing
Test what the code does, not implementation details like exact error message wording
Mock external services and API calls
Usevi.mock()for module-level mocks
Specify return values for component methods withvi.spyOn()
Reset mocks between tests withvi.clearAllMocks()
Always await async operations before making assertions
Files:
web/__test__/store/theme.test.ts
**/__test__/store/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/web-testing-rules.mdc)
**/__test__/store/**/*.ts: UsecreatePiniaandsetActivePiniawhen testing Pinia store files
UsecreatePinia()instead ofcreateTestingPinia()for most Pinia store tests
Only usecreateTestingPiniaif you specifically need its testing features for Pinia stores
Let stores initialize with their natural default state instead of forcing initial state
Do not mock the store being tested in the test file when usingcreatePinia()
Place all mock declarations at the top level in Pinia store tests
Use factory functions for module mocks in Pinia store tests to avoid hoisting issues
Test Pinia action side effects and state changes
Verify Pinia actions are called with correct parameters
Mock external dependencies appropriately in Pinia store tests
Test computed properties in Pinia stores by accessing them directly
Verify state changes after Pinia store actions
Test Pinia store getter dependencies are properly mocked
Test Pinia store interactions with other stores
Verify proper error handling in Pinia store tests
Test async operations completely in Pinia store tests
Override specific Pinia action implementations when needed in tests
Set initial state for focused Pinia store testing
Files:
web/__test__/store/theme.test.ts
🧠 Learnings (34)
📓 Common learnings
Learnt from: elibosley
Repo: unraid/api PR: 972
File: web/store/theme.ts:46-49
Timestamp: 2024-12-17T14:59:32.458Z
Learning: In the `web/store/theme.ts` file of the Unraid web application, the header is intentionally designed to have a light background with dark text in dark mode, and a dark background with light text in light mode.
Learnt from: elibosley
Repo: unraid/api PR: 1155
File: web/store/theme.ts:49-50
Timestamp: 2025-02-20T15:52:56.733Z
Learning: CSS variable names in the theme store should be concise and follow established patterns. For example, prefer '--gradient-start' over '--color-customgradient-start' to maintain consistency with other variable names.
Learnt from: elibosley
Repo: unraid/api PR: 1181
File: web/store/theme.ts:210-216
Timestamp: 2025-02-21T18:40:10.810Z
Learning: When updating theme-related CSS variables via `cssText`, preserve existing non-theme styles by filtering out only theme-related rules (those starting with '--') and combining them with the new theme styles.
Learnt from: elibosley
Repo: unraid/api PR: 1155
File: web/store/theme.ts:161-172
Timestamp: 2025-02-20T15:52:58.297Z
Learning: The banner gradient implementation in web/store/theme.ts doesn't require explicit error handling for hexToRgba as CSS gracefully handles invalid values by ignoring them.
📚 Learning: 2024-12-17T14:59:32.458Z
Learnt from: elibosley
Repo: unraid/api PR: 972
File: web/store/theme.ts:46-49
Timestamp: 2024-12-17T14:59:32.458Z
Learning: In the `web/store/theme.ts` file of the Unraid web application, the header is intentionally designed to have a light background with dark text in dark mode, and a dark background with light text in light mode.
Applied to files:
unraid-ui/src/composables/useTeleport.tsweb/src/store/theme.ts
📚 Learning: 2025-02-24T14:51:21.328Z
Learnt from: elibosley
Repo: unraid/api PR: 1181
File: web/store/theme.ts:0-0
Timestamp: 2025-02-24T14:51:21.328Z
Learning: In the Unraid API project's theme system, exact TypeScript type definitions are preferred over index signatures for theme variables to ensure better type safety.
Applied to files:
web/src/store/theme.ts
📚 Learning: 2025-02-21T18:40:10.810Z
Learnt from: elibosley
Repo: unraid/api PR: 1181
File: web/store/theme.ts:210-216
Timestamp: 2025-02-21T18:40:10.810Z
Learning: When updating theme-related CSS variables via `cssText`, preserve existing non-theme styles by filtering out only theme-related rules (those starting with '--') and combining them with the new theme styles.
Applied to files:
web/src/store/theme.ts
📚 Learning: 2025-06-13T17:14:21.739Z
Learnt from: pujitm
Repo: unraid/api PR: 1417
File: web/components/ConnectSettings/ConnectSettings.ce.vue:11-18
Timestamp: 2025-06-13T17:14:21.739Z
Learning: The project’s build tooling auto-imports common Vue/Pinia helpers such as `storeToRefs`, so explicit import statements for them are not required.
Applied to files:
web/src/store/theme.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Nuxt is set to auto import so some Vue files may need `computed` or `ref` imported explicitly in store files
Applied to files:
web/src/store/theme.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Use `createTestingPinia()` for mocking stores in Vue components
Applied to files:
web/src/store/theme.tsweb/__test__/store/theme.test.ts
📚 Learning: 2025-03-27T23:52:57.888Z
Learnt from: zackspear
Repo: unraid/api PR: 0
File: :0-0
Timestamp: 2025-03-27T23:52:57.888Z
Learning: In the unraid/api project, Vue components are compiled into web components. Using `setActivePinia(createPinia())` in store files ensures that all web components share a single Pinia store instance, which is the desired behavior. Without this initialization, each web component would have its own isolated store, breaking the intended architecture.
Applied to files:
web/src/store/theme.ts
📚 Learning: 2025-03-27T23:52:57.888Z
Learnt from: zackspear
Repo: unraid/api PR: 0
File: :0-0
Timestamp: 2025-03-27T23:52:57.888Z
Learning: In the unraid/api project, Vue components are compiled into web components. The `setActivePinia(createPinia())` call at the module level in store files is intentional and ensures all web components share a single Pinia store instance, which is the desired behavior. This shared state approach is critical for the application's architecture to function correctly.
Applied to files:
web/src/store/theme.ts
📚 Learning: 2025-03-27T23:33:13.215Z
Learnt from: zackspear
Repo: unraid/api PR: 0
File: :0-0
Timestamp: 2025-03-27T23:33:13.215Z
Learning: In the unraid/api project, Vue components are compiled into web components. Using `setActivePinia(createPinia())` in store files would break the build by causing all web components to share a singular Pinia store instance. Each web component needs its own Pinia store instance to maintain proper isolation and encapsulation.
Applied to files:
web/src/store/theme.ts
📚 Learning: 2025-03-27T23:33:13.215Z
Learnt from: zackspear
Repo: unraid/api PR: 0
File: :0-0
Timestamp: 2025-03-27T23:33:13.215Z
Learning: In the unraid/api project, Vue components are compiled into web components. Using `setActivePinia(createPinia())` in store files would break the build by causing all web components to share a singular Pinia store instance. Each web component needs its own Pinia store instance to function correctly.
Applied to files:
web/src/store/theme.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/store/**/*.ts : Ensure Vue reactivity imports like `computed`, `ref`, and `watchEffect` are added to store files even with Nuxt auto-imports enabled
Applied to files:
web/src/store/theme.ts
📚 Learning: 2024-12-09T15:45:46.492Z
Learnt from: pujitm
Repo: unraid/api PR: 975
File: web/components/Notifications/TabList.vue:1-4
Timestamp: 2024-12-09T15:45:46.492Z
Learning: In our Nuxt.js setup for the `web` project, it's not necessary to explicitly import `computed` from `vue` in Vue components, as it's globally available.
Applied to files:
web/src/store/theme.ts
📚 Learning: 2025-11-24T17:51:37.915Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:51:37.915Z
Learning: Applies to web/src/**/*.ts : Ensure Vue reactivity imports are added to store files (computed, ref, watchEffect)
Applied to files:
web/src/store/theme.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Mock external dependencies appropriately in Pinia store tests
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Test Pinia store getter dependencies are properly mocked
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Use factory functions for module mocks in Pinia store tests to avoid hoisting issues
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Place all mock declarations at the top level in Pinia store tests
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Do not mock the store being tested in the test file when using `createPinia()`
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Mock external dependencies and services in Vue component tests
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Test component behavior and output, not implementation details
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Test Pinia store interactions with other stores
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Test Pinia action side effects and state changes
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:51:37.915Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:51:37.915Z
Learning: Applies to web/__test__/**/*.test.{ts,tsx} : Test component behavior and output, not implementation details
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-02-20T15:52:58.297Z
Learnt from: elibosley
Repo: unraid/api PR: 1155
File: web/store/theme.ts:161-172
Timestamp: 2025-02-20T15:52:58.297Z
Learning: The banner gradient implementation in web/store/theme.ts doesn't require explicit error handling for hexToRgba as CSS gracefully handles invalid values by ignoring them.
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Test computed properties in Pinia stores by accessing them directly
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Verify state changes after Pinia store actions
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-02-20T15:52:56.733Z
Learnt from: elibosley
Repo: unraid/api PR: 1155
File: web/store/theme.ts:49-50
Timestamp: 2025-02-20T15:52:56.733Z
Learning: CSS variable names in the theme store should be concise and follow established patterns. For example, prefer '--gradient-start' over '--color-customgradient-start' to maintain consistency with other variable names.
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/*.test.ts : Specify return values for component methods with `vi.spyOn()`
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/*.test.ts : Reset mocks between tests with `vi.clearAllMocks()`
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/*.test.ts : Use `vi.mock()` for module-level mocks
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Verify element existence with `expect(element.exists()).toBe(true)`
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Set initial state for focused Pinia store testing
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Verify that the expected elements are rendered in Vue components
Applied to files:
web/__test__/store/theme.test.ts
🪛 GitHub Check: Test API
web/src/store/theme.ts
[failure] 117-117: test/components/ColorSwitcher.test.ts > ColorSwitcher > enables gradient automatically when banner is enabled
Error: [vitest] No "useLazyQuery" export is defined on the "@vue/apollo-composable" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "importOriginal" helper inside:
vi.mock(import("@vue/apollo-composable"), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
// your mocked methods
}
})
❯ src/store/theme.ts:117:39
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:98
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:88
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:54
❯ fallbackRunWithContext ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1156:40
❯ createSetupStore ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:24
[failure] 117-117: test/components/ColorSwitcher.test.ts > ColorSwitcher > updates theme store when switches change
Error: [vitest] No "useLazyQuery" export is defined on the "@vue/apollo-composable" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "importOriginal" helper inside:
vi.mock(import("@vue/apollo-composable"), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
// your mocked methods
}
})
❯ src/store/theme.ts:117:39
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:98
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:88
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:54
❯ fallbackRunWithContext ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1156:40
❯ createSetupStore ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:24
[failure] 117-117: test/components/ColorSwitcher.test.ts > ColorSwitcher > updates theme store when color inputs change
Error: [vitest] No "useLazyQuery" export is defined on the "@vue/apollo-composable" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "importOriginal" helper inside:
vi.mock(import("@vue/apollo-composable"), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
// your mocked methods
}
})
❯ src/store/theme.ts:117:39
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:98
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:88
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:54
❯ fallbackRunWithContext ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1156:40
❯ createSetupStore ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:24
[failure] 117-117: test/components/ColorSwitcher.test.ts > ColorSwitcher > updates theme store when theme selection changes
Error: [vitest] No "useLazyQuery" export is defined on the "@vue/apollo-composable" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "importOriginal" helper inside:
vi.mock(import("@vue/apollo-composable"), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
// your mocked methods
}
})
❯ src/store/theme.ts:117:39
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:98
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:88
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:54
❯ fallbackRunWithContext ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1156:40
❯ createSetupStore ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:24
[failure] 117-117: test/components/ColorSwitcher.test.ts > ColorSwitcher > renders all form elements correctly
Error: [vitest] No "useLazyQuery" export is defined on the "@vue/apollo-composable" mock. Did you forget to return it from "vi.mock"?
If you need to partially mock a module, you can use "importOriginal" helper inside:
vi.mock(import("@vue/apollo-composable"), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
// your mocked methods
}
})
❯ src/store/theme.ts:117:39
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:98
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:88
❯ EffectScope.run ../node_modules/.pnpm/@VUE[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:85:16
❯ ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:54
❯ fallbackRunWithContext ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1156:40
❯ createSetupStore ../node_modules/.pnpm/[email protected][email protected][email protected][email protected]_/node_modules/pinia/dist/pinia.mjs:1464:24
🔇 Additional comments (7)
web/src/store/theme.ts (6)
41-85: Well-structured helper functions.The DOM availability checks and CSS variable reading helpers are well-organized. The
syncDarkClassfunction correctly propagates the dark class todocumentElement,body, and all.unapielements, ensuring consistency across the application.
113-114: Good initialization pattern.Calling
bootstrapDarkClass()during store initialization ensures that dark mode is properly applied based on CSS variables or existing DOM state when the store is created.
158-168: CSS-variable-driven getters align with PR objectives.The
themeNameanddarkModecomputed properties correctly read from CSS variables set by PHP (--theme-nameand--theme-dark-mode), with appropriate fallbacks. This ensures consistent theme state between server-rendered HTML and client-side JavaScript.
180-196: Banner gradient logic correctly respects dark mode.The implementation correctly returns
undefinedwhendarkModeis active, aligning with the learned behavior that banners should not show gradients in dark mode. The CSS variable reading includes proper normalization to handle empty, null, or undefined values.Based on learnings, the header design intentionally differs between light and dark modes.
234-241: Watch withimmediate: falseprevents initialization conflict.Setting
immediate: falsecorrectly prevents the watcher from overriding the dark class applied bybootstrapDarkClass()during initialization. The watcher now only applies the dark class when the theme name actually changes (e.g., via dev tools), which is the intended behavior per the comment.
254-257: Computed theme wrapper ensures CSS variable takes precedence.Returning a computed object that overrides
namewiththemeName.valueensures the theme name always reflects the CSS variable set by PHP, even if the internalthemeref has a different value. This maintains the CSS variable as the source of truth for theme name.web/__test__/store/theme.test.ts (1)
100-294: Comprehensive test coverage for CSS-variable-driven theme.The new tests effectively validate the CSS-variable-driven theme initialization and computed properties:
- Dark mode detection from
--theme-dark-mode(both '1' and '0' values)- Banner gradient reading from
--banner-gradient- Proper handling of missing/empty CSS variables
- Bootstrap dark class application on store creation
The mocking strategy using
getComputedStyleis appropriate and aligns with the implementation.
|
🚀 Storybook has been deployed to staging: https://unraid-ui-storybook-staging.unraid-workers.workers.dev |
2 similar comments
|
🚀 Storybook has been deployed to staging: https://unraid-ui-storybook-staging.unraid-workers.workers.dev |
|
🚀 Storybook has been deployed to staging: https://unraid-ui-storybook-staging.unraid-workers.workers.dev |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
web/__test__/store/theme.test.ts (1)
106-128: Consider extracting repeatedgetComputedStylemock to a helper.The mock pattern for
getComputedStyleis repeated across multiple tests with slight variations. A helper function could reduce duplication:const mockCssVariables = (variables: Record<string, string>) => { const originalGetComputedStyle = window.getComputedStyle; vi.spyOn(window, 'getComputedStyle').mockImplementation((el) => { const style = originalGetComputedStyle(el); if (el === document.documentElement) { return { ...style, getPropertyValue: (prop: string) => variables[prop] ?? style.getPropertyValue(prop), } as CSSStyleDeclaration; } return style; }); };Usage:
mockCssVariables({ '--theme-dark-mode': '1' })
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
web/__test__/components/ColorSwitcher.test.ts(3 hunks)web/__test__/components/UserProfile.test.ts(3 hunks)web/__test__/store/theme.test.ts(4 hunks)
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,js,jsx}: Always use TypeScript imports with.jsextensions for ESM compatibility
Never add comments unless they are needed for clarity of function
Never add comments for obvious things, and avoid commenting when starting and ending code blocks
Files:
web/__test__/store/theme.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
**/*.test.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.test.{ts,tsx,js,jsx}: Use VITEST for test suite, not jest
Use.rejects.toThrow()without arguments to test that functions throw errors, not exact error message strings
Files:
web/__test__/store/theme.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
web/**/*
📄 CodeRabbit inference engine (CLAUDE.md)
Always run
pnpm codegenfor GraphQL code generation in the web directory
Files:
web/__test__/store/theme.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
web/__test__/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/__test__/**/*.test.{ts,tsx}: Test component behavior and output, not implementation details
Usemountfrom Vue Test Utils for component testing
Stub complex child components that aren't the focus of the test
Find elements with semantic queries likefind('button')rather than data-test IDs
UsecreatePinia()andsetActivePiniawhen testing Store files, only usecreateTestingPiniaif you specifically need its testing features
Files:
web/__test__/store/theme.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Never use theanytype. Always prefer proper typing
Avoid using casting whenever possible, prefer proper typing from the start
Files:
web/__test__/store/theme.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
**/*
📄 CodeRabbit inference engine (.cursor/rules/default.mdc)
Never add comments unless they are needed for clarity of function
Files:
web/__test__/store/theme.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
**/*.test.ts
📄 CodeRabbit inference engine (.cursor/rules/web-testing-rules.mdc)
**/*.test.ts: Use.rejects.toThrow()without arguments to test that functions throw errors. Don't test exact error message strings unless the message format is specifically what you're testing
Test what the code does, not implementation details like exact error message wording
Mock external services and API calls
Usevi.mock()for module-level mocks
Specify return values for component methods withvi.spyOn()
Reset mocks between tests withvi.clearAllMocks()
Always await async operations before making assertions
Files:
web/__test__/store/theme.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
**/__test__/store/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/web-testing-rules.mdc)
**/__test__/store/**/*.ts: UsecreatePiniaandsetActivePiniawhen testing Pinia store files
UsecreatePinia()instead ofcreateTestingPinia()for most Pinia store tests
Only usecreateTestingPiniaif you specifically need its testing features for Pinia stores
Let stores initialize with their natural default state instead of forcing initial state
Do not mock the store being tested in the test file when usingcreatePinia()
Place all mock declarations at the top level in Pinia store tests
Use factory functions for module mocks in Pinia store tests to avoid hoisting issues
Test Pinia action side effects and state changes
Verify Pinia actions are called with correct parameters
Mock external dependencies appropriately in Pinia store tests
Test computed properties in Pinia stores by accessing them directly
Verify state changes after Pinia store actions
Test Pinia store getter dependencies are properly mocked
Test Pinia store interactions with other stores
Verify proper error handling in Pinia store tests
Test async operations completely in Pinia store tests
Override specific Pinia action implementations when needed in tests
Set initial state for focused Pinia store testing
Files:
web/__test__/store/theme.test.ts
**/store/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/web-testing-rules.mdc)
Ensure Vue reactivity imports like
computed,ref, andwatchEffectare added to store files even with Nuxt auto-imports enabled
Files:
web/__test__/store/theme.test.ts
**/__test__/components/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/web-testing-rules.mdc)
**/__test__/components/**/*.ts: Usemountfrom Vue Test Utils for component testing
Stub complex child components that aren't the focus of the test
Mock external dependencies and services in Vue component tests
Test component behavior and output, not implementation details
Verify that the expected elements are rendered in Vue components
Test component interactions such as clicks and inputs
Check for expected prop handling and event emissions in Vue components
UsecreateTestingPinia()for mocking stores in Vue components
Use semantic queries likefind('button')orfind('[data-test="id"]')but prefer not to use data test ID's when finding elements
Find Vue components withfindComponent(ComponentName)
UsefindAllto check for multiple elements in Vue components
Assert on rendered text content withwrapper.text()
Assert on element attributes withelement.attributes()
Verify element existence withexpect(element.exists()).toBe(true)
Check component state through rendered output
Trigger events withawait element.trigger('click')
Set input values withawait input.setValue('value')
Test emitted events withwrapper.emitted()
Useawait nextTick()for DOM updates in Vue component tests
UseflushPromises()for complex promise chains in Vue component tests
Files:
web/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
🧠 Learnings (37)
📓 Common learnings
Learnt from: elibosley
Repo: unraid/api PR: 972
File: web/store/theme.ts:46-49
Timestamp: 2024-12-17T14:59:32.458Z
Learning: In the `web/store/theme.ts` file of the Unraid web application, the header is intentionally designed to have a light background with dark text in dark mode, and a dark background with light text in light mode.
Learnt from: elibosley
Repo: unraid/api PR: 1155
File: web/store/theme.ts:49-50
Timestamp: 2025-02-20T15:52:56.733Z
Learning: CSS variable names in the theme store should be concise and follow established patterns. For example, prefer '--gradient-start' over '--color-customgradient-start' to maintain consistency with other variable names.
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Mock external dependencies appropriately in Pinia store tests
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Use factory functions for module mocks in Pinia store tests to avoid hoisting issues
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Test Pinia store getter dependencies are properly mocked
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Place all mock declarations at the top level in Pinia store tests
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Do not mock the store being tested in the test file when using `createPinia()`
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Test Pinia store interactions with other stores
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/UserProfile.test.ts
📚 Learning: 2025-11-24T17:51:37.915Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:51:37.915Z
Learning: Applies to web/__test__/**/*.test.{ts,tsx} : Use `createPinia()` and `setActivePinia` when testing Store files, only use `createTestingPinia` if you specifically need its testing features
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Mock external dependencies and services in Vue component tests
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Test Pinia action side effects and state changes
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/UserProfile.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Test component behavior and output, not implementation details
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-02-20T15:52:58.297Z
Learnt from: elibosley
Repo: unraid/api PR: 1155
File: web/store/theme.ts:161-172
Timestamp: 2025-02-20T15:52:58.297Z
Learning: The banner gradient implementation in web/store/theme.ts doesn't require explicit error handling for hexToRgba as CSS gracefully handles invalid values by ignoring them.
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/UserProfile.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Verify proper error handling in Pinia store tests
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Test computed properties in Pinia stores by accessing them directly
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Verify state changes after Pinia store actions
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/UserProfile.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Use `await nextTick()` for DOM updates in Vue component tests
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/*.test.ts : Use `vi.mock()` for module-level mocks
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Use `createTestingPinia()` for mocking stores in Vue components
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Check for expected prop handling and event emissions in Vue components
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Verify that the expected elements are rendered in Vue components
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:51:37.915Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:51:37.915Z
Learning: Applies to web/src/**/*.ts : Ensure Vue reactivity imports are added to store files (computed, ref, watchEffect)
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:51:37.915Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:51:37.915Z
Learning: Applies to web/__test__/**/*.test.{ts,tsx} : Test component behavior and output, not implementation details
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-02-20T15:52:56.733Z
Learnt from: elibosley
Repo: unraid/api PR: 1155
File: web/store/theme.ts:49-50
Timestamp: 2025-02-20T15:52:56.733Z
Learning: CSS variable names in the theme store should be concise and follow established patterns. For example, prefer '--gradient-start' over '--color-customgradient-start' to maintain consistency with other variable names.
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/*.test.ts : Specify return values for component methods with `vi.spyOn()`
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/*.test.ts : Reset mocks between tests with `vi.clearAllMocks()`
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Verify element existence with `expect(element.exists()).toBe(true)`
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Set initial state for focused Pinia store testing
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Use `createPinia` and `setActivePinia` when testing Pinia store files
Applied to files:
web/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Use `createPinia()` instead of `createTestingPinia()` for most Pinia store tests
Applied to files:
web/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Override specific Pinia action implementations when needed in tests
Applied to files:
web/__test__/components/UserProfile.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Assert on rendered text content with `wrapper.text()`
Applied to files:
web/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Check component state through rendered output
Applied to files:
web/__test__/components/UserProfile.test.ts
📚 Learning: 2025-11-24T17:51:37.915Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:51:37.915Z
Learning: Applies to web/__test__/**/*.test.{ts,tsx} : Use `mount` from Vue Test Utils for component testing
Applied to files:
web/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Use `mount` from Vue Test Utils for component testing
Applied to files:
web/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Test component interactions such as clicks and inputs
Applied to files:
web/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-08-09T01:03:29.676Z
Learnt from: elibosley
Repo: unraid/api PR: 1575
File: packages/unraid-shared/src/services/socket-config.service.spec.ts:10-13
Timestamp: 2025-08-09T01:03:29.676Z
Learning: Vitest is used for all testing across all repositories in the unraid organization, not Jest. Always use `vi` for mocking utilities, not `jest`.
Applied to files:
web/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Only use `createTestingPinia` if you specifically need its testing features for Pinia stores
Applied to files:
web/__test__/components/ColorSwitcher.test.ts
🧬 Code graph analysis (2)
web/__test__/store/theme.test.ts (1)
api/src/store/index.ts (1)
store(10-16)
web/__test__/components/UserProfile.test.ts (1)
web/src/store/theme.ts (1)
useThemeStore(105-263)
🔇 Additional comments (12)
web/__test__/components/UserProfile.test.ts (3)
191-210: LGTM! Well-structured theme initialization.The CSS variable setup and
setThemeinitialization properly align with the new CSS-variable-driven theme resolution. This approach ensures tests reflect production behavior where theme state is derived from CSS variables set by PHP.
336-357: Clean refactor to use public API.The spread pattern
{ ...themeStore.theme, descriptionShow: ... }correctly preserves existing theme state while updating specific properties, replacing direct state mutations with propersetThemecalls.
368-398: LGTM!Banner tests correctly use
setThemeto toggle flags and verifybannerGradientcomputed behavior with CSS variables.web/__test__/components/ColorSwitcher.test.ts (3)
25-31: Good addition ofuseLazyQuerymock.This addresses the missing mock issue flagged in past reviews. The mock shape correctly mirrors the
useLazyQueryreturn type withload,result,loading,onResult, andonError.
64-67: CSS variable setup enables proper theme store initialization.Setting
--theme-dark-modeand--banner-gradientbefore creating the store ensures the CSS-variable-driven computed properties initialize correctly during tests.
84-89: Improved cleanup robustness.The guards prevent potential errors during teardown when
modalDivis already removed orconsoleWarnSpywasn't created.web/__test__/store/theme.test.ts (6)
21-27:useLazyQuerymock added correctly.This aligns with the mock added in
ColorSwitcher.test.tsand addresses the past review comment about consistency across test files.
65-71: Defensive cleanup pattern.The try-catch handles the case where the app was never mounted, preventing test failures during teardown.
106-152: Thorough CSS variable-based darkMode tests.These tests properly verify the
darkModecomputed property reads from--theme-dark-modeCSS variable and returns the correct boolean value.
154-186: Good test for bannerGradient with CSS variable.The test correctly sets
banner: trueandbannerGradient: trueviasetThemebefore asserting the computed value, addressing the issue flagged in past reviews.
188-210: Correctly tests undefined gradient when CSS variable is empty.This validates the edge case where
--banner-gradientis not set or empty.
279-308: LGTM! Validates dark mode initialization from CSS variable.This test confirms that
bootstrapDarkClasscorrectly applies the dark class todocumentElementandbodywhen the CSS variable--theme-dark-modeis set to'1'at store creation time.
|
🚀 Storybook has been deployed to staging: https://unraid-ui-storybook-staging.unraid-workers.workers.dev |
There was a problem hiding this 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 (8)
web/src/components/DevThemeSwitcher.standalone.vue (3)
66-77: Remove unnecessaryasynckeyword.This function doesn't contain any
awaitexpressions, so theasyncmodifier is unnecessary overhead.-const resolveInitialTheme = async (): Promise<ThemeName> => { +const resolveInitialTheme = (): ThemeName => { const candidates = [readCssTheme(), readCookieTheme(), readLocalStorageTheme(), theme.value?.name];If this is intentionally async for future extensibility, consider adding a comment to clarify.
96-102: Redundant watch for query loading state.The
queryLoadingref fromuseQuerycan be used directly instead of copying it to another ref via a watcher.Remove the redundant watch and
isQueryLoadingref:-const isQueryLoading = ref(false); ... -watch( - () => queryLoading.value, - (loading) => { - isQueryLoading.value = loading; - }, - { immediate: true } -);Then in the template, use
queryLoadingdirectly:- :disabled="isSaving || isQueryLoading" + :disabled="isSaving || queryLoading"
191-216: Clarify thepersistparameter semantics.The
persistparameter name is misleading—local persistence to cookie/localStorage always happens (line 198), whilepersistonly controls the GraphQL server mutation.Consider renaming for clarity:
const applyThemeSelection = async ( themeName: string | null | undefined, - { persist = false, skipStore = false }: { persist?: boolean; skipStore?: boolean } = {} + { persistToServer = false, skipStore = false }: { persistToServer?: boolean; skipStore?: boolean } = {} ) => { // ... - if (persist) { + if (persistToServer) {web/test-pages/partials/styles.njk (1)
2-5: Dev theme bootstrap matches CSS-var/dark-mode flow; confirm azure dark-mode mappingThe dev-only logic looks good overall:
activeThemeprecedence (resolvedTheme→query.theme→'white') and the explicit allowlist guard prevent invalid theme names from leaking into CSS vars.- The script keeps the
<link id="dev-theme-css-link">href in sync with the normalized theme.--theme-name,--theme-dark-mode(as'1'/'0'), anddata-themeondocumentElementalign with how the theme store expects to bootstrap state. Based on learnings, this should integrate cleanly with existing CSS-variable-driven theming.syncDarkClassupdatinghtml,body, and.unapimirrors how the app propagates thedarkclass to containers.Two small follow-ups:
isDarkonly treats'black'and'gray'as dark:var isDark = theme === 'black' || theme === 'gray';Please double-check whether
azureshould also be considered dark for your design system. If yes, add it here so--theme-dark-modeand thedarkclass stay consistent.The theme allowlist,
themeCssMapkeys, andisDarkpredicate all repeat the same set of theme names. If you find yourself tweaking themes often, consider centralizing this into a single map/object and deriving the allowlist andisDarkfrom it to avoid drift.Also applies to: 26-66
web/vite-plugin-serve-static.ts (4)
10-39: TightennormalizeThemetyping to avoid unnecessary castThe runtime logic is fine, but casting
(theme ?? '').toLowerCase()directly toThemeNameis a bit loose for TS and goes against the “exact theme types, minimal casting” pattern used elsewhere in the theme system (see previous learnings). You can keep the same behavior while letting TypeScript infer the union fromALLOWED_THEMES:-const normalizeTheme = (theme?: string | null): ThemeName => { - const normalized = (theme ?? '').toLowerCase() as ThemeName; - return ALLOWED_THEMES.includes(normalized) ? normalized : 'white'; -}; +const normalizeTheme = (theme?: string | null): ThemeName => { + const normalized = (theme ?? '').toLowerCase(); + return ALLOWED_THEMES.find((t) => t === normalized) ?? 'white'; +};This preserves guarantees that only whitelisted values flow out, without relying on a broad cast. Based on learnings, this matches the preference for precise theme typings.
40-58: HardenparseCookiesagainst malformed encodingsThe implementation is solid for normal cookies, but
decodeURIComponentwill throw if a cookie value contains invalid percent-encoding, which would currently take down the dev request.If you want this helper to be more defensive (even in dev), consider wrapping the decode in a try/catch and skipping values that can’t be decoded:
- acc[trimmedName] = decodeURIComponent(rest.join('=').trim()); + const rawValue = rest.join('=').trim(); + try { + acc[trimmedName] = decodeURIComponent(rawValue); + } catch { + // Ignore malformed cookie values + }That way a single bad cookie header doesn’t break
/test-pagesrendering.
70-109: INI parsing and theme read behavior are minimal but adequate
parseIniSectionis intentionally minimal (single-linekey=value, no multi-line or advanced INI features) andreadThemeFromConfiggracefully falls back tonullon any error, which is appropriate for a dev helper.One behavioral note: if
[display]exists but has notheme,normalizeTheme(displaySection.theme)effectively defaults to'white'. That’s probably fine for dev, but it does mean a misconfigured file silently falls back rather than surfacing an error. If you ever want stricter behavior, you could explicitly check forObject.hasOwn(displaySection, 'theme')before normalizing.
307-318: Theme resolution precedence is clear; confirm it matches intended UXThe resolution chain:
const cookies = parseCookies(req.headers.cookie); const cookieTheme = cookies['unraid_dev_theme']; const queryTheme = requestUrl.searchParams.get('theme'); const cfgTheme = readThemeFromConfig(); const resolvedTheme = normalizeTheme(cfgTheme || queryTheme || cookieTheme);gives precedence
config > query param > cookie, then defaults vianormalizeTheme. That’s a reasonable choice for dev, but it does mean a remembered config will override ad‑hoc query testing unless config is cleared/changed.If that’s the intended behavior for local testing, this looks good. If you expect query params to always win during manual testing, you may want to reorder to
query > cookie > config.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
api/dev/configs/api.json(1 hunks)api/generated-schema.graphql(2 hunks)api/src/unraid-api/graph/resolvers/customization/customization.module.ts(1 hunks)api/src/unraid-api/graph/resolvers/customization/customization.mutations.resolver.ts(1 hunks)api/src/unraid-api/graph/resolvers/customization/customization.service.ts(2 hunks)api/src/unraid-api/graph/resolvers/mutation/mutation.model.ts(2 hunks)api/src/unraid-api/graph/resolvers/mutation/mutation.resolver.ts(2 hunks)web/.prettierignore(1 hunks)web/src/components/DevThemeSwitcher.standalone.vue(3 hunks)web/test-pages/partials/styles.njk(2 hunks)web/vite-plugin-serve-static.ts(4 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
api/**/*
📄 CodeRabbit inference engine (CLAUDE.md)
Prefer adding new files to the NestJS repo located at
api/src/unraid-api/instead of the legacy code
Files:
api/dev/configs/api.jsonapi/src/unraid-api/graph/resolvers/mutation/mutation.model.tsapi/src/unraid-api/graph/resolvers/customization/customization.mutations.resolver.tsapi/src/unraid-api/graph/resolvers/customization/customization.service.tsapi/src/unraid-api/graph/resolvers/mutation/mutation.resolver.tsapi/src/unraid-api/graph/resolvers/customization/customization.module.tsapi/generated-schema.graphql
**/*
📄 CodeRabbit inference engine (.cursor/rules/default.mdc)
Never add comments unless they are needed for clarity of function
Files:
api/dev/configs/api.jsonapi/src/unraid-api/graph/resolvers/mutation/mutation.model.tsapi/src/unraid-api/graph/resolvers/customization/customization.mutations.resolver.tsapi/src/unraid-api/graph/resolvers/customization/customization.service.tsapi/src/unraid-api/graph/resolvers/mutation/mutation.resolver.tsapi/src/unraid-api/graph/resolvers/customization/customization.module.tsweb/src/components/DevThemeSwitcher.standalone.vueweb/vite-plugin-serve-static.tsweb/test-pages/partials/styles.njkapi/generated-schema.graphql
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,js,jsx}: Always use TypeScript imports with.jsextensions for ESM compatibility
Never add comments unless they are needed for clarity of function
Never add comments for obvious things, and avoid commenting when starting and ending code blocks
Files:
api/src/unraid-api/graph/resolvers/mutation/mutation.model.tsapi/src/unraid-api/graph/resolvers/customization/customization.mutations.resolver.tsapi/src/unraid-api/graph/resolvers/customization/customization.service.tsapi/src/unraid-api/graph/resolvers/mutation/mutation.resolver.tsapi/src/unraid-api/graph/resolvers/customization/customization.module.tsweb/vite-plugin-serve-static.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Never use theanytype. Always prefer proper typing
Avoid using casting whenever possible, prefer proper typing from the start
Files:
api/src/unraid-api/graph/resolvers/mutation/mutation.model.tsapi/src/unraid-api/graph/resolvers/customization/customization.mutations.resolver.tsapi/src/unraid-api/graph/resolvers/customization/customization.service.tsapi/src/unraid-api/graph/resolvers/mutation/mutation.resolver.tsapi/src/unraid-api/graph/resolvers/customization/customization.module.tsweb/vite-plugin-serve-static.ts
api/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
cache-manager v7 expects TTL values in milliseconds, not seconds (e.g., 600000 for 10 minutes, not 600)
Files:
api/src/unraid-api/graph/resolvers/mutation/mutation.model.tsapi/src/unraid-api/graph/resolvers/customization/customization.mutations.resolver.tsapi/src/unraid-api/graph/resolvers/customization/customization.service.tsapi/src/unraid-api/graph/resolvers/mutation/mutation.resolver.tsapi/src/unraid-api/graph/resolvers/customization/customization.module.ts
web/**/*
📄 CodeRabbit inference engine (CLAUDE.md)
Always run
pnpm codegenfor GraphQL code generation in the web directory
Files:
web/src/components/DevThemeSwitcher.standalone.vueweb/vite-plugin-serve-static.tsweb/test-pages/partials/styles.njk
🧠 Learnings (18)
📓 Common learnings
Learnt from: elibosley
Repo: unraid/api PR: 972
File: web/store/theme.ts:46-49
Timestamp: 2024-12-17T14:59:32.458Z
Learning: In the `web/store/theme.ts` file of the Unraid web application, the header is intentionally designed to have a light background with dark text in dark mode, and a dark background with light text in light mode.
Learnt from: elibosley
Repo: unraid/api PR: 1155
File: web/store/theme.ts:49-50
Timestamp: 2025-02-20T15:52:56.733Z
Learning: CSS variable names in the theme store should be concise and follow established patterns. For example, prefer '--gradient-start' over '--color-customgradient-start' to maintain consistency with other variable names.
Learnt from: elibosley
Repo: unraid/api PR: 1181
File: web/store/theme.ts:210-216
Timestamp: 2025-02-21T18:40:10.810Z
Learning: When updating theme-related CSS variables via `cssText`, preserve existing non-theme styles by filtering out only theme-related rules (those starting with '--') and combining them with the new theme styles.
Learnt from: elibosley
Repo: unraid/api PR: 1181
File: web/store/theme.ts:0-0
Timestamp: 2025-02-24T14:51:21.328Z
Learning: In the Unraid API project's theme system, exact TypeScript type definitions are preferred over index signatures for theme variables to ensure better type safety.
📚 Learning: 2025-11-24T17:51:37.915Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:51:37.915Z
Learning: Applies to web/src/**/*.ts : Ensure Vue reactivity imports are added to store files (computed, ref, watchEffect)
Applied to files:
web/.prettierignore
📚 Learning: 2025-11-24T17:51:37.915Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:51:37.915Z
Learning: Applies to web/**/* : Always run `pnpm codegen` for GraphQL code generation in the web directory
Applied to files:
web/.prettierignore
📚 Learning: 2025-11-24T17:51:37.915Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:51:37.915Z
Learning: Applies to web/**/*.query.ts : GraphQL queries must be placed in `.query.ts` files
Applied to files:
web/.prettierignore
📚 Learning: 2025-05-23T21:59:29.632Z
Learnt from: elibosley
Repo: unraid/api PR: 1408
File: web/components/ApiKey/PermissionCounter.vue:6-6
Timestamp: 2025-05-23T21:59:29.632Z
Learning: This codebase uses ESM (ECMAScript Modules) and requires .js extensions in import statements, even when importing from TypeScript files, as the imports refer to the compiled JavaScript output.
Applied to files:
web/.prettierignore
📚 Learning: 2025-11-24T17:52:00.572Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-graphql.mdc:0-0
Timestamp: 2025-11-24T17:52:00.572Z
Learning: Applies to web/**/*.{query.ts,mutation.ts} : All GraphQL under `web/` must follow the `.query.ts` and `.mutation.ts` naming convention
Applied to files:
web/.prettierignore
📚 Learning: 2025-11-24T17:51:37.915Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:51:37.915Z
Learning: Applies to web/**/*.mutation.ts : GraphQL mutations must be placed in `.mutation.ts` files
Applied to files:
web/.prettierignoreapi/src/unraid-api/graph/resolvers/mutation/mutation.model.tsapi/src/unraid-api/graph/resolvers/mutation/mutation.resolver.ts
📚 Learning: 2025-03-12T13:48:14.850Z
Learnt from: pujitm
Repo: unraid/api PR: 1211
File: web/composables/gql/gql.ts:17-18
Timestamp: 2025-03-12T13:48:14.850Z
Learning: In the Unraid API project, the duplicate GraphQL query and mutation strings in gql.ts files are intentionally generated by GraphQL CodeGen tool and are necessary for the type system to function properly.
Applied to files:
web/.prettierignoreapi/src/unraid-api/graph/resolvers/mutation/mutation.model.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Test component behavior and output, not implementation details
Applied to files:
web/.prettierignore
📚 Learning: 2025-03-14T16:10:38.562Z
Learnt from: pujitm
Repo: unraid/api PR: 1211
File: api/src/graphql/schema/types/connect/connect.graphql:142-146
Timestamp: 2025-03-14T16:10:38.562Z
Learning: The updateApiSettings mutation in api/src/unraid-api/graph/connect/connect.resolver.ts is protected with the UsePermissions decorator that requires UPDATE permission on the CONFIG resource.
Applied to files:
api/src/unraid-api/graph/resolvers/mutation/mutation.model.tsapi/src/unraid-api/graph/resolvers/customization/customization.mutations.resolver.tsapi/src/unraid-api/graph/resolvers/mutation/mutation.resolver.ts
📚 Learning: 2024-11-06T20:59:25.809Z
Learnt from: mdatelle
Repo: unraid/api PR: 942
File: api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts:111-113
Timestamp: 2024-11-06T20:59:25.809Z
Learning: In the Unraid API project, error handling for mutations is handled at the service level rather than in the GraphQL resolvers. Specifically, in `api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts`, methods like `removeRoleFromApiKey` rely on service-level error handling.
Applied to files:
api/src/unraid-api/graph/resolvers/mutation/mutation.model.tsapi/src/unraid-api/graph/resolvers/customization/customization.mutations.resolver.tsapi/src/unraid-api/graph/resolvers/mutation/mutation.resolver.ts
📚 Learning: 2024-11-05T14:48:56.949Z
Learnt from: elibosley
Repo: unraid/api PR: 942
File: api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts:41-47
Timestamp: 2024-11-05T14:48:56.949Z
Learning: Input types for GraphQL mutations can be pulled from the GraphQL codegen types, as they are generated into the API files. Therefore, explicit typing in the resolver files may be unnecessary.
Applied to files:
api/src/unraid-api/graph/resolvers/mutation/mutation.model.tsapi/src/unraid-api/graph/resolvers/customization/customization.mutations.resolver.tsapi/src/unraid-api/graph/resolvers/mutation/mutation.resolver.ts
📚 Learning: 2024-11-06T20:59:06.096Z
Learnt from: mdatelle
Repo: unraid/api PR: 942
File: api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts:87-89
Timestamp: 2024-11-06T20:59:06.096Z
Learning: Error handling in mutations like `addRoleForUser` in `auth.resolver.ts` is performed at the service level, so additional error handling in the resolver is unnecessary.
Applied to files:
api/src/unraid-api/graph/resolvers/customization/customization.mutations.resolver.ts
📚 Learning: 2024-12-17T14:59:32.458Z
Learnt from: elibosley
Repo: unraid/api PR: 972
File: web/store/theme.ts:46-49
Timestamp: 2024-12-17T14:59:32.458Z
Learning: In the `web/store/theme.ts` file of the Unraid web application, the header is intentionally designed to have a light background with dark text in dark mode, and a dark background with light text in light mode.
Applied to files:
api/src/unraid-api/graph/resolvers/customization/customization.service.tsweb/src/components/DevThemeSwitcher.standalone.vueweb/vite-plugin-serve-static.tsweb/test-pages/partials/styles.njk
📚 Learning: 2025-04-23T20:19:42.542Z
Learnt from: pujitm
Repo: unraid/api PR: 1367
File: packages/unraid-api-plugin-connect/src/pubsub/user.service.ts:44-52
Timestamp: 2025-04-23T20:19:42.542Z
Learning: The project uses a custom or extended implementation of NestJS ConfigService that includes a `set()` method for runtime configuration mutation, unlike the standard nestjs/config package which only provides getter methods.
Applied to files:
api/src/unraid-api/graph/resolvers/customization/customization.module.ts
📚 Learning: 2025-02-24T14:51:21.328Z
Learnt from: elibosley
Repo: unraid/api PR: 1181
File: web/store/theme.ts:0-0
Timestamp: 2025-02-24T14:51:21.328Z
Learning: In the Unraid API project's theme system, exact TypeScript type definitions are preferred over index signatures for theme variables to ensure better type safety.
Applied to files:
web/src/components/DevThemeSwitcher.standalone.vueweb/vite-plugin-serve-static.ts
📚 Learning: 2025-03-27T23:52:57.888Z
Learnt from: zackspear
Repo: unraid/api PR: 0
File: :0-0
Timestamp: 2025-03-27T23:52:57.888Z
Learning: In the unraid/api project, Vue components are compiled into web components. Using `setActivePinia(createPinia())` in store files ensures that all web components share a single Pinia store instance, which is the desired behavior. Without this initialization, each web component would have its own isolated store, breaking the intended architecture.
Applied to files:
web/src/components/DevThemeSwitcher.standalone.vue
📚 Learning: 2025-02-21T18:40:10.810Z
Learnt from: elibosley
Repo: unraid/api PR: 1181
File: web/store/theme.ts:210-216
Timestamp: 2025-02-21T18:40:10.810Z
Learning: When updating theme-related CSS variables via `cssText`, preserve existing non-theme styles by filtering out only theme-related rules (those starting with '--') and combining them with the new theme styles.
Applied to files:
web/test-pages/partials/styles.njk
🧬 Code graph analysis (2)
api/src/unraid-api/graph/resolvers/customization/customization.mutations.resolver.ts (3)
api/src/unraid-api/graph/resolvers/mutation/mutation.resolver.ts (1)
Resolver(14-50)packages/unraid-shared/src/use-permissions.directive.ts (1)
UsePermissions(81-128)api/src/unraid-api/graph/resolvers/customization/customization.resolver.ts (1)
theme(58-60)
api/src/unraid-api/graph/resolvers/mutation/mutation.resolver.ts (1)
web/composables/gql/graphql.ts (1)
Mutation(1199-1239)
🔇 Additional comments (19)
web/.prettierignore (1)
5-5: Addingdist/to Prettier ignore list is sensible.The change appropriately excludes the compiled/build output directory from Prettier formatting, which is a best practice to prevent formatting of generated files. The entry follows the existing naming convention with other directory-based ignores in the file.
web/src/components/DevThemeSwitcher.standalone.vue (7)
1-27: LGTM!Good use of the generated
ThemeNameenum for type-safe theme options, aligning with the project's preference for exact TypeScript type definitions. Based on learnings, this approach ensures better type safety.
34-64: LGTM!The helper functions are SSR-safe with appropriate guards for
documentandwindow. Cookie parsing correctly handles values containing=by usingrest.join('=').
142-150: LGTM!Cookie persistence with reasonable expiration and
SameSite=Lax. The omission of theSecureflag is acceptable for a dev-only component that may run on localhost.
152-163: LGTM!The CSS variable names
--theme-nameand--theme-dark-modefollow the concise naming pattern as per established conventions. The dark class synchronization across root, body, and.unapielements ensures consistent theming. Based on learnings, this aligns with the project's approach to CSS variable naming.
227-244: LGTM!The lifecycle management correctly:
- Sets dev override on mount before applying theme
- Watches store changes with
skipStore: trueto prevent circular updates- Separates user-initiated changes (with server persistence) from external sync
247-286: LGTM!Template correctly binds to
currentThemeand disables the select during saving/loading operations. The scoped styles appropriately handle the disabled state.
29-32: No case mismatch exists — review comment is incorrect.The
ThemeNameenum values are lowercase strings ('azure','black','gray','white'), not uppercase. On line 27,themeValuesis populated fromthemeOptions.map((option) => option.value), where eachoption.valueis aThemeNameenum member with a lowercase string value. ThenormalizeThemefunction correctly converts input to lowercase and checks membership againstthemeValues, which contains lowercase strings. The logic functions as intended—no fix is needed.api/dev/configs/api.json (1)
2-2: Version bump looks consistentOnly the dev API config version was updated; structure and other fields are unchanged. As long as
4.27.2matches the API build you’re shipping, this is good to go.api/src/unraid-api/graph/resolvers/mutation/mutation.model.ts (1)
27-30: CustomizationMutations container wiring matches existing mutation patternDefining
CustomizationMutationsand exposing it viaRootMutations.customizationmirrors the existing nested mutation containers (array/docker/vm/apiKey/parityCheck/rclone). Naming and descriptions are consistent.Also applies to: 62-63
api/src/unraid-api/graph/resolvers/customization/customization.mutations.resolver.ts (1)
1-25: setTheme mutation resolver is well-scoped and permissionedThe resolver cleanly delegates to
CustomizationService.setTheme, usesThemeNameas the argument type and returnsTheme, and appliesUsePermissions({ action: AuthAction.UPDATE_ANY, resource: Resource.CUSTOMIZATIONS })in line with existing mutation patterns. No additional error handling is needed here since it’s handled in the service.api/src/unraid-api/graph/resolvers/mutation/mutation.resolver.ts (1)
6-6: RootMutations.customization entry correctly exposes customization mutationsThe new
@Mutation(() => CustomizationMutations, { name: 'customization' })method matches the existing container-style mutations and cleanly returns a newCustomizationMutationsinstance. Import updates are correct.Also applies to: 41-44
api/src/unraid-api/graph/resolvers/customization/customization.module.ts (1)
3-3: CustomizationModule now cleanly wires mutations resolver and exports the serviceAdding
CustomizationMutationsResolvertoprovidersand exportingCustomizationServicealigns this module with how other resolver/service modules are structured. No issues.Also applies to: 8-9
api/generated-schema.graphql (1)
947-963: Schema additions for customization/theming align with resolvers; ensure dependent codegen is updatedThe generated SDL updates look internally consistent:
type CustomizationMutationswithsetTheme(theme: ThemeName!): Theme!matchesCustomizationMutationsResolver.setTheme.enum ThemeName { azure black gray white }aligns with the service logic that mapsdynamix.display.themetoThemeName.Mutation.customization: CustomizationMutations!matches the newRootMutationsResolver.customization()entry.Since this file is generated, the main follow-up is to ensure all dependent codegen is refreshed (e.g., web GraphQL TypeScript types via
pnpm codegeninweb/) so clients seeThemeName,CustomizationMutations, and thecustomizationmutation root.Also applies to: 2461-2461
api/src/unraid-api/graph/resolvers/customization/customization.service.ts (1)
12-15: Argument shape is correct;loadDynamixConfigFromDiskSyncreceives the right data structureThe review comment's concern about the loader argument is based on a misunderstanding. The code is correct as-is:
- Line 50 in
onModuleInit:this.configFile = paths['dynamix-config']?.[1]extracts a single file path string for use withupdateCfgFile.- Line 478 in
setTheme:loadDynamixConfigFromDiskSync(paths['dynamix-config'])correctly passes the full array to the loader.The function signature requires
configPaths: readonly (string | undefined | null)[](an array), andpaths['dynamix-config']is exactly that. This matches the existing pattern inapi/src/store/index.tsline 32, which also passes the full array:loadDynamixConfigFromDiskSync(configPaths)whereconfigPaths = store.getState().paths['dynamix-config'] ?? [].Passing
this.configFile(a single string) would violate the type contract and break the function.Optional: add error handling for reload/dispatch resilience
If the disk read or dispatch fails after
updateCfgFilesucceeds, the mutation currently rejects even though the config on disk is updated. Wrapping the reload/dispatch in atry-catchthat logs but proceeds togetTheme()would make the endpoint more resilient, though this is optional.web/vite-plugin-serve-static.ts (4)
60-69: Config path resolution order looks reasonable for devThe candidate resolution favors explicit env vars (
DEV_DYNAMIX_CFG,PATHS_DYNAMIX_CONFIG) before falling back to repo-relative paths, which is a sane precedence for a dev-only tool. TheexistsSyncprobe infindDynamixConfigPathis simple and should behave predictably across the small candidate list.No changes needed from my side; just confirm this order matches how you expect to override local configs during development.
111-152: Config write logic is correct; consider concurrency edge cases only if neededThe
writeThemeToConfigread–modify–write flow correctly:
- Preserves existing sections.
- Updates an existing
theme=in[display]if present.- Otherwise inserts
theme=into an existing[display]or creates the section at the end.For a Vite dev server this is plenty. The only theoretical gap is concurrent writes (multiple
/dev/themePOSTs) racing on the same file, but that’s unlikely and probably not worth guarding against here.No required changes; just be aware of the race window in case this ever gets reused outside dev tooling.
153-162: Request body reader is fine for dev; no size limits
readRequestBodyis straightforward and adequate for the small JSON payload expected on/dev/theme. If this ever grows beyond a tiny dev interface, you might want to:
- Impose a max body size and reject larger payloads.
- Handle
req.abortedexplicitly.For now, I’d keep it as-is.
230-276:/dev/themeendpoint behavior looks correct and robustThe middleware cleanly handles:
GET→ returns{ theme: ThemeName | null }JSON from config.POST→ reads JSON body, validatesthemeas a string, normalizes againstALLOWED_THEMES, persists viawriteThemeToConfig, and returns the result plustheme.- Other methods → 405 with an
Allowheader.- Malformed JSON → 400 with a structured error.
This aligns well with the PR’s goal of a small dev-only surface for theme persistence, and keeps responses consistently JSON-typed for the main flows.
No behavior changes to suggest here.
- Added PHP logic to determine if the current theme is dark and set a CSS variable accordingly. - Introduced a new function to retrieve the dark mode state from the CSS variable in JavaScript. - Updated the theme store to initialize dark mode based on the CSS variable, ensuring consistent theme application across the application. This improves user experience by ensuring the correct theme is applied based on user preferences.
- Removed the previous test for applying dark mode classes to .unapi elements. - Added a new test to verify that dark mode is initialized based on a CSS variable when the store is created. - Mocked getComputedStyle to simulate dark mode and checked that the appropriate classes are added to document elements. This enhances the test coverage for theme handling in the application.
- Removed the unnecessary assignment of the store variable in the theme store test. - This change streamlines the test setup while maintaining the verification of dark mode class application. This update enhances the clarity and efficiency of the test code.
- Updated the WebComponentsExtractor to set the theme name and conditionally apply gradient variables based on banner visibility. - Improved the useTeleport and mount-engine functions to inherit dark mode from the document, ensuring consistency across components. - Refactored the theme store to initialize dark mode based on CSS variables and streamline theme management, including lazy loading of theme queries. These changes improve the user experience by ensuring that the correct theme and dark mode settings are applied consistently throughout the application.
- Updated the WebComponentsExtractor to utilize a CSS variable for the banner gradient stop, allowing for responsive adjustments. - Modified theme store tests to reflect the new gradient format, ensuring consistency in gradient rendering. - Added CSS rules to define the banner gradient stop, with a media query for different screen sizes. These changes improve the flexibility and responsiveness of the banner gradient in the application.
- Introduced a new test to verify the correct application of the banner gradient when the theme is set. - This enhancement ensures that the theme store's functionality for banner gradient customization is properly validated. These changes improve the robustness of the theme store tests.
- Refactored UserProfile tests to replace direct theme property manipulations with the setTheme method, ensuring consistency in theme state management. - Mocked getComputedStyle to simulate CSS variables for banner gradient and dark mode, enhancing test reliability. - Improved test coverage for theme-related functionality, particularly around banner visibility and description rendering. These changes enhance the robustness and maintainability of the UserProfile component tests.
- Updated ColorSwitcher and UserProfile tests to set CSS variables directly on the document element, improving test reliability and consistency. - Refactored cleanup logic in theme store tests to handle unmounting more gracefully, ensuring robust test teardown. These changes enhance the maintainability and clarity of the theme-related tests.
- Introduced CustomizationMutations to handle theme updates via GraphQL. - Implemented setTheme mutation to update the UI theme and persist changes in the configuration. - Updated the generated GraphQL schema to include the new mutations and theme enum. - Enhanced the CustomizationService to manage theme persistence and in-memory updates. - Updated the frontend to support theme selection and synchronization with the server. These changes improve the user experience by allowing dynamic theme management and ensuring consistent application of user preferences.
8655575 to
d9c4b01
Compare
|
🚀 Storybook has been deployed to staging: https://unraid-ui-storybook-staging.unraid-workers.workers.dev |
- Added a new mutation, SET_THEME_MUTATION, to facilitate theme updates via GraphQL. - Refactored DevThemeSwitcher.standalone.vue to utilize the new mutation for setting the theme. - Updated GraphQL schema to include the setTheme mutation and its associated types. - Enhanced gql.ts and graphql.ts to support the new mutation structure. These changes enable dynamic theme switching, improving user experience by allowing real-time updates to the UI theme.
- Introduced a new utility function, isDarkModeActive, to streamline dark mode detection across the application. - Refactored useTeleport, mount-engine, and theme store to utilize the new utility for consistent dark mode handling. - Removed redundant dark mode checks from various components, enhancing code clarity and maintainability. These changes improve the consistency of dark mode application and simplify future theme management.
|
🚀 Storybook has been deployed to staging: https://unraid-ui-storybook-staging.unraid-workers.workers.dev |
- Updated useTeleport tests to include checks for dark mode application based on CSS variables and document classes. - Refactored test setup to ensure consistent state before each test, including resetting dark mode classes and clearing the DOM. - Added new tests to verify the correct behavior of the teleportTarget ref and the creation of the virtual container in both dark and light modes. These changes improve the reliability and coverage of the useTeleport tests, ensuring accurate dark mode handling.
|
|
||
| describe('isDarkModeActive', () => { | ||
| const originalGetComputedStyle = window.getComputedStyle; | ||
| const originalDocumentElement = document.documentElement; |
Check notice
Code scanning / CodeQL
Unused variable, import, function or class Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 days ago
The best way to fix this problem is to simply remove the declaration of the unused variable originalDocumentElement from line 6 in the file unraid-ui/src/lib/utils.test.ts. This prevents confusion, improves code readability, and may marginally reduce execution overhead or linting output. No other code needs editing, as there are no references to this variable anywhere in the file.
| @@ -3,7 +3,6 @@ | ||
|
|
||
| describe('isDarkModeActive', () => { | ||
| const originalGetComputedStyle = window.getComputedStyle; | ||
| const originalDocumentElement = document.documentElement; | ||
| const originalBody = document.body; | ||
|
|
||
| beforeEach(() => { |
| describe('isDarkModeActive', () => { | ||
| const originalGetComputedStyle = window.getComputedStyle; | ||
| const originalDocumentElement = document.documentElement; | ||
| const originalBody = document.body; |
Check notice
Code scanning / CodeQL
Unused variable, import, function or class Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 days ago
The best fix is to simply remove the declaration of the unused variable originalBody on line 7 in unraid-ui/src/lib/utils.test.ts. No other code should be changed, as there is no reference to that variable anywhere else in the file. Just one line can be deleted.
| @@ -4,7 +4,6 @@ | ||
| describe('isDarkModeActive', () => { | ||
| const originalGetComputedStyle = window.getComputedStyle; | ||
| const originalDocumentElement = document.documentElement; | ||
| const originalBody = document.body; | ||
|
|
||
| beforeEach(() => { | ||
| document.documentElement.classList.remove('dark'); |
|
🚀 Storybook has been deployed to staging: https://unraid-ui-storybook-staging.unraid-workers.workers.dev |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
♻️ Duplicate comments (1)
unraid-ui/src/lib/utils.test.ts (1)
5-7: Remove unusedoriginalDocumentElement/originalBodytest variables.These two constants are never read; only
originalGetComputedStyleis used. You can simplify the setup and satisfy CodeQL by dropping them:- const originalGetComputedStyle = window.getComputedStyle; - const originalDocumentElement = document.documentElement; - const originalBody = document.body; + const originalGetComputedStyle = window.getComputedStyle;
🧹 Nitpick comments (4)
web/src/components/UserProfile.standalone.vue (1)
95-98: Verify header layout with new left padding across breakpointsThe added
pl-[30%] md:pl-[160px]on an absolutely positioned, right-aligned root container changes its effective width. This is likely to grow the banner gradient footprint, but it’s also a fairly “magic” offset and could affect:
- Very small viewports (e.g., mobile) with limited header width.
- Cases with long server names or verbose localized descriptions.
Please sanity‑check on the smallest supported viewport and in a few locales to ensure:
- No horizontal overflow or double scrollbars.
- Header left‑side content (logo/nav) isn’t unintentionally overlapped.
If this offset needs to be reused or tuned later, consider moving it into a shared header layout utility or CSS variable instead of an inline arbitrary value for easier future adjustments.
Also, since this file is under
web/, confirmpnpm codegenhas been run if any GraphQL schema/operation changes are part of this PR, per the repo guidelines.web/__test__/components/UserProfile.test.ts (1)
343-369: Use partial updates withthemeStore.setThemeinstead of spreading the whole theme.
setThemealready merges a partial payload into the existing theme, so you don’t need to spreadthemeStore.themeon every call. This keeps the tests less coupled to the full theme shape and easier to maintain. For example:- themeStore.setTheme({ ...themeStore.theme, descriptionShow: true }); + themeStore.setTheme({ descriptionShow: true }); … - themeStore.setTheme({ - ...themeStore.theme, - banner: true, - bannerGradient: true, - }); + themeStore.setTheme({ banner: true, bannerGradient: true }); … - themeStore.setTheme({ - ...themeStore.theme, - bannerGradient: false, - }); + themeStore.setTheme({ bannerGradient: false }); … - themeStore.setTheme({ - ...themeStore.theme, - bannerGradient: true, - }); + themeStore.setTheme({ bannerGradient: true });Also applies to: 380-410
web/__test__/store/theme.test.ts (1)
106-152: CSS-variable-driven dark mode and bannerGradient tests correctly mirror store behaviorThe new tests accurately exercise the theme store’s DOM-driven logic:
darkModeis derived from--theme-dark-modewhen set to'1'/'0', independent of classes.bannerGradientonly returns abackground-imagestring when:
- not in dark mode,
theme.bannerandtheme.bannerGradientare true, and--banner-gradientis a non-empty, non-sentinel value.- Initialization of dark mode from
--theme-dark-mode/--theme-nameverifies that.darkis applied to bothdocumentElementandbody, matchingbootstrapDarkClass()semantics.The explicit
vi.restoreAllMocks()calls within these tests are redundant given theafterEachcleanup but don’t cause issues; you could drop them later for brevity if desired.Also applies to: 154-210, 279-308
web/src/components/DevThemeSwitcher.standalone.vue (1)
4-31: Dev theme switcher's multi-source resolution and persistence flow looks solidThe refactor to use generated GraphQL types (
ThemeName,SET_THEME_MUTATION) plusGET_THEME_QUERYand the centralizedapplyThemeSelectionpipeline gives you a clean, single path that:
- Normalizes all inputs (CSS var, cookie, localStorage, store, server) through
ThemeName.- Keeps cookie, localStorage, DOM CSS vars, dark classes, and the Pinia theme store in sync.
- Only calls the
setThememutation when a user-initiated change occurs (persist: true), while still reflecting server-provided theme viauseQuery.One optional refinement you could consider:
Hoist
themeCssMapto module scope
To avoid recreating the same mapping object on every call toupdateThemeCssLink, you could lift it out of the function into aconstat module scope. Not required, but slightly cleaner.Functionally, the component matches the dark-mode and theme semantics of
useThemeStoreand the backend schema, so this is good to ship.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (29)
api/generated-schema.graphql(2 hunks)api/src/unraid-api/graph/resolvers/customization/customization.module.ts(1 hunks)api/src/unraid-api/graph/resolvers/customization/customization.mutations.resolver.ts(1 hunks)api/src/unraid-api/graph/resolvers/customization/customization.service.ts(2 hunks)api/src/unraid-api/graph/resolvers/mutation/mutation.model.ts(2 hunks)api/src/unraid-api/graph/resolvers/mutation/mutation.resolver.ts(2 hunks)plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/include/web-components-extractor.php(2 hunks)unraid-ui/src/composables/useTeleport.test.ts(5 hunks)unraid-ui/src/composables/useTeleport.ts(1 hunks)unraid-ui/src/lib/utils.test.ts(1 hunks)unraid-ui/src/lib/utils.ts(1 hunks)web/.prettierignore(1 hunks)web/__test__/components/ColorSwitcher.test.ts(3 hunks)web/__test__/components/UserProfile.test.ts(4 hunks)web/__test__/components/Wrapper/mount-engine.test.ts(1 hunks)web/__test__/mocks/ui-components.ts(1 hunks)web/__test__/store/theme.test.ts(4 hunks)web/src/assets/main.css(1 hunks)web/src/components/DevThemeSwitcher.mutation.ts(1 hunks)web/src/components/DevThemeSwitcher.standalone.vue(3 hunks)web/src/components/UserProfile.standalone.vue(1 hunks)web/src/components/Wrapper/mount-engine.ts(2 hunks)web/src/composables/gql/gql.ts(3 hunks)web/src/composables/gql/graphql.ts(8 hunks)web/src/composables/gql/index.ts(1 hunks)web/src/helpers/dark-mode.ts(1 hunks)web/src/store/theme.ts(5 hunks)web/test-pages/partials/styles.njk(2 hunks)web/vite-plugin-serve-static.ts(4 hunks)
✅ Files skipped from review due to trivial changes (1)
- web/src/composables/gql/index.ts
🚧 Files skipped from review as they are similar to previous changes (4)
- api/src/unraid-api/graph/resolvers/customization/customization.module.ts
- plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/include/web-components-extractor.php
- web/vite-plugin-serve-static.ts
- web/.prettierignore
🧰 Additional context used
📓 Path-based instructions (15)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,js,jsx}: Always use TypeScript imports with.jsextensions for ESM compatibility
Never add comments unless they are needed for clarity of function
Never add comments for obvious things, and avoid commenting when starting and ending code blocks
Files:
api/src/unraid-api/graph/resolvers/customization/customization.service.tsweb/__test__/store/theme.test.tsapi/src/unraid-api/graph/resolvers/mutation/mutation.resolver.tsweb/src/components/DevThemeSwitcher.mutation.tsunraid-ui/src/composables/useTeleport.tsunraid-ui/src/composables/useTeleport.test.tsunraid-ui/src/lib/utils.test.tsapi/src/unraid-api/graph/resolvers/customization/customization.mutations.resolver.tsweb/__test__/components/Wrapper/mount-engine.test.tsweb/__test__/mocks/ui-components.tsweb/__test__/components/UserProfile.test.tsapi/src/unraid-api/graph/resolvers/mutation/mutation.model.tsweb/src/components/Wrapper/mount-engine.tsweb/src/composables/gql/graphql.tsunraid-ui/src/lib/utils.tsweb/src/store/theme.tsweb/src/composables/gql/gql.tsweb/src/helpers/dark-mode.tsweb/__test__/components/ColorSwitcher.test.ts
api/**/*
📄 CodeRabbit inference engine (CLAUDE.md)
Prefer adding new files to the NestJS repo located at
api/src/unraid-api/instead of the legacy code
Files:
api/src/unraid-api/graph/resolvers/customization/customization.service.tsapi/src/unraid-api/graph/resolvers/mutation/mutation.resolver.tsapi/src/unraid-api/graph/resolvers/customization/customization.mutations.resolver.tsapi/src/unraid-api/graph/resolvers/mutation/mutation.model.tsapi/generated-schema.graphql
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Never use theanytype. Always prefer proper typing
Avoid using casting whenever possible, prefer proper typing from the start
Files:
api/src/unraid-api/graph/resolvers/customization/customization.service.tsweb/__test__/store/theme.test.tsapi/src/unraid-api/graph/resolvers/mutation/mutation.resolver.tsweb/src/components/DevThemeSwitcher.mutation.tsunraid-ui/src/composables/useTeleport.tsunraid-ui/src/composables/useTeleport.test.tsunraid-ui/src/lib/utils.test.tsapi/src/unraid-api/graph/resolvers/customization/customization.mutations.resolver.tsweb/__test__/components/Wrapper/mount-engine.test.tsweb/__test__/mocks/ui-components.tsweb/__test__/components/UserProfile.test.tsapi/src/unraid-api/graph/resolvers/mutation/mutation.model.tsweb/src/components/Wrapper/mount-engine.tsweb/src/composables/gql/graphql.tsunraid-ui/src/lib/utils.tsweb/src/store/theme.tsweb/src/composables/gql/gql.tsweb/src/helpers/dark-mode.tsweb/__test__/components/ColorSwitcher.test.ts
api/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
cache-manager v7 expects TTL values in milliseconds, not seconds (e.g., 600000 for 10 minutes, not 600)
Files:
api/src/unraid-api/graph/resolvers/customization/customization.service.tsapi/src/unraid-api/graph/resolvers/mutation/mutation.resolver.tsapi/src/unraid-api/graph/resolvers/customization/customization.mutations.resolver.tsapi/src/unraid-api/graph/resolvers/mutation/mutation.model.ts
**/*
📄 CodeRabbit inference engine (.cursor/rules/default.mdc)
Never add comments unless they are needed for clarity of function
Files:
api/src/unraid-api/graph/resolvers/customization/customization.service.tsweb/__test__/store/theme.test.tsweb/src/assets/main.cssapi/src/unraid-api/graph/resolvers/mutation/mutation.resolver.tsweb/src/components/DevThemeSwitcher.mutation.tsweb/test-pages/partials/styles.njkunraid-ui/src/composables/useTeleport.tsunraid-ui/src/composables/useTeleport.test.tsunraid-ui/src/lib/utils.test.tsapi/src/unraid-api/graph/resolvers/customization/customization.mutations.resolver.tsweb/__test__/components/Wrapper/mount-engine.test.tsweb/__test__/mocks/ui-components.tsweb/src/components/DevThemeSwitcher.standalone.vueweb/__test__/components/UserProfile.test.tsapi/src/unraid-api/graph/resolvers/mutation/mutation.model.tsweb/src/components/Wrapper/mount-engine.tsweb/src/composables/gql/graphql.tsunraid-ui/src/lib/utils.tsapi/generated-schema.graphqlweb/src/store/theme.tsweb/src/composables/gql/gql.tsweb/src/helpers/dark-mode.tsweb/__test__/components/ColorSwitcher.test.tsweb/src/components/UserProfile.standalone.vue
**/*.test.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.test.{ts,tsx,js,jsx}: Use VITEST for test suite, not jest
Use.rejects.toThrow()without arguments to test that functions throw errors, not exact error message strings
Files:
web/__test__/store/theme.test.tsunraid-ui/src/composables/useTeleport.test.tsunraid-ui/src/lib/utils.test.tsweb/__test__/components/Wrapper/mount-engine.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
web/**/*
📄 CodeRabbit inference engine (CLAUDE.md)
Always run
pnpm codegenfor GraphQL code generation in the web directory
Files:
web/__test__/store/theme.test.tsweb/src/assets/main.cssweb/src/components/DevThemeSwitcher.mutation.tsweb/test-pages/partials/styles.njkweb/__test__/components/Wrapper/mount-engine.test.tsweb/__test__/mocks/ui-components.tsweb/src/components/DevThemeSwitcher.standalone.vueweb/__test__/components/UserProfile.test.tsweb/src/components/Wrapper/mount-engine.tsweb/src/composables/gql/graphql.tsweb/src/store/theme.tsweb/src/composables/gql/gql.tsweb/src/helpers/dark-mode.tsweb/__test__/components/ColorSwitcher.test.tsweb/src/components/UserProfile.standalone.vue
web/__test__/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/__test__/**/*.test.{ts,tsx}: Test component behavior and output, not implementation details
Usemountfrom Vue Test Utils for component testing
Stub complex child components that aren't the focus of the test
Find elements with semantic queries likefind('button')rather than data-test IDs
UsecreatePinia()andsetActivePiniawhen testing Store files, only usecreateTestingPiniaif you specifically need its testing features
Files:
web/__test__/store/theme.test.tsweb/__test__/components/Wrapper/mount-engine.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
**/*.test.ts
📄 CodeRabbit inference engine (.cursor/rules/web-testing-rules.mdc)
**/*.test.ts: Use.rejects.toThrow()without arguments to test that functions throw errors. Don't test exact error message strings unless the message format is specifically what you're testing
Test what the code does, not implementation details like exact error message wording
Mock external services and API calls
Usevi.mock()for module-level mocks
Specify return values for component methods withvi.spyOn()
Reset mocks between tests withvi.clearAllMocks()
Always await async operations before making assertions
Files:
web/__test__/store/theme.test.tsunraid-ui/src/composables/useTeleport.test.tsunraid-ui/src/lib/utils.test.tsweb/__test__/components/Wrapper/mount-engine.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
**/__test__/store/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/web-testing-rules.mdc)
**/__test__/store/**/*.ts: UsecreatePiniaandsetActivePiniawhen testing Pinia store files
UsecreatePinia()instead ofcreateTestingPinia()for most Pinia store tests
Only usecreateTestingPiniaif you specifically need its testing features for Pinia stores
Let stores initialize with their natural default state instead of forcing initial state
Do not mock the store being tested in the test file when usingcreatePinia()
Place all mock declarations at the top level in Pinia store tests
Use factory functions for module mocks in Pinia store tests to avoid hoisting issues
Test Pinia action side effects and state changes
Verify Pinia actions are called with correct parameters
Mock external dependencies appropriately in Pinia store tests
Test computed properties in Pinia stores by accessing them directly
Verify state changes after Pinia store actions
Test Pinia store getter dependencies are properly mocked
Test Pinia store interactions with other stores
Verify proper error handling in Pinia store tests
Test async operations completely in Pinia store tests
Override specific Pinia action implementations when needed in tests
Set initial state for focused Pinia store testing
Files:
web/__test__/store/theme.test.ts
**/store/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/web-testing-rules.mdc)
Ensure Vue reactivity imports like
computed,ref, andwatchEffectare added to store files even with Nuxt auto-imports enabled
Files:
web/__test__/store/theme.test.tsweb/src/store/theme.ts
web/**/*.mutation.ts
📄 CodeRabbit inference engine (CLAUDE.md)
GraphQL mutations must be placed in
.mutation.tsfiles
Files:
web/src/components/DevThemeSwitcher.mutation.ts
web/src/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Ensure Vue reactivity imports are added to store files (computed, ref, watchEffect)
Files:
web/src/components/DevThemeSwitcher.mutation.tsweb/src/components/Wrapper/mount-engine.tsweb/src/composables/gql/graphql.tsweb/src/store/theme.tsweb/src/composables/gql/gql.tsweb/src/helpers/dark-mode.ts
web/**/*.{query.ts,mutation.ts}
📄 CodeRabbit inference engine (.cursor/rules/web-graphql.mdc)
All GraphQL under
web/must follow the.query.tsand.mutation.tsnaming convention
Files:
web/src/components/DevThemeSwitcher.mutation.ts
**/__test__/components/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/web-testing-rules.mdc)
**/__test__/components/**/*.ts: Usemountfrom Vue Test Utils for component testing
Stub complex child components that aren't the focus of the test
Mock external dependencies and services in Vue component tests
Test component behavior and output, not implementation details
Verify that the expected elements are rendered in Vue components
Test component interactions such as clicks and inputs
Check for expected prop handling and event emissions in Vue components
UsecreateTestingPinia()for mocking stores in Vue components
Use semantic queries likefind('button')orfind('[data-test="id"]')but prefer not to use data test ID's when finding elements
Find Vue components withfindComponent(ComponentName)
UsefindAllto check for multiple elements in Vue components
Assert on rendered text content withwrapper.text()
Assert on element attributes withelement.attributes()
Verify element existence withexpect(element.exists()).toBe(true)
Check component state through rendered output
Trigger events withawait element.trigger('click')
Set input values withawait input.setValue('value')
Test emitted events withwrapper.emitted()
Useawait nextTick()for DOM updates in Vue component tests
UseflushPromises()for complex promise chains in Vue component tests
Files:
web/__test__/components/Wrapper/mount-engine.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
🧠 Learnings (78)
📓 Common learnings
Learnt from: elibosley
Repo: unraid/api PR: 972
File: web/store/theme.ts:46-49
Timestamp: 2024-12-17T14:59:32.458Z
Learning: In the `web/store/theme.ts` file of the Unraid web application, the header is intentionally designed to have a light background with dark text in dark mode, and a dark background with light text in light mode.
📚 Learning: 2025-02-24T14:51:21.328Z
Learnt from: elibosley
Repo: unraid/api PR: 1181
File: web/store/theme.ts:0-0
Timestamp: 2025-02-24T14:51:21.328Z
Learning: In the Unraid API project's theme system, exact TypeScript type definitions are preferred over index signatures for theme variables to ensure better type safety.
Applied to files:
api/src/unraid-api/graph/resolvers/customization/customization.service.tsunraid-ui/src/lib/utils.test.tsweb/src/components/DevThemeSwitcher.standalone.vueweb/src/composables/gql/graphql.tsunraid-ui/src/lib/utils.tsweb/src/store/theme.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Mock external dependencies appropriately in Pinia store tests
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/Wrapper/mount-engine.test.tsweb/__test__/components/UserProfile.test.tsweb/src/store/theme.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Test Pinia store getter dependencies are properly mocked
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/UserProfile.test.tsweb/src/store/theme.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Use factory functions for module mocks in Pinia store tests to avoid hoisting issues
Applied to files:
web/__test__/store/theme.test.tsunraid-ui/src/composables/useTeleport.test.tsweb/__test__/components/Wrapper/mount-engine.test.tsweb/__test__/mocks/ui-components.tsweb/__test__/components/UserProfile.test.tsweb/src/store/theme.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Place all mock declarations at the top level in Pinia store tests
Applied to files:
web/__test__/store/theme.test.tsweb/src/store/theme.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Do not mock the store being tested in the test file when using `createPinia()`
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Test Pinia store interactions with other stores
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Test Pinia action side effects and state changes
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/UserProfile.test.ts
📚 Learning: 2025-11-24T17:51:37.915Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:51:37.915Z
Learning: Applies to web/__test__/**/*.test.{ts,tsx} : Use `createPinia()` and `setActivePinia` when testing Store files, only use `createTestingPinia` if you specifically need its testing features
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Mock external dependencies and services in Vue component tests
Applied to files:
web/__test__/store/theme.test.tsunraid-ui/src/composables/useTeleport.test.tsunraid-ui/src/lib/utils.test.tsweb/__test__/components/Wrapper/mount-engine.test.tsweb/__test__/mocks/ui-components.tsweb/__test__/components/UserProfile.test.tsweb/src/store/theme.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Test component behavior and output, not implementation details
Applied to files:
web/__test__/store/theme.test.tsunraid-ui/src/composables/useTeleport.test.tsunraid-ui/src/lib/utils.test.tsweb/__test__/components/Wrapper/mount-engine.test.tsweb/__test__/mocks/ui-components.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-02-20T15:52:58.297Z
Learnt from: elibosley
Repo: unraid/api PR: 1155
File: web/store/theme.ts:161-172
Timestamp: 2025-02-20T15:52:58.297Z
Learning: The banner gradient implementation in web/store/theme.ts doesn't require explicit error handling for hexToRgba as CSS gracefully handles invalid values by ignoring them.
Applied to files:
web/__test__/store/theme.test.tsweb/src/assets/main.cssweb/__test__/components/UserProfile.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Verify proper error handling in Pinia store tests
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Test computed properties in Pinia stores by accessing them directly
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Verify state changes after Pinia store actions
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Use `await nextTick()` for DOM updates in Vue component tests
Applied to files:
web/__test__/store/theme.test.tsunraid-ui/src/composables/useTeleport.test.tsweb/src/store/theme.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Check for expected prop handling and event emissions in Vue components
Applied to files:
web/__test__/store/theme.test.tsunraid-ui/src/composables/useTeleport.test.tsweb/src/store/theme.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/*.test.ts : Use `vi.mock()` for module-level mocks
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/Wrapper/mount-engine.test.tsweb/__test__/mocks/ui-components.tsweb/src/store/theme.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Verify that the expected elements are rendered in Vue components
Applied to files:
web/__test__/store/theme.test.tsunraid-ui/src/composables/useTeleport.test.tsunraid-ui/src/lib/utils.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Use `createTestingPinia()` for mocking stores in Vue components
Applied to files:
web/__test__/store/theme.test.tsunraid-ui/src/composables/useTeleport.test.tsweb/__test__/components/Wrapper/mount-engine.test.tsweb/__test__/components/UserProfile.test.tsweb/src/store/theme.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:51:37.915Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:51:37.915Z
Learning: Applies to web/src/**/*.ts : Ensure Vue reactivity imports are added to store files (computed, ref, watchEffect)
Applied to files:
web/__test__/store/theme.test.tsweb/src/store/theme.ts
📚 Learning: 2025-11-24T17:51:37.915Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:51:37.915Z
Learning: Applies to web/__test__/**/*.test.{ts,tsx} : Test component behavior and output, not implementation details
Applied to files:
web/__test__/store/theme.test.tsunraid-ui/src/composables/useTeleport.test.tsunraid-ui/src/lib/utils.test.tsweb/__test__/components/Wrapper/mount-engine.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-02-20T15:52:56.733Z
Learnt from: elibosley
Repo: unraid/api PR: 1155
File: web/store/theme.ts:49-50
Timestamp: 2025-02-20T15:52:56.733Z
Learning: CSS variable names in the theme store should be concise and follow established patterns. For example, prefer '--gradient-start' over '--color-customgradient-start' to maintain consistency with other variable names.
Applied to files:
web/__test__/store/theme.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/*.test.ts : Specify return values for component methods with `vi.spyOn()`
Applied to files:
web/__test__/store/theme.test.tsunraid-ui/src/lib/utils.test.tsweb/__test__/components/Wrapper/mount-engine.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/*.test.ts : Reset mocks between tests with `vi.clearAllMocks()`
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/Wrapper/mount-engine.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Verify element existence with `expect(element.exists()).toBe(true)`
Applied to files:
web/__test__/store/theme.test.tsunraid-ui/src/composables/useTeleport.test.tsunraid-ui/src/lib/utils.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Set initial state for focused Pinia store testing
Applied to files:
web/__test__/store/theme.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-03-14T16:10:38.562Z
Learnt from: pujitm
Repo: unraid/api PR: 1211
File: api/src/graphql/schema/types/connect/connect.graphql:142-146
Timestamp: 2025-03-14T16:10:38.562Z
Learning: The updateApiSettings mutation in api/src/unraid-api/graph/connect/connect.resolver.ts is protected with the UsePermissions decorator that requires UPDATE permission on the CONFIG resource.
Applied to files:
api/src/unraid-api/graph/resolvers/mutation/mutation.resolver.tsapi/src/unraid-api/graph/resolvers/customization/customization.mutations.resolver.tsapi/src/unraid-api/graph/resolvers/mutation/mutation.model.ts
📚 Learning: 2024-11-06T20:59:25.809Z
Learnt from: mdatelle
Repo: unraid/api PR: 942
File: api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts:111-113
Timestamp: 2024-11-06T20:59:25.809Z
Learning: In the Unraid API project, error handling for mutations is handled at the service level rather than in the GraphQL resolvers. Specifically, in `api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts`, methods like `removeRoleFromApiKey` rely on service-level error handling.
Applied to files:
api/src/unraid-api/graph/resolvers/mutation/mutation.resolver.tsapi/src/unraid-api/graph/resolvers/customization/customization.mutations.resolver.tsweb/src/components/DevThemeSwitcher.standalone.vueapi/src/unraid-api/graph/resolvers/mutation/mutation.model.ts
📚 Learning: 2025-03-12T13:48:14.850Z
Learnt from: pujitm
Repo: unraid/api PR: 1211
File: web/composables/gql/gql.ts:17-18
Timestamp: 2025-03-12T13:48:14.850Z
Learning: In the Unraid API project, the duplicate GraphQL query and mutation strings in gql.ts files are intentionally generated by GraphQL CodeGen tool and are necessary for the type system to function properly.
Applied to files:
api/src/unraid-api/graph/resolvers/mutation/mutation.resolver.tsweb/src/components/DevThemeSwitcher.standalone.vueapi/src/unraid-api/graph/resolvers/mutation/mutation.model.tsweb/src/composables/gql/graphql.tsweb/src/composables/gql/gql.ts
📚 Learning: 2025-11-24T17:51:37.915Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:51:37.915Z
Learning: Applies to web/**/*.mutation.ts : GraphQL mutations must be placed in `.mutation.ts` files
Applied to files:
api/src/unraid-api/graph/resolvers/mutation/mutation.resolver.tsweb/src/components/DevThemeSwitcher.mutation.tsweb/src/components/DevThemeSwitcher.standalone.vueapi/src/unraid-api/graph/resolvers/mutation/mutation.model.tsweb/src/composables/gql/graphql.tsweb/src/composables/gql/gql.ts
📚 Learning: 2024-11-05T14:48:56.949Z
Learnt from: elibosley
Repo: unraid/api PR: 942
File: api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts:41-47
Timestamp: 2024-11-05T14:48:56.949Z
Learning: Input types for GraphQL mutations can be pulled from the GraphQL codegen types, as they are generated into the API files. Therefore, explicit typing in the resolver files may be unnecessary.
Applied to files:
api/src/unraid-api/graph/resolvers/mutation/mutation.resolver.tsapi/src/unraid-api/graph/resolvers/customization/customization.mutations.resolver.tsweb/src/components/DevThemeSwitcher.standalone.vueapi/src/unraid-api/graph/resolvers/mutation/mutation.model.tsweb/src/composables/gql/gql.ts
📚 Learning: 2025-11-24T17:52:00.572Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-graphql.mdc:0-0
Timestamp: 2025-11-24T17:52:00.572Z
Learning: Applies to web/**/*.{query.ts,mutation.ts} : All GraphQL under `web/` must follow the `.query.ts` and `.mutation.ts` naming convention
Applied to files:
web/src/components/DevThemeSwitcher.mutation.tsweb/src/components/DevThemeSwitcher.standalone.vueweb/src/composables/gql/gql.ts
📚 Learning: 2025-11-24T17:51:37.915Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:51:37.915Z
Learning: Applies to web/**/*.query.ts : GraphQL queries must be placed in `.query.ts` files
Applied to files:
web/src/components/DevThemeSwitcher.mutation.tsweb/src/components/DevThemeSwitcher.standalone.vueweb/src/composables/gql/gql.ts
📚 Learning: 2025-02-21T18:40:10.810Z
Learnt from: elibosley
Repo: unraid/api PR: 1181
File: web/store/theme.ts:210-216
Timestamp: 2025-02-21T18:40:10.810Z
Learning: When updating theme-related CSS variables via `cssText`, preserve existing non-theme styles by filtering out only theme-related rules (those starting with '--') and combining them with the new theme styles.
Applied to files:
web/test-pages/partials/styles.njkweb/src/components/DevThemeSwitcher.standalone.vueweb/src/store/theme.ts
📚 Learning: 2024-12-17T14:59:32.458Z
Learnt from: elibosley
Repo: unraid/api PR: 972
File: web/store/theme.ts:46-49
Timestamp: 2024-12-17T14:59:32.458Z
Learning: In the `web/store/theme.ts` file of the Unraid web application, the header is intentionally designed to have a light background with dark text in dark mode, and a dark background with light text in light mode.
Applied to files:
web/test-pages/partials/styles.njkweb/__test__/mocks/ui-components.tsweb/src/components/DevThemeSwitcher.standalone.vueweb/src/components/Wrapper/mount-engine.tsunraid-ui/src/lib/utils.tsweb/src/store/theme.ts
📚 Learning: 2025-04-25T13:29:07.868Z
Learnt from: elibosley
Repo: unraid/api PR: 1369
File: web/components/Activation/WelcomeModal.ce.vue:40-40
Timestamp: 2025-04-25T13:29:07.868Z
Learning: The `#confirmPassword` selector in the WelcomeModal.ce.vue component is intentionally looking for an element that exists in the Unraid OS login page (.login.php), not within the component itself. This is used as part of a font-size adjustment workaround to handle different CSS between login and authenticated pages.
Applied to files:
unraid-ui/src/composables/useTeleport.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Test component interactions such as clicks and inputs
Applied to files:
unraid-ui/src/composables/useTeleport.test.tsunraid-ui/src/lib/utils.test.tsweb/__test__/components/UserProfile.test.ts
📚 Learning: 2025-11-24T17:51:37.915Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:51:37.915Z
Learning: Applies to web/__test__/**/*.test.{ts,tsx} : Use `mount` from Vue Test Utils for component testing
Applied to files:
unraid-ui/src/composables/useTeleport.test.tsweb/__test__/components/Wrapper/mount-engine.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Use `mount` from Vue Test Utils for component testing
Applied to files:
unraid-ui/src/composables/useTeleport.test.tsweb/__test__/components/Wrapper/mount-engine.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: This is a Nuxt.js app but tests are run with vitest outside of the Nuxt environment
Applied to files:
unraid-ui/src/composables/useTeleport.test.tsunraid-ui/src/lib/utils.test.tsweb/src/components/Wrapper/mount-engine.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Use `flushPromises()` for complex promise chains in Vue component tests
Applied to files:
unraid-ui/src/composables/useTeleport.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Check component state through rendered output
Applied to files:
unraid-ui/src/composables/useTeleport.test.tsunraid-ui/src/lib/utils.test.tsweb/__test__/components/UserProfile.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Trigger events with `await element.trigger('click')`
Applied to files:
unraid-ui/src/composables/useTeleport.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Assert on rendered text content with `wrapper.text()`
Applied to files:
unraid-ui/src/composables/useTeleport.test.tsweb/__test__/components/Wrapper/mount-engine.test.tsweb/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:51:37.915Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:51:37.915Z
Learning: Applies to web/__test__/**/*.test.{ts,tsx} : Find elements with semantic queries like `find('button')` rather than data-test IDs
Applied to files:
unraid-ui/src/lib/utils.test.ts
📚 Learning: 2025-11-24T17:51:46.348Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/api-rules.mdc:0-0
Timestamp: 2025-11-24T17:51:46.348Z
Learning: Applies to api/**/*.test.{ts,tsx} : Use Vitest for the test suite, not Jest
Applied to files:
unraid-ui/src/lib/utils.test.ts
📚 Learning: 2025-11-24T17:51:37.915Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:51:37.915Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use VITEST for test suite, not jest
Applied to files:
unraid-ui/src/lib/utils.test.ts
📚 Learning: 2024-11-06T20:59:06.096Z
Learnt from: mdatelle
Repo: unraid/api PR: 942
File: api/src/unraid-api/graph/resolvers/auth/auth.resolver.ts:87-89
Timestamp: 2024-11-06T20:59:06.096Z
Learning: Error handling in mutations like `addRoleForUser` in `auth.resolver.ts` is performed at the service level, so additional error handling in the resolver is unnecessary.
Applied to files:
api/src/unraid-api/graph/resolvers/customization/customization.mutations.resolver.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Test emitted events with `wrapper.emitted()`
Applied to files:
web/__test__/components/Wrapper/mount-engine.test.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/*.test.ts : Mock external services and API calls
Applied to files:
web/__test__/components/Wrapper/mount-engine.test.ts
📚 Learning: 2025-08-09T01:03:29.676Z
Learnt from: elibosley
Repo: unraid/api PR: 1575
File: packages/unraid-shared/src/services/socket-config.service.spec.ts:10-13
Timestamp: 2025-08-09T01:03:29.676Z
Learning: Vitest is used for all testing across all repositories in the unraid organization, not Jest. Always use `vi` for mocking utilities, not `jest`.
Applied to files:
web/__test__/components/Wrapper/mount-engine.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:00.572Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-graphql.mdc:0-0
Timestamp: 2025-11-24T17:52:00.572Z
Learning: Always run `pnpm codegen` for GraphQL code generation in the web directory
Applied to files:
web/src/components/DevThemeSwitcher.standalone.vue
📚 Learning: 2025-11-24T17:51:37.915Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:51:37.915Z
Learning: Applies to web/**/* : Always run `pnpm codegen` for GraphQL code generation in the web directory
Applied to files:
web/src/components/DevThemeSwitcher.standalone.vue
📚 Learning: 2025-03-12T13:35:43.900Z
Learnt from: pujitm
Repo: unraid/api PR: 1211
File: web/codegen.ts:14-14
Timestamp: 2025-03-12T13:35:43.900Z
Learning: The JSON scalar type in web/codegen.ts was temporarily changed from 'string' to 'any' for compatibility with JsonForms integration. This change facilitates the implementation of the Connect settings web component.
Applied to files:
web/src/components/DevThemeSwitcher.standalone.vue
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Applies to **/__test__/components/**/*.ts : Stub complex child components that aren't the focus of the test
Applied to files:
web/__test__/components/UserProfile.test.ts
📚 Learning: 2025-11-24T17:51:37.915Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:51:37.915Z
Learning: Applies to web/__test__/**/*.test.{ts,tsx} : Stub complex child components that aren't the focus of the test
Applied to files:
web/__test__/components/UserProfile.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Use `createPinia` and `setActivePinia` when testing Pinia store files
Applied to files:
web/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Use `createPinia()` instead of `createTestingPinia()` for most Pinia store tests
Applied to files:
web/__test__/components/UserProfile.test.tsweb/__test__/components/ColorSwitcher.test.ts
📚 Learning: 2025-04-02T21:21:29.168Z
Learnt from: elibosley
Repo: unraid/api PR: 1308
File: unraid-ui/src/components/common/loading/Error.vue:2-2
Timestamp: 2025-04-02T21:21:29.168Z
Learning: Components in the unraid-ui folder require explicit imports and are not autoloaded, unlike other parts of the project that may use Nuxt.js autoloading features.
Applied to files:
web/src/components/Wrapper/mount-engine.ts
📚 Learning: 2024-12-17T13:55:42.068Z
Learnt from: elibosley
Repo: unraid/api PR: 972
File: web/components/ColorSwitcher.ce.vue:1-2
Timestamp: 2024-12-17T13:55:42.068Z
Learning: In this Nuxt.js project, components used in templates are automatically imported by Nuxt, so explicit import statements for components are unnecessary.
Applied to files:
web/src/components/Wrapper/mount-engine.ts
📚 Learning: 2025-03-13T16:17:21.897Z
Learnt from: elibosley
Repo: unraid/api PR: 1211
File: unraid-ui/src/components/form/number/NumberFieldInput.vue:1-21
Timestamp: 2025-03-13T16:17:21.897Z
Learning: The unraid-ui is a UI library being published externally, where wrapping third-party components (like those from reka-ui) is an intentional design choice to maintain a consistent interface, standardize styling, and control the exposed API.
Applied to files:
web/src/components/Wrapper/mount-engine.ts
📚 Learning: 2024-12-09T15:47:29.325Z
Learnt from: pujitm
Repo: unraid/api PR: 975
File: web/components/Notifications/TabList.vue:1-24
Timestamp: 2024-12-09T15:47:29.325Z
Learning: In our Nuxt setup using Vue.js, components defined within the codebase are autoloaded and do not require explicit import statements in the script section. For example, components like `TabsList` and `TabsTrigger` used in `web/components/Notifications/TabList.vue` are automatically available without imports.
Applied to files:
web/src/components/Wrapper/mount-engine.ts
📚 Learning: 2025-03-27T23:52:57.888Z
Learnt from: zackspear
Repo: unraid/api PR: 0
File: :0-0
Timestamp: 2025-03-27T23:52:57.888Z
Learning: In the unraid/api project, Vue components are compiled into web components. The `setActivePinia(createPinia())` call at the module level in store files is intentional and ensures all web components share a single Pinia store instance, which is the desired behavior. This shared state approach is critical for the application's architecture to function correctly.
Applied to files:
web/src/components/Wrapper/mount-engine.tsweb/src/store/theme.ts
📚 Learning: 2024-12-06T17:38:40.999Z
Learnt from: elibosley
Repo: unraid/api PR: 974
File: web/components/Loading/Error.vue:1-3
Timestamp: 2024-12-06T17:38:40.999Z
Learning: In Nuxt.js projects, components are automatically imported, so explicit import statements for components like `LoadingSpinner` in `web/components/Loading/Error.vue` are not necessary.
Applied to files:
web/src/components/Wrapper/mount-engine.ts
📚 Learning: 2024-12-06T17:34:16.133Z
Learnt from: pujitm
Repo: unraid/api PR: 974
File: web/components/Loading/Error.vue:50-50
Timestamp: 2024-12-06T17:34:16.133Z
Learning: In this project, the `Button` component from `~/components/shadcn/Button.vue` is autoloaded and does not need to be imported manually in components like `web/components/Loading/Error.vue`.
Applied to files:
web/src/components/Wrapper/mount-engine.ts
📚 Learning: 2025-03-27T23:52:57.888Z
Learnt from: zackspear
Repo: unraid/api PR: 0
File: :0-0
Timestamp: 2025-03-27T23:52:57.888Z
Learning: In the unraid/api project, Vue components are compiled into web components. Using `setActivePinia(createPinia())` in store files ensures that all web components share a single Pinia store instance, which is the desired behavior. Without this initialization, each web component would have its own isolated store, breaking the intended architecture.
Applied to files:
web/src/components/Wrapper/mount-engine.tsweb/src/store/theme.ts
📚 Learning: 2025-11-24T17:52:26.907Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.907Z
Learning: Nuxt is set to auto import so some Vue files may need `computed` or `ref` imported explicitly in store files
Applied to files:
web/src/components/Wrapper/mount-engine.tsweb/src/store/theme.ts
📚 Learning: 2024-11-19T16:34:44.804Z
Learnt from: pujitm
Repo: unraid/api PR: 963
File: web/helpers/markdown.ts:1-41
Timestamp: 2024-11-19T16:34:44.804Z
Learning: In `web/store/updateOsChangelog.ts`, when custom logic is required, `Markdown.create` is used to obtain a custom `marked` instance. This is the correct and intended usage of the `Markdown` helper in such cases.
Applied to files:
unraid-ui/src/lib/utils.ts
📚 Learning: 2025-11-24T17:51:46.348Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/api-rules.mdc:0-0
Timestamp: 2025-11-24T17:51:46.348Z
Learning: Applies to api/**/*.test.{ts,tsx} : Prefer to not mock simple dependencies
Applied to files:
web/src/store/theme.ts
📚 Learning: 2025-03-27T23:33:13.215Z
Learnt from: zackspear
Repo: unraid/api PR: 0
File: :0-0
Timestamp: 2025-03-27T23:33:13.215Z
Learning: In the unraid/api project, Vue components are compiled into web components. Using `setActivePinia(createPinia())` in store files would break the build by causing all web components to share a singular Pinia store instance. Each web component needs its own Pinia store instance to maintain proper isolation and encapsulation.
Applied to files:
web/src/store/theme.ts
📚 Learning: 2025-03-27T23:33:13.215Z
Learnt from: zackspear
Repo: unraid/api PR: 0
File: :0-0
Timestamp: 2025-03-27T23:33:13.215Z
Learning: In the unraid/api project, Vue components are compiled into web components. Using `setActivePinia(createPinia())` in store files would break the build by causing all web components to share a singular Pinia store instance. Each web component needs its own Pinia store instance to function correctly.
Applied to files:
web/src/store/theme.ts
📚 Learning: 2025-06-13T17:14:21.739Z
Learnt from: pujitm
Repo: unraid/api PR: 1417
File: web/components/ConnectSettings/ConnectSettings.ce.vue:11-18
Timestamp: 2025-06-13T17:14:21.739Z
Learning: The project’s build tooling auto-imports common Vue/Pinia helpers such as `storeToRefs`, so explicit import statements for them are not required.
Applied to files:
web/src/store/theme.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/store/**/*.ts : Ensure Vue reactivity imports like `computed`, `ref`, and `watchEffect` are added to store files even with Nuxt auto-imports enabled
Applied to files:
web/src/store/theme.ts
📚 Learning: 2024-12-09T15:45:46.492Z
Learnt from: pujitm
Repo: unraid/api PR: 975
File: web/components/Notifications/TabList.vue:1-4
Timestamp: 2024-12-09T15:45:46.492Z
Learning: In our Nuxt.js setup for the `web` project, it's not necessary to explicitly import `computed` from `vue` in Vue components, as it's globally available.
Applied to files:
web/src/store/theme.ts
📚 Learning: 2025-09-02T19:35:14.427Z
Learnt from: elibosley
Repo: unraid/api PR: 1639
File: web/components/standalone-mount.ts:38-54
Timestamp: 2025-09-02T19:35:14.427Z
Learning: In the unraid/api project, exposing Apollo client and GraphQL utilities (apolloClient, gql, graphqlParse) on the global window object is intentional to allow developers to interact with GraphQL from the browser console without needing to be inside Vue components. Developer experience is prioritized over minimizing global exposure in this context.
Applied to files:
web/src/composables/gql/gql.ts
📚 Learning: 2025-11-24T17:52:26.908Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-11-24T17:52:26.908Z
Learning: Applies to **/__test__/store/**/*.ts : Only use `createTestingPinia` if you specifically need its testing features for Pinia stores
Applied to files:
web/__test__/components/ColorSwitcher.test.ts
🧬 Code graph analysis (10)
api/src/unraid-api/graph/resolvers/customization/customization.service.ts (6)
api/src/unraid-api/graph/resolvers/customization/customization.resolver.ts (1)
theme(58-60)web/composables/gql/graphql.ts (1)
Theme(2057-2073)api/src/store/modules/paths.ts (1)
paths(114-118)api/src/store/index.ts (2)
getters(50-55)store(10-16)api/src/store/actions/load-dynamix-config-file.ts (1)
loadDynamixConfigFromDiskSync(57-61)api/src/store/modules/dynamix.ts (1)
updateDynamixConfig(20-22)
web/__test__/store/theme.test.ts (1)
api/src/store/index.ts (1)
store(10-16)
api/src/unraid-api/graph/resolvers/mutation/mutation.resolver.ts (1)
web/src/composables/gql/graphql.ts (1)
CustomizationMutations(605-609)
web/src/components/DevThemeSwitcher.mutation.ts (1)
web/src/composables/gql/gql.ts (1)
graphql(300-302)
unraid-ui/src/composables/useTeleport.ts (2)
unraid-ui/src/lib/utils.ts (1)
isDarkModeActive(58-70)web/src/helpers/dark-mode.ts (1)
isDarkModeActive(1-12)
api/src/unraid-api/graph/resolvers/mutation/mutation.model.ts (1)
web/src/composables/gql/graphql.ts (1)
CustomizationMutations(605-609)
web/src/components/Wrapper/mount-engine.ts (2)
unraid-ui/src/lib/utils.ts (1)
isDarkModeActive(58-70)web/src/helpers/dark-mode.ts (1)
isDarkModeActive(1-12)
unraid-ui/src/lib/utils.ts (1)
web/src/helpers/dark-mode.ts (1)
isDarkModeActive(1-12)
web/src/store/theme.ts (2)
unraid-ui/src/lib/utils.ts (1)
isDarkModeActive(58-70)web/src/helpers/dark-mode.ts (1)
isDarkModeActive(1-12)
web/src/helpers/dark-mode.ts (1)
unraid-ui/src/lib/utils.ts (1)
isDarkModeActive(58-70)
🪛 ast-grep (0.40.0)
unraid-ui/src/composables/useTeleport.test.ts
[warning] 9-9: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: document.body.innerHTML = ''
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
🪛 GitHub Check: CodeQL
unraid-ui/src/lib/utils.test.ts
[notice] 6-6: Unused variable, import, function or class
Unused variable originalDocumentElement.
[notice] 7-7: Unused variable, import, function or class
Unused variable originalBody.
| const ensureVirtualContainer = () => { | ||
| if (!virtualModalContainer) { | ||
| virtualModalContainer = document.createElement('div'); | ||
| virtualModalContainer.id = 'unraid-api-modals-virtual'; | ||
| virtualModalContainer.className = 'unapi'; | ||
| virtualModalContainer.style.position = 'relative'; | ||
| virtualModalContainer.style.zIndex = '999999'; | ||
| document.body.appendChild(virtualModalContainer); | ||
| const existing = document.getElementById('unraid-api-modals-virtual'); | ||
| if (existing) { | ||
| virtualModalContainer = existing as HTMLDivElement; | ||
| } else { | ||
| virtualModalContainer = document.createElement('div'); | ||
| virtualModalContainer.id = 'unraid-api-modals-virtual'; | ||
| virtualModalContainer.className = 'unapi'; | ||
| virtualModalContainer.style.position = 'relative'; | ||
| virtualModalContainer.style.zIndex = '999999'; | ||
| if (isDarkModeActive()) { | ||
| virtualModalContainer.classList.add('dark'); | ||
| } | ||
| document.body.appendChild(virtualModalContainer); | ||
| } | ||
| } | ||
| return virtualModalContainer; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential stale dark mode state after theme switching.
The dark mode check (lines 17-19) only runs when the container is first created. Since virtualModalContainer is a module-scoped singleton that's reused, the dark class won't update if the user switches themes dynamically after the initial mount.
Consider one of these approaches if dynamic theme switching is a requirement:
Option 1: Check and sync dark mode on every call
const ensureVirtualContainer = () => {
if (!virtualModalContainer) {
const existing = document.getElementById('unraid-api-modals-virtual');
if (existing) {
virtualModalContainer = existing as HTMLDivElement;
} else {
virtualModalContainer = document.createElement('div');
virtualModalContainer.id = 'unraid-api-modals-virtual';
virtualModalContainer.className = 'unapi';
virtualModalContainer.style.position = 'relative';
virtualModalContainer.style.zIndex = '999999';
- if (isDarkModeActive()) {
- virtualModalContainer.classList.add('dark');
- }
document.body.appendChild(virtualModalContainer);
}
}
+ // Sync dark mode on every call
+ virtualModalContainer.classList.toggle('dark', isDarkModeActive());
return virtualModalContainer;
};Option 2: Document that theme changes require page reload
If theme switching doesn't need real-time updates, add a comment explaining the limitation.
📝 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.
| const ensureVirtualContainer = () => { | |
| if (!virtualModalContainer) { | |
| virtualModalContainer = document.createElement('div'); | |
| virtualModalContainer.id = 'unraid-api-modals-virtual'; | |
| virtualModalContainer.className = 'unapi'; | |
| virtualModalContainer.style.position = 'relative'; | |
| virtualModalContainer.style.zIndex = '999999'; | |
| document.body.appendChild(virtualModalContainer); | |
| const existing = document.getElementById('unraid-api-modals-virtual'); | |
| if (existing) { | |
| virtualModalContainer = existing as HTMLDivElement; | |
| } else { | |
| virtualModalContainer = document.createElement('div'); | |
| virtualModalContainer.id = 'unraid-api-modals-virtual'; | |
| virtualModalContainer.className = 'unapi'; | |
| virtualModalContainer.style.position = 'relative'; | |
| virtualModalContainer.style.zIndex = '999999'; | |
| if (isDarkModeActive()) { | |
| virtualModalContainer.classList.add('dark'); | |
| } | |
| document.body.appendChild(virtualModalContainer); | |
| } | |
| } | |
| return virtualModalContainer; | |
| }; | |
| const ensureVirtualContainer = () => { | |
| if (!virtualModalContainer) { | |
| const existing = document.getElementById('unraid-api-modals-virtual'); | |
| if (existing) { | |
| virtualModalContainer = existing as HTMLDivElement; | |
| } else { | |
| virtualModalContainer = document.createElement('div'); | |
| virtualModalContainer.id = 'unraid-api-modals-virtual'; | |
| virtualModalContainer.className = 'unapi'; | |
| virtualModalContainer.style.position = 'relative'; | |
| virtualModalContainer.style.zIndex = '999999'; | |
| document.body.appendChild(virtualModalContainer); | |
| } | |
| } | |
| // Sync dark mode on every call | |
| virtualModalContainer.classList.toggle('dark', isDarkModeActive()); | |
| return virtualModalContainer; | |
| }; |
🤖 Prompt for AI Agents
In unraid-ui/src/composables/useTeleport.ts around lines 6 to 24, the
module-scoped virtualModalContainer only gets the 'dark' class when first
created, so theme switches after initial mount leave the class stale; update
ensureVirtualContainer to re-check isDarkModeActive() on every call and add or
remove the 'dark' class accordingly (i.e., if virtualModalContainer exists, call
isDarkModeActive() and ensure classList contains 'dark' when true and does not
when false) so the container stays in sync with theme changes.
| if (isDarkModeActive()) { | ||
| element.classList.add('dark'); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code duplication: Two isDarkModeActive implementations exist.
While this implementation correctly uses isDarkModeActive from @unraid/ui, there's a duplicate implementation in web/src/helpers/dark-mode.ts with slightly different logic:
unraid-ui/src/lib/utils.ts(lines 58-70): checkscssVar === '1'ANDcssVar === '0'web/src/helpers/dark-mode.ts(lines 0-11): only checkscssVar === '1', not'0'
This inconsistency could lead to different behavior depending on which implementation is used.
Consider consolidating to a single implementation. If both packages need this utility, ensure they have identical logic or re-export from a shared location.
// Option 1: Remove web/src/helpers/dark-mode.ts and use @unraid/ui everywhere
import { isDarkModeActive } from '@unraid/ui';
// Option 2: Make web/src/helpers/dark-mode.ts re-export from @unraid/ui
export { isDarkModeActive } from '@unraid/ui';🤖 Prompt for AI Agents
In web/src/components/Wrapper/mount-engine.ts around lines 183-185 there is a
duplicate dark-mode checker usage; the project also defines a slightly different
isDarkModeActive in web/src/helpers/dark-mode.ts which causes inconsistent
behavior — remove the duplicate implementation and consolidate to a single
source: either delete web/src/helpers/dark-mode.ts and update all imports to
import { isDarkModeActive } from '@unraid/ui', or change
web/src/helpers/dark-mode.ts to re-export the function from '@unraid/ui' (export
{ isDarkModeActive } from '@unraid/ui'); then update any local imports, run
typecheck/lint and ensure behavior is unchanged.
web/src/helpers/dark-mode.ts
Outdated
| export const isDarkModeActive = (): boolean => { | ||
| if (typeof document === 'undefined') return false; | ||
|
|
||
| const cssVar = getComputedStyle(document.documentElement).getPropertyValue('--theme-dark-mode').trim(); | ||
| if (cssVar === '1') return true; | ||
|
|
||
| if (document.documentElement.classList.contains('dark')) return true; | ||
| if (document.body?.classList.contains('dark')) return true; | ||
| if (document.querySelector('.unapi.dark')) return true; | ||
|
|
||
| return false; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honor --theme-dark-mode = "0" as an explicit “not dark” override.
Right now, if the CSS variable is "0", the function falls through to class-based checks and can still return true if .dark classes or .unapi.dark are present, even though the variable indicates dark mode is off. To keep the CSS variable as the source of truth (and to match the unraid-ui implementation), add an explicit false branch before the class checks:
const cssVar = getComputedStyle(document.documentElement).getPropertyValue('--theme-dark-mode').trim();
if (cssVar === '1') return true;
+ if (cssVar === '0') return false;📝 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.
| export const isDarkModeActive = (): boolean => { | |
| if (typeof document === 'undefined') return false; | |
| const cssVar = getComputedStyle(document.documentElement).getPropertyValue('--theme-dark-mode').trim(); | |
| if (cssVar === '1') return true; | |
| if (document.documentElement.classList.contains('dark')) return true; | |
| if (document.body?.classList.contains('dark')) return true; | |
| if (document.querySelector('.unapi.dark')) return true; | |
| return false; | |
| }; | |
| export const isDarkModeActive = (): boolean => { | |
| if (typeof document === 'undefined') return false; | |
| const cssVar = getComputedStyle(document.documentElement).getPropertyValue('--theme-dark-mode').trim(); | |
| if (cssVar === '1') return true; | |
| if (cssVar === '0') return false; | |
| if (document.documentElement.classList.contains('dark')) return true; | |
| if (document.body?.classList.contains('dark')) return true; | |
| if (document.querySelector('.unapi.dark')) return true; | |
| return false; | |
| }; |
🤖 Prompt for AI Agents
In web/src/helpers/dark-mode.ts around lines 1 to 12, the CSS variable check
treats only '1' as true but does not treat '0' as an explicit false, allowing
class-based checks to override the variable; change the logic so after reading
and trimming --theme-dark-mode you return true if it's '1' and return false if
it's '0' before running any class-based checks, otherwise fall back to existing
class and selector checks.
| // Lazy query - only executes when explicitly called | ||
| const { load, onResult, onError } = useLazyQuery<GetThemeQuery>(GET_THEME_QUERY, null, { | ||
| fetchPolicy: 'cache-and-network', | ||
| nextFetchPolicy: 'cache-first', | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# First, let's see the structure of the theme.ts file
echo "=== Checking theme.ts file structure ==="
wc -l web/src/store/theme.ts
echo ""
echo "=== Context around lines 100-104 ==="
sed -n '90,115n' web/src/store/theme.ts
echo ""
echo "=== Context around lines 208-210 ==="
sed -n '200,220n' web/src/store/theme.tsRepository: unraid/api
Length of output: 197
🏁 Script executed:
#!/bin/bash
# Search for fetchTheme calls in the entire codebase
echo "=== Searching for fetchTheme calls ==="
rg "fetchTheme" --type ts --type tsx -C 3
echo ""
echo "=== Searching for useThemeStore().fetch or useThemeStore().load ==="
rg "useThemeStore\(\)\s*\.\s*(fetch|load)" --type ts --type tsx -C 2Repository: unraid/api
Length of output: 234
🏁 Script executed:
#!/bin/bash
# Search for direct theme store imports and method calls
echo "=== Looking for theme store usage patterns ==="
rg "useThemeStore|themeStore" --type ts --type tsx -C 2 | head -100Repository: unraid/api
Length of output: 145
🏁 Script executed:
#!/bin/bash
# Read the theme.ts file
echo "=== Reading web/src/store/theme.ts ==="
cat -n web/src/store/theme.tsRepository: unraid/api
Length of output: 9046
🏁 Script executed:
#!/bin/bash
# Search for fetchTheme without type restrictions
echo "=== Searching for fetchTheme ==="
rg "fetchTheme" --context 3
echo ""
echo "=== Searching for useThemeStore calls ==="
rg "useThemeStore" --context 2 | head -80Repository: unraid/api
Length of output: 5390
🏁 Script executed:
#!/bin/bash
# Search specifically for fetchTheme() calls (with parentheses/invocations)
echo "=== Searching for fetchTheme() invocations ==="
rg "\.fetchTheme\(" --context 3
echo ""
echo "=== Searching for fetchTheme with store usage ==="
rg "fetchTheme" -C 2
echo ""
echo "=== Checking for app initialization, plugins, setup ==="
rg "useThemeStore" web/src -C 3 | grep -v "__test__" | head -150Repository: unraid/api
Length of output: 10534
🏁 Script executed:
#!/bin/bash
# Read the themeInitializer.ts file
echo "=== Reading web/src/store/themeInitializer.ts ==="
cat -n web/src/store/themeInitializer.tsRepository: unraid/api
Length of output: 2399
🏁 Script executed:
#!/bin/bash
# Search for initializeTheme calls to see if themeInitializer is even used
echo "=== Searching for initializeTheme calls ==="
rg "initializeTheme" --context 3
echo ""
echo "=== Verifying setTheme() logic from theme.ts lines 179-202 ==="
sed -n '179,202p' web/src/store/theme.tsRepository: unraid/api
Length of output: 4325
The lazy query won't execute: fetchTheme() is never invoked anywhere in the codebase.
The useLazyQuery at lines 100-104 requires load() to be called to execute. While fetchTheme() wraps this call and is exported from the store, it's never actually invoked. The theme initialization in web/src/components/Wrapper/auto-mount.ts calls initializeTheme(), which in turn calls setTheme() without arguments—a no-op that returns early without triggering the GraphQL query. This means server-provided theme values are never fetched; the store remains at defaults with only CSS-based theme values available. Either call fetchTheme() during initialization, or clarify if server theme fetching is intentionally disabled.
🤖 Prompt for AI Agents
In web/src/store/theme.ts around lines 100-104: the useLazyQuery returns load()
(wrapped by fetchTheme) but fetchTheme() is never called, so the server theme
query never runs; update the app initialization path (e.g., in
web/src/components/Wrapper/auto-mount.ts where initializeTheme() is called) to
invoke fetchTheme() after store setup (or call load() directly) so the GraphQL
GET_THEME_QUERY actually executes; ensure you handle the Promise/result via
onResult/onError and preserve the early-return behavior of setTheme() when no
payload is present.
- Added a mock for dark mode detection in theme store tests, improving the accuracy of dark mode state verification. - Refactored test setup to ensure proper initialization and cleanup of dark mode classes and CSS variables. - Updated tests to check for reactive updates to dark mode based on theme changes, enhancing test coverage and reliability. These changes improve the robustness of the theme store tests, ensuring consistent behavior in dark mode handling.
- Modified the banner gradient test to use a resolved CSS value instead of a nested variable, ensuring accurate rendering in tests. - Updated expectations to reflect the new gradient format, enhancing the reliability of theme store tests. These changes improve the clarity and correctness of the theme store's banner gradient functionality in tests.
|
🚀 Storybook has been deployed to staging: https://unraid-ui-storybook-staging.unraid-workers.workers.dev |
|
This plugin has been deployed to Cloudflare R2 and is available for testing. |
| const expires = new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toUTCString(); | ||
| document.cookie = `${THEME_COOKIE_KEY}=${encodeURIComponent(themeName)}; path=/; SameSite=Lax; expires=${expires}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm assuming this sets expiration to 1 yr from now, but a comment would be nice!
| const syncDomForTheme = (themeName: ThemeName) => { | ||
| const root = document.documentElement; | ||
| const isDark = DARK_UI_THEMES.includes(themeName as (typeof DARK_UI_THEMES)[number]); | ||
| const method: 'add' | 'remove' = isDark ? 'add' : 'remove'; | ||
| root.style.setProperty('--theme-name', themeName); | ||
| root.style.setProperty('--theme-dark-mode', isDark ? '1' : '0'); | ||
| root.setAttribute('data-theme', themeName); | ||
| root.classList[method]('dark'); | ||
| document.body?.classList[method]('dark'); | ||
| document.querySelectorAll('.unapi').forEach((el) => el.classList[method]('dark')); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
generally speaking, this is a lot of things we're having to change to set dark mode
| $theme = strtolower(trim($display['theme'] ?? '')); | ||
| $darkThemes = ['gray', 'black']; | ||
| $isDarkMode = in_array($theme, $darkThemes, true); | ||
| $vars['--theme-dark-mode'] = $isDarkMode ? '1' : '0'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i get antipattern/red flag vibes from this (ie multiple ways to set dark mode, multiple places you have to consider to debug themes--which could step on each others' toes)
i suspect this is in the spirit of a css implementation of the dark mode recognition we were doing in js, though.
for my own context, what are the limitations of purely setting a css var palette based on theme? is it the use of dark: styles in our component code & component dependencies?
This improves user experience by ensuring the correct theme is applied based on user preferences.
Summary by CodeRabbit
Release Notes
New Features
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.