From a205edf6f9075097a57f9fb8ba5c67122aede1a5 Mon Sep 17 00:00:00 2001 From: Erdem Yerebasmaz Date: Thu, 5 Mar 2026 13:28:44 +0300 Subject: [PATCH 01/13] feat: centralize brand configuration for white-label support Create src/config/brand.ts with all brand values (name, logo, tagline, icons, domains, log prefixes). Replace hardcoded brand strings across 15 files with brand.* imports. To white-label the app, only brand.ts and static assets need to change. Co-Authored-By: Claude Opus 4.6 --- src/components/InstallPrompt.tsx | 3 ++- src/components/PaymentReceivedCelebration.tsx | 5 +++-- src/components/SideMenu.tsx | 11 ++++++----- src/components/layout/PageLayout.tsx | 3 ++- src/config/brand.ts | 18 ++++++++++++++++++ .../receive/LightningAddressDisplay.tsx | 3 ++- src/features/receive/ReceivePaymentDialog.tsx | 3 ++- .../receive/hooks/useLightningAddress.ts | 5 +++-- src/features/send/steps/ProcessingStep.tsx | 3 ++- src/features/send/steps/ResultStep.tsx | 5 +++-- src/pages/GeneratePage.tsx | 3 ++- src/pages/HomePage.tsx | 9 +++++---- src/pages/SettingsPage.tsx | 3 ++- src/services/logExport.ts | 13 +++++++------ src/services/logStorage.ts | 4 +++- 15 files changed, 62 insertions(+), 29 deletions(-) create mode 100644 src/config/brand.ts diff --git a/src/components/InstallPrompt.tsx b/src/components/InstallPrompt.tsx index 8d4dea0b..af437741 100644 --- a/src/components/InstallPrompt.tsx +++ b/src/components/InstallPrompt.tsx @@ -1,6 +1,7 @@ import React, { useState, useEffect } from 'react'; import { Transition } from '@headlessui/react'; import { DownloadIcon, CloseIcon } from './Icons'; +import { brand } from '../config/brand'; const INSTALL_PROMPT_DISMISSED_KEY = 'install_prompt_dismissed'; @@ -94,7 +95,7 @@ const InstallPrompt: React.FC = ({ onClose }) => {

- Install Glow + {`Install ${brand.name}`}

Add to your home screen for quick access and a better experience. diff --git a/src/components/PaymentReceivedCelebration.tsx b/src/components/PaymentReceivedCelebration.tsx index fd74e21f..33fdcbb3 100644 --- a/src/components/PaymentReceivedCelebration.tsx +++ b/src/components/PaymentReceivedCelebration.tsx @@ -1,5 +1,6 @@ import React, { useEffect, useState } from 'react'; import { createPortal } from 'react-dom'; +import { brand } from '../config/brand'; // Star positions around the logo (same as sidebar) const STARS = [ @@ -79,8 +80,8 @@ const PaymentReceivedCelebration: React.FC = ({ {/* Logo container */}

Glow diff --git a/src/components/SideMenu.tsx b/src/components/SideMenu.tsx index 17454a3e..9490fbf0 100644 --- a/src/components/SideMenu.tsx +++ b/src/components/SideMenu.tsx @@ -3,6 +3,7 @@ import { createPortal } from 'react-dom'; import { Transition } from '@headlessui/react'; import { isPasskeyMode } from '@/services/passkeyService'; import { RefundIcon, BackupIcon, SettingsIcon, LogoutIcon, CloseIcon, AlertTriangleIcon } from './Icons'; +import { brand } from '../config/brand'; // Star positions around the logo (relative to center, in pixels) const STARS = [ { x: -28, y: -20, size: 3 }, @@ -137,9 +138,9 @@ const SideMenu: React.FC = ({ isOpen, onClose, onLogout, onOpenSe
- Glow {/* Twinkling stars */} @@ -157,7 +158,7 @@ const SideMenu: React.FC = ({ isOpen, onClose, onLogout, onOpenSe /> ))}
-

Glow

+

{brand.name}

