feat: Twitter-style UI theme with custom theme override system#93
feat: Twitter-style UI theme with custom theme override system#93leonvanzyl merged 3 commits intoAutoForgeAI:masterfrom
Conversation
Create custom-theme.css for theme overrides that won't conflict with upstream updates. The file loads after globals.css, so its CSS variables take precedence. This approach ensures: - Zero merge conflicts on git pull (new file, not in upstream) - Theme persists across upstream updates - Easy to modify without touching upstream code Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
UI Changes: - Replace neobrutalism with clean Twitter/Supabase-style design - Remove all shadows, use thin borders (1px) - Single accent color (Twitter blue) for all status indicators - Rounded corners (1.3rem base) - Fix dark mode contrast and visibility - Make KanbanColumn themeable via CSS classes Backend Changes: - Default Playwright browser changed to Firefox (lower CPU) - Default Playwright mode changed to headless (saves resources) - Add PLAYWRIGHT_BROWSER env var support Documentation: - Add CUSTOM_UPDATES.md with all customizations documented - Update .env.example with new Playwright options Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughUpdates Playwright defaults and helpers (default browser Firefox, headless true), adds robust SQLite connection and a DB-health endpoint, introduces a Twitter-style global theme CSS and Kanban column class-based coloring, and expands documentation and revert/update checklists. Changes
Sequence Diagram(s)mermaid Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
client.py (1)
46-55: Handle invalidPLAYWRIGHT_HEADLESSvalues explicitly.Right now any unexpected value silently resolves to
False, which can surprise users and flip behavior. Consider validating and falling back to the default with a warning.🔧 Suggested validation
- value = os.getenv("PLAYWRIGHT_HEADLESS", str(DEFAULT_PLAYWRIGHT_HEADLESS).lower()).lower() - # Accept various truthy/falsy values - return value in ("true", "1", "yes", "on") + value = os.getenv("PLAYWRIGHT_HEADLESS", str(DEFAULT_PLAYWRIGHT_HEADLESS).lower()).strip().lower() + truthy = {"true", "1", "yes", "on"} + falsy = {"false", "0", "no", "off"} + if value not in truthy | falsy: + print(f" - Warning: Invalid PLAYWRIGHT_HEADLESS='{value}', defaulting to {DEFAULT_PLAYWRIGHT_HEADLESS}") + return DEFAULT_PLAYWRIGHT_HEADLESS + return value in truthy
🤖 Fix all issues with AI agents
In `@client.py`:
- Around line 28-31: The get_playwright_browser() function should validate the
environment value against an allowlist of MCP-supported browsers ("chrome",
"firefox", "webkit", "msedge"); if the env var is missing or not in that set,
log a warning and return the DEFAULT_PLAYWRIGHT_BROWSER constant instead of
passing the invalid value to `@playwright/mcp`. Locate get_playwright_browser and
DEFAULT_PLAYWRIGHT_BROWSER in the file, implement the allowlist check, emit a
clear warning when falling back, and ensure only allowed values are returned to
the caller.
In `@ui/src/styles/custom-theme.css`:
- Around line 87-89: The global rule that strips box-shadow for all elements ("*
{ box-shadow: none !important; }") removes focus rings and, together with
removed outlines on inputs, breaks keyboard focus visibility; restore accessible
focus indicators by removing the global override and instead add explicit
:focus-visible rules for interactive elements (e.g., input, button, a,
.your-focusable-class) to apply a visible outline or box-shadow (and ensure
inputs retain their outline on :focus-visible); locate the "*" rule and the
input outline removal and replace them with scoped :focus-visible styling to
preserve keyboard focus while keeping visual design for non-focused states.
- Around line 269-279: The rule is incorrectly blanket-disabling all translate
utilities ([class*="translate-x"], [class*="translate-y"]) which breaks layouts;
remove those selectors from the global rule and only keep the hover-specific
selectors ([class*="hover:translate"], [class*="hover:-translate"]) so you still
neutralize hover-based neo effects but do not zero out core translate utilities
like -translate-x-1/2 used for centering.
🧹 Nitpick comments (2)
client.py (1)
247-257: Avoid double-reading headless configuration.Read once so the logged value always matches the applied value and avoids redundant env parsing.
♻️ Suggested refactor
- browser = get_playwright_browser() + browser = get_playwright_browser() + headless = get_playwright_headless() playwright_args = [ "@playwright/mcp@latest", "--viewport-size", "1280x720", "--browser", browser, ] - if get_playwright_headless(): + if headless: playwright_args.append("--headless") - print(f" - Browser: {browser} (headless={get_playwright_headless()})") + print(f" - Browser: {browser} (headless={headless})")CUSTOM_UPDATES.md (1)
270-275: Add UI lint step to the update checklist.The checklist currently includes
npm run buildbut omitsnpm run lint, which is part of the UI workflow.📘 Suggested doc update
### UI Changes - [ ] `ui/src/styles/custom-theme.css` is preserved - [ ] `ui/src/components/KanbanColumn.tsx` changes are preserved +- [ ] Run `npm run lint` in `ui/` directory - [ ] Run `npm run build` in `ui/` directory - [ ] Test both light and dark modesAs per coding guidelines, ...
| # Default browser for Playwright - can be overridden via PLAYWRIGHT_BROWSER env var | ||
| # Options: chrome, firefox, webkit, msedge | ||
| # Firefox is recommended for lower CPU usage | ||
| DEFAULT_PLAYWRIGHT_BROWSER = "firefox" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's find and read the client.py file to see the current implementation
find . -name "client.py" -type f | head -20Repository: leonvanzyl/autocoder
Length of output: 75
🏁 Script executed:
# Also search for any existing validation or usage of PLAYWRIGHT_BROWSER
rg "PLAYWRIGHT_BROWSER|get_playwright_browser" --type pyRepository: leonvanzyl/autocoder
Length of output: 482
🌐 Web query:
@playwright/mcp CLI documentation supported browser values --browser parameter
💡 Result:
For the @playwright/mcp CLI, the --browser <browser> option supports these values: chrome, firefox, webkit, msedge. [1]
(Example in Playwright’s MCP docs: --browser msedge.) [2]
Sources: [1][2]
🏁 Script executed:
cat -n client.py | head -100Repository: leonvanzyl/autocoder
Length of output: 4572
🏁 Script executed:
# Check how the browser variable is actually used after get_playwright_browser()
rg -A 5 "get_playwright_browser()" --type pyRepository: leonvanzyl/autocoder
Length of output: 593
🏁 Script executed:
rg -B 5 -A 10 "playwright_args = \[" --type pyRepository: leonvanzyl/autocoder
Length of output: 910
🏁 Script executed:
# Check if there's any error handling or try-catch around MCP invocation
rg -B 3 -A 3 "@playwright/mcp" --type pyRepository: leonvanzyl/autocoder
Length of output: 404
Add validation to get_playwright_browser() to prevent MCP startup failures.
The function currently passes any value to @playwright/mcp CLI without validation. Invalid browser values cause hard-fail at startup. Add an allowlist matching MCP's supported values (chrome, firefox, webkit, msedge) with a warning and fallback to the default.
🔧 Suggested validation
+# Allowed Playwright MCP browser values
+ALLOWED_PLAYWRIGHT_BROWSERS = {"chrome", "firefox", "webkit", "msedge"}
+
def get_playwright_browser() -> str:
"""
Get the browser to use for Playwright.
Reads from PLAYWRIGHT_BROWSER environment variable, defaults to firefox.
Options: chrome, firefox, webkit, msedge
Firefox is recommended for lower CPU usage.
"""
- return os.getenv("PLAYWRIGHT_BROWSER", DEFAULT_PLAYWRIGHT_BROWSER).lower()
+ browser = os.getenv("PLAYWRIGHT_BROWSER", DEFAULT_PLAYWRIGHT_BROWSER).strip().lower()
+ if browser not in ALLOWED_PLAYWRIGHT_BROWSERS:
+ print(f" - Warning: Invalid PLAYWRIGHT_BROWSER='{browser}', defaulting to {DEFAULT_PLAYWRIGHT_BROWSER}")
+ return DEFAULT_PLAYWRIGHT_BROWSER
+ return browser🤖 Prompt for AI Agents
In `@client.py` around lines 28 - 31, The get_playwright_browser() function should
validate the environment value against an allowlist of MCP-supported browsers
("chrome", "firefox", "webkit", "msedge"); if the env var is missing or not in
that set, log a warning and return the DEFAULT_PLAYWRIGHT_BROWSER constant
instead of passing the invalid value to `@playwright/mcp`. Locate
get_playwright_browser and DEFAULT_PLAYWRIGHT_BROWSER in the file, implement the
allowlist check, emit a clear warning when falling back, and ensure only allowed
values are returned to the caller.
| * { | ||
| box-shadow: none !important; | ||
| } |
There was a problem hiding this comment.
Restore visible focus indicators after shadow/outline removal.
Line 87-89 removes box-shadows globally (often used for focus rings), and Line 172 removes outlines on inputs. This can eliminate keyboard focus visibility across the UI, which is an accessibility blocker. Add explicit :focus-visible styles (or avoid removing outlines) to preserve focus states.
🛠️ Suggested fix
* {
box-shadow: none !important;
}
+
+*:focus-visible {
+ outline: 2px solid var(--color-neo-accent);
+ outline-offset: 2px;
+}
@@
.neo-input:focus,
.neo-textarea:focus,
input:focus,
textarea:focus,
select:focus {
box-shadow: none !important;
border-color: var(--color-neo-accent) !important;
- outline: none !important;
+ outline: 2px solid var(--color-neo-accent) !important;
+ outline-offset: 2px;
}Also applies to: 165-173
🤖 Prompt for AI Agents
In `@ui/src/styles/custom-theme.css` around lines 87 - 89, The global rule that
strips box-shadow for all elements ("* { box-shadow: none !important; }")
removes focus rings and, together with removed outlines on inputs, breaks
keyboard focus visibility; restore accessible focus indicators by removing the
global override and instead add explicit :focus-visible rules for interactive
elements (e.g., input, button, a, .your-focusable-class) to apply a visible
outline or box-shadow (and ensure inputs retain their outline on
:focus-visible); locate the "*" rule and the input outline removal and replace
them with scoped :focus-visible styling to preserve keyboard focus while keeping
visual design for non-focused states.
| /* ===== REMOVE NEO EFFECTS ===== */ | ||
| [class*="shadow-neo"], | ||
| [class*="shadow-"] { | ||
| box-shadow: none !important; | ||
| } | ||
|
|
||
| [class*="hover:translate"], | ||
| [class*="hover:-translate"], | ||
| [class*="translate-x"], | ||
| [class*="translate-y"] { | ||
| transform: none !important; |
There was a problem hiding this comment.
Don’t blanket-disable translate-x/y utilities.
This will break layout patterns that rely on translate utilities (e.g., centering with -translate-x-1/2). Keep the hover-only overrides but don’t zero out all translate classes.
🛠️ Suggested fix
[class*="hover:translate"],
-[class*="hover:-translate"],
-[class*="translate-x"],
-[class*="translate-y"] {
+[class*="hover:-translate"] {
transform: none !important;
}🤖 Prompt for AI Agents
In `@ui/src/styles/custom-theme.css` around lines 269 - 279, The rule is
incorrectly blanket-disabling all translate utilities ([class*="translate-x"],
[class*="translate-y"]) which breaks layouts; remove those selectors from the
global rule and only keep the hover-specific selectors
([class*="hover:translate"], [class*="hover:-translate"]) so you still
neutralize hover-based neo effects but do not zero out core translate utilities
like -translate-x-1/2 used for centering.
Add focus-visible styles for keyboard navigation accessibility and improve PLAYWRIGHT_HEADLESS environment variable validation to warn users about invalid values instead of silently defaulting. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Thanks |
- Remove translate-x/translate-y CSS selectors that broke layout utilities (AssistantPanel slide animation, DebugLogViewer resize handle) - Add browser validation to get_playwright_browser() with warning for invalid values (matches get_playwright_headless() behavior) - Remove phantom SQLite documentation from CUSTOM_UPDATES.md that described features not present in PR #93 - Update checklist and revert instructions to match actual changes Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…I#93 - Created tests/test_graph_cycle_protection.py with 33 unit tests: - TestResolveDependenciesKahnsAlgorithm: Verifies Kahn's algo cycle detection - TestDetectCyclesVisitedTracking: Verifies visited/rec_stack sets - TestComputeSchedulingScoresQueuedTracking: Verifies BFS visited tracking - TestWouldCreateCircularDependencyVisited: Verifies DFS visited set - TestIterationLimitsLogging: Verifies logging on limit exceeded - TestCycleProtectionCodeAudit: Static analysis of protection mechanisms - TestEdgeCases: Empty lists, None deps, missing keys - Created tests/verify_feature_93.py verification script All 5 verification steps pass: 1. resolve_dependencies() uses in_degree tracking (Kahn's algo) 2. _detect_cycles() uses visited + rec_stack sets 3. compute_scheduling_scores() uses visited set + iteration limit 4. would_create_circular_dependency() uses visited set + depth limit 5. All functions have iteration/depth limits Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
feat: Twitter-style UI theme with custom theme override system
- Remove translate-x/translate-y CSS selectors that broke layout utilities (AssistantPanel slide animation, DebugLogViewer resize handle) - Add browser validation to get_playwright_browser() with warning for invalid values (matches get_playwright_headless() behavior) - Remove phantom SQLite documentation from CUSTOM_UPDATES.md that described features not present in PR AutoForgeAI#93 - Update checklist and revert instructions to match actual changes Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Summary
A fresh, modern UI theme inspired by Twitter/X design language, plus a flexible theme override system that allows users to customize the look without modifying core files.
Features
Twitter-style UI Theme
Custom Theme Override System
ui/src/styles/custom-theme.cssfor user customizationsDocumentation
CUSTOM_UPDATES.mdwith theme customization guideScreenshots
The new theme has received positive feedback from users who prefer a cleaner, more modern look.
Test plan
npm run devin ui/ directory🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Style
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.