[eric] theme: stock wallpaper is a designed contrasting gradient (canvas + shell + onboarding stage), never flat white when no accent picked

This commit is contained in:
ciregenz
2026-07-28 18:16:01 -07:00
parent 70eb3cd846
commit 224dc04696
4 changed files with 16 additions and 5 deletions
@@ -38,7 +38,7 @@ import { findBrowserByWebContentsId } from '@/shared/browserRegistry';
import { byPreviewRecency } from '@/shared/previewOrder';
import { useClaudeTokens, useThemeAccent, useThemeWash } from '@/shared/styles/ThemeContext';
import SpacesStrip from '@/app/pages/Dashboard/desktop/SpacesStrip';
import { washBackgroundUrl } from '@/shared/styles/washBackground';
import { washBackgroundUrl, effectiveWashStops } from '@/shared/styles/washBackground';
import { ErrorSlime } from '@/app/components/feedback/ErrorSlime';
const UPDATE_DISMISS_KEY = 'openswarm-update-dismissed';
@@ -96,7 +96,7 @@ const AppShell: React.FC = () => {
// the content floats as a rounded card). Mirrors the DashboardCanvas wash formula.
const { accent: themeAccent, gradient: themeGradient } = useThemeAccent();
const { washOpacity: themeWashOpacity } = useThemeWash();
const fsWashStops = themeGradient ?? (themeAccent ? [themeAccent] : null);
const fsWashStops = effectiveWashStops(themeGradient, themeAccent);
// During an active free trial the user CAN run things, so a red "no model connected" warning is misleading and discouraging (it sits right above the working starter chips). The trial flips connection_mode back to own_key the moment it's spent, so this banner returns then, landing the connect-a-model nudge after the win, not before it.
const freeTrialActive = useAppSelector((s) => {
const d = s.settings.data as any;
@@ -1,4 +1,5 @@
import React, { useEffect, useState } from 'react';
import { effectiveWashStops } from '@/shared/styles/washBackground';
import { motion } from 'framer-motion';
import { ArrowLeft } from 'lucide-react';
import { useThemeAccent, useThemeWash } from '@/shared/styles/ThemeContext';
@@ -53,7 +54,7 @@ const BeatShell: React.FC<{
// Once the user has picked stops the stage wears them (our theme beat repaints live); before that it stays Arc-mauve.
const { accent, gradient } = useThemeAccent();
const { washOpacity, grain } = useThemeWash();
const stops = gradient ?? (accent ? [accent] : null);
const stops = effectiveWashStops(gradient, accent);
// Picked color reads VIVID on the stage (alpha floor over near-white), not muddied into the mauve.
const washAlpha = Math.round(Math.max(0.5, washOpacity) * 255).toString(16).padStart(2, '0');
const stageBg = stageDark
@@ -14,7 +14,7 @@ import ApplicationsWindow from '../desktop/ApplicationsWindow';
import type { ClaudeTokens } from '@/shared/styles/claudeTokens';
import { useThemeAccent, useThemeWash } from '@/shared/styles/ThemeContext';
import { GRAIN_URL } from '@/shared/styles/grainTexture';
import { washBackgroundUrl } from '@/shared/styles/washBackground';
import { washBackgroundUrl, effectiveWashStops } from '@/shared/styles/washBackground';
import type { AgentSession } from '@/shared/state/agentsSlice';
import type {
CardPosition,
@@ -168,7 +168,7 @@ const DashboardCanvas: React.FC<DashboardCanvasProps> = ({
const { accent, gradient } = useThemeAccent();
const { washOpacity, grain } = useThemeWash();
// A single picked color stores gradient=null, so fall back to the accent (mirrors BeatShell).
const washStops = gradient ?? (accent ? [accent] : null);
const washStops = effectiveWashStops(gradient, accent);
const dotSize = Math.max(1, 1.5 * canvas.zoom);
const dotSpacing = 24 * canvas.zoom;
@@ -12,3 +12,13 @@ export function washBackgroundUrl(stops: string[], washOpacity: number): string
const svg = `<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' preserveAspectRatio='none'><defs><linearGradient id='w' x1='0%' y1='0%' x2='90%' y2='42%'>${stopEls}</linearGradient></defs><rect width='100' height='100' fill='url(%23w)'/></svg>`;
return `url("data:image/svg+xml,${svg.replace(/#/g, '%23').replace(/'/g, '%27')}")`;
}
// Stock wallpaper when the user hasn't picked an accent yet: a designed blue-to-cream-to-pink
// gradient (contrasting stops), so a fresh install and the onboarding stage never look flat/white.
export const DEFAULT_WASH_STOPS = ['#B7CDEA', '#EFE0D2', '#E7BDD1'];
export function effectiveWashStops(gradient: string[] | null, accent: string | null): string[] {
if (gradient && gradient.length > 0) return gradient;
if (accent) return [accent];
return DEFAULT_WASH_STOPS;
}