diff --git a/src/features/receive/ReceivePaymentDialog.tsx b/src/features/receive/ReceivePaymentDialog.tsx index e53f7785..851810d5 100644 --- a/src/features/receive/ReceivePaymentDialog.tsx +++ b/src/features/receive/ReceivePaymentDialog.tsx @@ -1,4 +1,5 @@ import React, { useState, useEffect } from 'react'; +import { brand } from '../../config/brand'; import { useToast } from '../../contexts/ToastContext'; import LoadingSpinner from '../../components/LoadingSpinner'; import { @@ -115,7 +116,7 @@ const ReceivePaymentDialog: React.FC = ({ isOpen, onC if (!lightningAddress) return ''; const parts = lightningAddress.lightningAddress.split('@'); const username = parts[0]; - const domain = parts[1] || 'breez.tips'; + const domain = parts[1] || brand.lnAddressDomain; return `Changing your Lightning Address username will permanently release '${username}@${domain}', making it available for other users.\n\nDo you want to proceed?`; }; diff --git a/src/features/receive/hooks/useLightningAddress.ts b/src/features/receive/hooks/useLightningAddress.ts index 95b8ffae..4e29099c 100644 --- a/src/features/receive/hooks/useLightningAddress.ts +++ b/src/features/receive/hooks/useLightningAddress.ts @@ -1,5 +1,6 @@ import { useCallback, useState } from 'react'; import type { LightningAddressInfo } from '@breeztech/breez-sdk-spark'; +import { brand } from '../../../config/brand'; import { useWallet } from '../../../contexts/WalletContext'; import { generateRandomName } from '../../../utils/randomName'; import { logger, LogCategory } from '@/services/logger'; @@ -66,7 +67,7 @@ export const useLightningAddress = (): UseLightningAddress => { const username = baseName + suffix; const isAvailable = await wallet.checkLightningAddressAvailable({ username }); if (isAvailable) { - await wallet.registerLightningAddress({ username, description: `Pay to ${username}@breez.tips` }); + await wallet.registerLightningAddress({ username, description: `Pay to ${username}@${brand.lnAddressDomain}` }); addr = await wallet.getLightningAddress(); break; } @@ -126,7 +127,7 @@ export const useLightningAddress = (): UseLightningAddress => { return; } - await wallet.registerLightningAddress({ username, description: `Pay to ${username}@breez.tips` }); + await wallet.registerLightningAddress({ username, description: `Pay to ${username}@${brand.lnAddressDomain}` }); const actualInfo = await wallet.getLightningAddress(); setAddress(actualInfo ?? null); setIsEditing(false); diff --git a/src/features/send/steps/ProcessingStep.tsx b/src/features/send/steps/ProcessingStep.tsx index 89b0370b..207678bf 100644 --- a/src/features/send/steps/ProcessingStep.tsx +++ b/src/features/send/steps/ProcessingStep.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { brand } from '../../../config/brand'; export interface ProcessingStepProps { /** Operation type to customize messaging (default: 'payment') */ @@ -76,7 +77,7 @@ const ProcessingStep: React.FC = ({ operationType = 'paymen {/* Icon */} {isAuth ? renderIcon() : ( Processing = ({ result, error, onClose, operati {/* Logo with sparkles */}
Glow diff --git a/src/pages/GeneratePage.tsx b/src/pages/GeneratePage.tsx index b3624b3b..25409a8e 100644 --- a/src/pages/GeneratePage.tsx +++ b/src/pages/GeneratePage.tsx @@ -1,5 +1,6 @@ import React, { useState, useEffect } from 'react'; import * as bip39 from 'bip39'; +import { brand } from '../config/brand'; import { PrimaryButton } from '../components/ui'; import LoadingSpinner from '../components/LoadingSpinner'; import PageLayout from '../components/layout/PageLayout'; @@ -61,7 +62,7 @@ const GeneratePage: React.FC = ({ return ( } title="Get Started" onClearError={onClearError}>
- +
); diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx index c32b0602..df196662 100644 --- a/src/pages/HomePage.tsx +++ b/src/pages/HomePage.tsx @@ -1,5 +1,6 @@ import React, { useEffect, useState } from 'react'; import { useSecretTap } from '@/hooks/useSecretTap'; +import { brand } from '../config/brand'; // Star positions around the logo (relative to center, in pixels) - larger radius for bigger logo const STARS = [ @@ -91,8 +92,8 @@ const HomePage: React.FC = ({ {/* Icon container */}
Glow @@ -116,13 +117,13 @@ const HomePage: React.FC = ({ {/* Title */}

- Glow + {brand.name}

{/* Tagline */}

- Powered by Breez SDK + {brand.tagline}

{/* CTA Buttons */} diff --git a/src/pages/SettingsPage.tsx b/src/pages/SettingsPage.tsx index 37804068..a244e3b7 100644 --- a/src/pages/SettingsPage.tsx +++ b/src/pages/SettingsPage.tsx @@ -1,4 +1,5 @@ import React, { useEffect, useState } from 'react'; +import { brand } from '../config/brand'; import { FormGroup, FormInput, LoadingSpinner, PrimaryButton, Switch } from '../components/ui'; import { getSettings, saveSettings, UserSettings } from '../services/settings'; import type { Config, Network } from '@breeztech/breez-sdk-spark'; @@ -321,7 +322,7 @@ const SettingsPage: React.FC = ({ onBack, config, onOpenFiatC onClick={devTap} className="text-spark-text-muted text-xs hover:text-spark-text-secondary transition-colors select-none" > - Glow v1.0.0 + {`${brand.name} v${brand.version}`} {isDevMode && (dev)} {devTapCount > 0 && devTapCount < devTapThreshold && ( diff --git a/src/services/logExport.ts b/src/services/logExport.ts index 09f23748..927d4cf3 100644 --- a/src/services/logExport.ts +++ b/src/services/logExport.ts @@ -1,3 +1,4 @@ +import { brand } from '../config/brand'; import { logger, LogCategory } from './logger'; import { getAllSessions, isStorageAvailable } from './logStorage'; import JSZip from 'jszip'; @@ -12,22 +13,22 @@ export const getAllLogsAsZip = async (): Promise => { const nowTimestamp = Math.floor(now.getTime() / 1000); const currentSessionHeader = [ - `Glow Wallet Log Export`, + brand.logExportTitle, `Session: Current`, `Generated: ${now.toISOString()}`, '='.repeat(60), '', ].join('\n'); - zip.file(`${nowTimestamp}_glow_current.txt`, currentSessionHeader + '\n' + getAllLogs()); + zip.file(`${nowTimestamp}_${brand.logPrefix}_current.txt`, currentSessionHeader + '\n' + getAllLogs()); if (isStorageAvailable()) { try { const sessions = await getAllSessions(); for (const session of sessions) { const sessionTimestamp = Math.floor(new Date(session.startedAt).getTime() / 1000); - const filename = `${sessionTimestamp}_glow_session.txt`; + const filename = `${sessionTimestamp}_${brand.logPrefix}_session.txt`; const sessionHeader = [ - `Glow Wallet Log Export`, + brand.logExportTitle, `Session ID: ${session.id}`, `Started: ${session.startedAt}`, session.endedAt ? `Ended: ${session.endedAt}` : 'Status: Active', @@ -55,13 +56,13 @@ export const canShareFiles = (): boolean => { export const shareOrDownloadLogs = async (): Promise => { const blob = await getAllLogsAsZip(); const timestamp = Math.floor(Date.now() / 1000); - const filename = `${timestamp}_glow_logs.zip`; + const filename = `${timestamp}_${brand.logPrefix}_logs.zip`; if (canShareFiles()) { const file = new File([blob], filename, { type: 'application/zip' }); if (navigator.canShare({ files: [file] })) { try { - await navigator.share({ files: [file], title: 'Glow Wallet Logs' }); + await navigator.share({ files: [file], title: brand.logShareTitle }); return; } catch (e) { if ((e as Error).name === 'AbortError') return; diff --git a/src/services/logStorage.ts b/src/services/logStorage.ts index 75803e3d..75b20aa8 100644 --- a/src/services/logStorage.ts +++ b/src/services/logStorage.ts @@ -8,7 +8,9 @@ * - Automatic cleanup of oldest sessions when limit reached */ -const DB_NAME = 'glow-logs'; +import { brand } from '../config/brand'; + +const DB_NAME = brand.logDbName; const DB_VERSION = 2; // Bumped version for schema change const STORE_NAME = 'sessions'; const KEY_STORE_NAME = 'encryption'; From ab31d2b038f0ecf9bb0ebbcd5197e7e048e3b14a Mon Sep 17 00:00:00 2001 From: Erdem Yerebasmaz Date: Thu, 5 Mar 2026 17:52:58 +0300 Subject: [PATCH 02/13] feat: add theming system and brand setup tooling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Simplify brand.ts: 17 fields → 6 core + theme object, derive the rest - Remove "Wallet" from all derived strings - Add theme.colors and theme.fonts to brand config - Create theme injection system (CSS variables from brand config at startup) - Use Tailwind RGB variable pattern for opacity utility support (70+ usages) - Fix all hardcoded brand colors in SVGs and components - Add Vite plugin for build-time HTML brand injection (splash, meta tags, fonts) - Add `npm run brand:setup` CLI to generate icons + manifest from brand config - Rename all assets to generic filenames (no brand prefix) - Fix DM Sans font bug in index.css Co-Authored-By: Claude Opus 4.6 --- index.html | 62 +++++----- package-lock.json | 1 + package.json | 4 +- public/assets/{Glow_Logo.png => logo.png} | Bin public/icons/Glow-icon-192.png | Bin 52542 -> 0 bytes public/icons/Glow-icon-512.png | Bin 254939 -> 0 bytes public/icons/Glow-icon-maskable-192.png | Bin 26957 -> 0 bytes public/icons/Glow-icon-maskable-512.png | Bin 165180 -> 0 bytes public/icons/Glow_favicon.png | Bin 2694 -> 0 bytes public/icons/favicon.png | Bin 0 -> 761 bytes public/icons/icon-192.png | Bin 0 -> 10475 bytes public/icons/icon-512.png | Bin 0 -> 61655 bytes public/icons/icon-maskable-192.png | Bin 0 -> 10475 bytes public/icons/icon-maskable-512.png | Bin 0 -> 61655 bytes public/manifest.json | 15 ++- public/sw.js | 16 +-- scripts/brand-setup.ts | 108 ++++++++++++++++++ scripts/resize-icon.js | 58 ---------- src/components/CollapsingWalletHeader.tsx | 2 +- src/components/LoadingSpinner.tsx | 10 +- src/components/PaymentReceivedCelebration.tsx | 2 +- src/config/brand.ts | 48 +++++--- src/config/theme.ts | 79 +++++++++++++ src/features/send/steps/ProcessingStep.tsx | 6 +- src/features/send/steps/ResultStep.tsx | 2 +- src/index.css | 49 ++++++-- src/main.tsx | 9 +- src/services/logExport.ts | 14 +-- src/services/logStorage.ts | 4 +- tailwind.config.js | 73 ++++++------ vite.config.ts | 45 +++++++- 31 files changed, 418 insertions(+), 189 deletions(-) rename public/assets/{Glow_Logo.png => logo.png} (100%) delete mode 100644 public/icons/Glow-icon-192.png delete mode 100644 public/icons/Glow-icon-512.png delete mode 100644 public/icons/Glow-icon-maskable-192.png delete mode 100644 public/icons/Glow-icon-maskable-512.png delete mode 100644 public/icons/Glow_favicon.png create mode 100644 public/icons/favicon.png create mode 100644 public/icons/icon-192.png create mode 100644 public/icons/icon-512.png create mode 100644 public/icons/icon-maskable-192.png create mode 100644 public/icons/icon-maskable-512.png create mode 100644 scripts/brand-setup.ts delete mode 100644 scripts/resize-icon.js create mode 100644 src/config/theme.ts diff --git a/index.html b/index.html index f8157f74..05aff1e9 100644 --- a/index.html +++ b/index.html @@ -4,51 +4,51 @@ - + - + - + - - + + - + - - + + - - - - + + + + - + - - - Glow + + + %BRAND_NAME% - + - - + +