Skip to content

fix(platform): improve branding settings UX and dark mode support#497

Merged
Israeltheminer merged 3 commits into
mainfrom
fix/branding-improvements
Feb 19, 2026
Merged

fix(platform): improve branding settings UX and dark mode support#497
Israeltheminer merged 3 commits into
mainfrom
fix/branding-improvements

Conversation

@Israeltheminer

@Israeltheminer Israeltheminer commented Feb 19, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Fix color picker registering unchanged values as dirty by normalizing hex case comparison
  • Add support for 8-character hex color codes with alpha channel (#RRGGBBAA)
  • Track image uploads (logo, favicons) via React Hook Form state instead of refs, enabling proper dirty detection and save button behavior
  • Support image deletion by using null storage IDs in the Convex mutation
  • Add dark mode support to branding preview by replacing hardcoded colors with semantic tokens (bg-background, text-foreground, border-border, etc.)
  • Show app name as page title and favicon in the browser chrome preview
  • Use accent color for tab nav active indicator; brand color only for text logo/icon
  • Reorder settings navigation: Branding after API keys, Account last
  • Accept .ico files for favicon uploads
  • Add Figma capture script for local development

Test plan

  • Verify color picker does not mark form dirty when re-entering the same color
  • Test 8-char hex codes (e.g., #3B34FE1A) save and display correctly
  • Upload a logo and confirm save button enables
  • Delete a logo/favicon and confirm it persists after save
  • Toggle dark mode and confirm branding preview renders correctly
  • Verify app name and favicon appear in the browser chrome area of preview
  • Confirm accent color applies to tab nav, brand color to logo only
  • Check settings nav order: Organization, Teams, Integrations, API keys, Branding, Account
  • Upload a .ico file as favicon

Summary by CodeRabbit

Release Notes

  • New Features

    • Added favicon upload and preview support with separate light and dark variants
    • Hex color picker now supports 8-character codes with alpha transparency
  • Style

    • Enhanced branding preview styling with updated color tokens and visual improvements

- Fix color picker dirty state by normalizing hex case comparison
- Support 8-character hex colors with alpha channel (#RRGGBBAA)
- Track image uploads via form state for proper dirty detection
- Support image deletion with null storage ID handling
- Add dark mode support to branding preview with semantic tokens
- Show app name and favicon in browser chrome preview
- Use accent color for tab nav indicator, brand color for logo only
- Reorder settings nav: Branding after API keys, Account last
- Accept .ico files for favicon uploads
- Add Figma capture script for local development
@greptile-apps

greptile-apps Bot commented Feb 19, 2026

Copy link
Copy Markdown

Greptile Summary

This PR enhances the branding settings UX with better form state management and dark mode support. Key improvements include:

  • Color picker normalization: Hex values are now case-insensitive compared before triggering dirty state, preventing false positives when re-entering the same color
  • 8-character hex support: Added alpha channel support for colors (e.g., #3B34FE1A)
  • Image state management: Refactored from refs to React Hook Form state using setValue, enabling proper dirty detection and save button behavior
  • Image deletion: Empty string in form state converts to null which then becomes undefined in the database, properly removing images
  • Dark mode preview: Replaced hardcoded colors with semantic tokens (bg-background, text-foreground, etc.)
  • Browser chrome preview: Shows app name and favicon in the preview
  • Accent color usage: Tab nav now uses accent color for active indicator; brand color only for logo
  • Settings nav reorder: Branding moved after API keys, Account moved to last
  • .ico file support: Added for favicon uploads

Issue found: logoPreviewUrlRef and faviconPreviewUrlRef are declared but never updated, so the preview won't show newly uploaded images until after save/refresh

Confidence Score: 3/5

  • Safe to merge with one functional issue that affects UX but not data integrity
  • The changes are well-structured and most functionality works correctly. The color picker normalization logic is solid, dark mode implementation uses proper semantic tokens, and the image deletion flow properly converts null values. However, the preview URL refs issue means uploaded images won't appear in the preview until after saving, which impacts the user experience
  • Pay attention to services/platform/app/features/settings/branding/components/branding-form.tsx - the preview refs need to be wired up to actually update when images are uploaded

Important Files Changed

Filename Overview
services/platform/app/features/settings/branding/components/color-picker-input.tsx Added hex normalization to prevent false dirty state and support for 8-char hex codes with alpha channel
services/platform/app/features/settings/branding/components/branding-form.tsx Refactored to use React Hook Form state instead of refs; preview URL refs declared but never updated
services/platform/app/features/settings/branding/components/branding-preview.tsx Added browser chrome with app name and favicon; replaced hardcoded colors with semantic tokens for dark mode
services/platform/convex/branding/mutations.ts Added null support for storage IDs to enable deletion; properly converts null to undefined for database

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User uploads image] --> B[ImageUploadField]
    B --> C[Create object URL for preview]
    B --> D[Upload to Convex storage]
    D --> E[Get storageId]
    E --> F[Call onUpload with storageId]
    F --> G[BrandingForm: setValue with storageId]
    G --> H{User clicks Save}
    H --> I[toStorageId converts string to Id]
    I --> J[upsertBranding mutation]
    J --> K{Storage ID is null?}
    K -->|Yes| L[Convert null to undefined]
    K -->|No| M[Delete old storage file if exists]
    L --> N[Update database]
    M --> N
    N --> O[Form reset with new values]
    
    P[User removes image] --> Q[ImageUploadField: onRemove]
    Q --> R[BrandingForm: setValue with empty string]
    R --> H
Loading

Last reviewed commit: 7795f3d

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

8 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment thread services/platform/app/features/settings/branding/components/branding-form.tsx Outdated
@Israeltheminer Israeltheminer merged commit 85c4ecd into main Feb 19, 2026
16 of 17 checks passed
@Israeltheminer Israeltheminer deleted the fix/branding-improvements branch February 19, 2026 16:41
@coderabbitai

coderabbitai Bot commented Feb 19, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This pull request enhances the branding settings feature by adding favicon support across the component stack. Changes include updating BrandingPreview and BrowserChrome components to accept and display favicon URLs, extending the color picker to support 8-character hex codes with alpha channel transparency, and refactoring form state management to use useState instead of useRef. Backend updates include Convex schema and mutation extensions to handle nullable favicon storage IDs (light and dark variants) with revised deletion logic. Tests are updated to reflect new functionality with accentColor assertions and placeholder bar assertions. A development-only Figma MCP script is conditionally injected in index.html, and settings navigation tab ordering is adjusted.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: UX improvements (image upload handling, hex color normalization, color picker fixes) and dark mode support (semantic color tokens throughout the preview).

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

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/branding-improvements

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@services/platform/index.html`:
- Around line 37-47: The script auto-inject currently checks location.hostname
and uses var; change it to require an explicit opt-in via a query parameter
(e.g., check location.search for a token like "enableFigmaCapture=true" or a
short flag) before creating/loading the third‑party capture script, and replace
the use of `var s` with `const s`; in other words, update the conditional that
checks `location.hostname === 'localhost' || location.hostname === '127.0.0.1'`
to also require `location.search.includes('enableFigmaCapture')` (or similar
flag), then create `const s = document.createElement('script')`, set s.src to
'https://mcp.figma.com/mcp/html-to-design/capture.js', set s.async = true, and
append only when the opt‑in flag is present.

Comment on lines +37 to +47
<script>
if (
location.hostname === 'localhost' ||
location.hostname === '127.0.0.1'
) {
var s = document.createElement('script');
s.src = 'https://mcp.figma.com/mcp/html-to-design/capture.js';
s.async = true;
document.head.appendChild(s);
}
</script>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify if there is an existing dev/prod flag to use for gating (e.g., window.__ENV__).
rg -n "__ENV__" -S
rg -n "NODE_ENV|MODE|development|prod" -S

# Check for any CSP configuration that might affect inline scripts.
rg -n "Content-Security-Policy|CSP" -S

Repository: tale-project/tale

Length of output: 50374


🏁 Script executed:

# Check the env utility to understand how window.__ENV__ is used
head -50 services/platform/lib/env.ts

# Check the inject-env plugin to see what gets exposed
head -60 services/platform/vite-plugins/inject-env.ts

# Check full context around the Figma script in index.html
head -60 services/platform/index.html

# Look for any existing isDev or development flags
rg -n "isDev|isDevelopment|development.*true" services/platform/lib/env.ts -A 2
rg -n "MODE.*development|ENV.*dev" services/platform/vite-plugins/inject-env.ts -A 2

Repository: tale-project/tale

Length of output: 4061


Gate the Figma capture script behind explicit opt‑in on localhost.

Auto-injecting a third‑party capture script whenever the hostname is localhost or 127.0.0.1 can unintentionally exfiltrate sensitive local data during development (e.g., when testing with real data). Add an explicit opt‑in via query parameter so the script only loads when a developer intentionally enables it.

Also replace var with const for consistency.

🔧 Proposed fix (query param gate + const)
-    if (
-      location.hostname === 'localhost' ||
-      location.hostname === '127.0.0.1'
-    ) {
-      var s = document.createElement('script');
-      s.src = 'https://mcp.figma.com/mcp/html-to-design/capture.js';
-      s.async = true;
-      document.head.appendChild(s);
-    }
+    const enableFigmaCapture =
+      (location.hostname === 'localhost' ||
+        location.hostname === '127.0.0.1') &&
+      new URLSearchParams(location.search).has('figmaCapture');
+
+    if (enableFigmaCapture) {
+      const s = document.createElement('script');
+      s.src = 'https://mcp.figma.com/mcp/html-to-design/capture.js';
+      s.async = true;
+      document.head.appendChild(s);
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/platform/index.html` around lines 37 - 47, The script auto-inject
currently checks location.hostname and uses var; change it to require an
explicit opt-in via a query parameter (e.g., check location.search for a token
like "enableFigmaCapture=true" or a short flag) before creating/loading the
third‑party capture script, and replace the use of `var s` with `const s`; in
other words, update the conditional that checks `location.hostname ===
'localhost' || location.hostname === '127.0.0.1'` to also require
`location.search.includes('enableFigmaCapture')` (or similar flag), then create
`const s = document.createElement('script')`, set s.src to
'https://mcp.figma.com/mcp/html-to-design/capture.js', set s.async = true, and
append only when the opt‑in flag is present.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant