You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
790 lines
25 KiB
790 lines
25 KiB
package branding |
|
|
|
import ( |
|
"bytes" |
|
"embed" |
|
"encoding/json" |
|
"fmt" |
|
"image" |
|
"image/color" |
|
"image/png" |
|
"io/fs" |
|
"math" |
|
"os" |
|
"path/filepath" |
|
) |
|
|
|
// BrandingStyle represents the type of branding kit to generate |
|
type BrandingStyle string |
|
|
|
const ( |
|
StyleORLY BrandingStyle = "orly" // ORLY-branded assets |
|
StyleGeneric BrandingStyle = "generic" // Generic/white-label assets |
|
) |
|
|
|
// InitBrandingKit creates a branding directory with assets and configuration |
|
func InitBrandingKit(dir string, embeddedFS embed.FS, style BrandingStyle) error { |
|
// Create directory structure |
|
dirs := []string{ |
|
dir, |
|
filepath.Join(dir, "assets"), |
|
filepath.Join(dir, "css"), |
|
} |
|
|
|
for _, d := range dirs { |
|
if err := os.MkdirAll(d, 0755); err != nil { |
|
return fmt.Errorf("failed to create directory %s: %w", d, err) |
|
} |
|
} |
|
|
|
// Write branding.json based on style |
|
var config Config |
|
var cssTemplate, varsTemplate string |
|
|
|
switch style { |
|
case StyleGeneric: |
|
config = GenericConfig() |
|
cssTemplate = GenericCSSTemplate |
|
varsTemplate = GenericCSSVariablesTemplate |
|
default: |
|
config = DefaultConfig() |
|
cssTemplate = CSSTemplate |
|
varsTemplate = CSSVariablesTemplate |
|
} |
|
|
|
configData, err := json.MarshalIndent(config, "", " ") |
|
if err != nil { |
|
return fmt.Errorf("failed to marshal config: %w", err) |
|
} |
|
configPath := filepath.Join(dir, "branding.json") |
|
if err := os.WriteFile(configPath, configData, 0644); err != nil { |
|
return fmt.Errorf("failed to write branding.json: %w", err) |
|
} |
|
|
|
// Generate or extract assets based on style |
|
if style == StyleGeneric { |
|
// Generate generic placeholder images |
|
if err := generateGenericAssets(dir); err != nil { |
|
return fmt.Errorf("failed to generate generic assets: %w", err) |
|
} |
|
} else { |
|
// Extract ORLY embedded assets |
|
assetMappings := map[string]string{ |
|
"web/dist/orly.png": filepath.Join(dir, "assets", "logo.png"), |
|
"web/dist/favicon.png": filepath.Join(dir, "assets", "favicon.png"), |
|
"web/dist/icon-192.png": filepath.Join(dir, "assets", "icon-192.png"), |
|
"web/dist/icon-512.png": filepath.Join(dir, "assets", "icon-512.png"), |
|
} |
|
|
|
for src, dst := range assetMappings { |
|
data, err := fs.ReadFile(embeddedFS, src) |
|
if err != nil { |
|
altSrc := "web/" + filepath.Base(src) |
|
data, err = fs.ReadFile(embeddedFS, altSrc) |
|
if err != nil { |
|
fmt.Printf("Warning: could not extract %s: %v\n", src, err) |
|
continue |
|
} |
|
} |
|
if err := os.WriteFile(dst, data, 0644); err != nil { |
|
return fmt.Errorf("failed to write %s: %w", dst, err) |
|
} |
|
} |
|
} |
|
|
|
// Write CSS template |
|
cssPath := filepath.Join(dir, "css", "custom.css") |
|
if err := os.WriteFile(cssPath, []byte(cssTemplate), 0644); err != nil { |
|
return fmt.Errorf("failed to write custom.css: %w", err) |
|
} |
|
|
|
// Write variables-only CSS template |
|
varsPath := filepath.Join(dir, "css", "variables.css") |
|
if err := os.WriteFile(varsPath, []byte(varsTemplate), 0644); err != nil { |
|
return fmt.Errorf("failed to write variables.css: %w", err) |
|
} |
|
|
|
return nil |
|
} |
|
|
|
// generateGenericAssets creates simple geometric placeholder images |
|
func generateGenericAssets(dir string) error { |
|
// Color scheme: neutral blue-gray |
|
primaryColor := color.RGBA{R: 64, G: 128, B: 192, A: 255} // #4080C0 - professional blue |
|
transparent := color.RGBA{R: 0, G: 0, B: 0, A: 0} // Transparent background |
|
|
|
// Generate each size |
|
sizes := map[string]int{ |
|
"logo.png": 256, |
|
"favicon.png": 64, |
|
"icon-192.png": 192, |
|
"icon-512.png": 512, |
|
} |
|
|
|
for filename, size := range sizes { |
|
img := generateRoundedSquare(size, primaryColor, transparent) |
|
path := filepath.Join(dir, "assets", filename) |
|
if err := savePNG(path, img); err != nil { |
|
return fmt.Errorf("failed to save %s: %w", filename, err) |
|
} |
|
} |
|
|
|
return nil |
|
} |
|
|
|
// generateRoundedSquare creates a simple rounded square icon |
|
func generateRoundedSquare(size int, primary, bg color.RGBA) image.Image { |
|
img := image.NewRGBA(image.Rect(0, 0, size, size)) |
|
|
|
// Fill background |
|
for y := 0; y < size; y++ { |
|
for x := 0; x < size; x++ { |
|
img.Set(x, y, bg) |
|
} |
|
} |
|
|
|
// Draw a rounded square in the center |
|
margin := size / 8 |
|
cornerRadius := size / 6 |
|
squareSize := size - (margin * 2) |
|
|
|
for y := margin; y < margin+squareSize; y++ { |
|
for x := margin; x < margin+squareSize; x++ { |
|
// Check if point is inside rounded rectangle |
|
if isInsideRoundedRect(x-margin, y-margin, squareSize, squareSize, cornerRadius) { |
|
img.Set(x, y, primary) |
|
} |
|
} |
|
} |
|
|
|
// Draw a simple inner circle (relay symbol) |
|
centerX := size / 2 |
|
centerY := size / 2 |
|
innerRadius := size / 5 |
|
ringWidth := size / 20 |
|
|
|
for y := 0; y < size; y++ { |
|
for x := 0; x < size; x++ { |
|
dx := float64(x - centerX) |
|
dy := float64(y - centerY) |
|
dist := math.Sqrt(dx*dx + dy*dy) |
|
|
|
// Ring (circle outline) |
|
if dist >= float64(innerRadius-ringWidth) && dist <= float64(innerRadius) { |
|
img.Set(x, y, bg) |
|
} |
|
} |
|
} |
|
|
|
return img |
|
} |
|
|
|
// isInsideRoundedRect checks if a point is inside a rounded rectangle |
|
func isInsideRoundedRect(x, y, w, h, r int) bool { |
|
// Check corners |
|
if x < r && y < r { |
|
// Top-left corner |
|
return isInsideCircle(x, y, r, r, r) |
|
} |
|
if x >= w-r && y < r { |
|
// Top-right corner |
|
return isInsideCircle(x, y, w-r-1, r, r) |
|
} |
|
if x < r && y >= h-r { |
|
// Bottom-left corner |
|
return isInsideCircle(x, y, r, h-r-1, r) |
|
} |
|
if x >= w-r && y >= h-r { |
|
// Bottom-right corner |
|
return isInsideCircle(x, y, w-r-1, h-r-1, r) |
|
} |
|
|
|
// Inside main rectangle |
|
return x >= 0 && x < w && y >= 0 && y < h |
|
} |
|
|
|
// isInsideCircle checks if a point is inside a circle |
|
func isInsideCircle(x, y, cx, cy, r int) bool { |
|
dx := x - cx |
|
dy := y - cy |
|
return dx*dx+dy*dy <= r*r |
|
} |
|
|
|
// savePNG saves an image as a PNG file |
|
func savePNG(path string, img image.Image) error { |
|
var buf bytes.Buffer |
|
if err := png.Encode(&buf, img); err != nil { |
|
return err |
|
} |
|
return os.WriteFile(path, buf.Bytes(), 0644) |
|
} |
|
|
|
// GenericConfig returns a generic/white-label configuration |
|
func GenericConfig() Config { |
|
return Config{ |
|
Version: 1, |
|
App: AppConfig{ |
|
Name: "Relay", |
|
ShortName: "Relay", |
|
Title: "Relay Dashboard", |
|
Description: "Nostr relay service", |
|
}, |
|
NIP11: NIP11Config{ |
|
Name: "Relay", |
|
Description: "A Nostr relay", |
|
Icon: "", |
|
}, |
|
Manifest: ManifestConfig{ |
|
ThemeColor: "#4080C0", |
|
BackgroundColor: "#F0F4F8", |
|
}, |
|
Assets: AssetsConfig{ |
|
Logo: "assets/logo.png", |
|
Favicon: "assets/favicon.png", |
|
Icon192: "assets/icon-192.png", |
|
Icon512: "assets/icon-512.png", |
|
}, |
|
CSS: CSSConfig{ |
|
CustomCSS: "css/custom.css", |
|
VariablesCSS: "css/variables.css", |
|
}, |
|
} |
|
} |
|
|
|
// CSSTemplate is the full CSS template with all variables and documentation |
|
const CSSTemplate = `/* |
|
* Custom Branding CSS for ORLY Relay |
|
* ================================== |
|
* |
|
* This file is loaded AFTER the default styles, so any rules here |
|
* will override the defaults. You can customize: |
|
* |
|
* 1. CSS Variables (colors, spacing, etc.) |
|
* 2. Component styles (buttons, cards, headers, etc.) |
|
* 3. Add completely custom styles |
|
* |
|
* Restart the relay to apply changes. |
|
* |
|
* For variable-only overrides, edit variables.css instead. |
|
*/ |
|
|
|
/* ============================================================================= |
|
LIGHT THEME VARIABLES |
|
============================================================================= */ |
|
|
|
:root { |
|
/* Background colors */ |
|
--bg-color: #ddd; /* Main page background */ |
|
--header-bg: #eee; /* Header background */ |
|
--sidebar-bg: #eee; /* Sidebar background */ |
|
--card-bg: #f8f9fa; /* Card/container background */ |
|
--panel-bg: #f8f9fa; /* Panel background */ |
|
|
|
/* Border colors */ |
|
--border-color: #dee2e6; /* Default border color */ |
|
|
|
/* Text colors */ |
|
--text-color: #444444; /* Primary text color */ |
|
--text-muted: #6c757d; /* Secondary/muted text */ |
|
|
|
/* Input/form colors */ |
|
--input-border: #ccc; /* Input border color */ |
|
--input-bg: #ffffff; /* Input background */ |
|
--input-text-color: #495057; /* Input text color */ |
|
|
|
/* Button colors */ |
|
--button-bg: #ddd; /* Default button background */ |
|
--button-hover-bg: #eee; /* Button hover background */ |
|
--button-text: #444444; /* Button text color */ |
|
--button-hover-border: #adb5bd; /* Button hover border */ |
|
|
|
/* Theme/accent colors */ |
|
--primary: #00bcd4; /* Primary accent (cyan) */ |
|
--primary-bg: rgba(0, 188, 212, 0.1); /* Primary background tint */ |
|
--secondary: #6c757d; /* Secondary color */ |
|
|
|
/* Status colors */ |
|
--success: #28a745; /* Success/positive */ |
|
--success-bg: #d4edda; /* Success background */ |
|
--success-text: #155724; /* Success text */ |
|
--info: #17a2b8; /* Info/neutral */ |
|
--warning: #ff3e00; /* Warning (Svelte orange) */ |
|
--warning-bg: #fff3cd; /* Warning background */ |
|
--danger: #dc3545; /* Danger/error */ |
|
--danger-bg: #f8d7da; /* Danger background */ |
|
--danger-text: #721c24; /* Danger text */ |
|
--error-bg: #f8d7da; /* Error background */ |
|
--error-text: #721c24; /* Error text */ |
|
|
|
/* Code block colors */ |
|
--code-bg: #f8f9fa; /* Code block background */ |
|
--code-text: #495057; /* Code text color */ |
|
|
|
/* Tab colors */ |
|
--tab-inactive-bg: #bbb; /* Inactive tab background */ |
|
|
|
/* Link/accent colors */ |
|
--accent-color: #007bff; /* Link color */ |
|
--accent-hover-color: #0056b3; /* Link hover color */ |
|
} |
|
|
|
/* ============================================================================= |
|
DARK THEME VARIABLES |
|
============================================================================= */ |
|
|
|
body.dark-theme { |
|
/* Background colors */ |
|
--bg-color: #263238; /* Main page background */ |
|
--header-bg: #1e272c; /* Header background */ |
|
--sidebar-bg: #1e272c; /* Sidebar background */ |
|
--card-bg: #37474f; /* Card/container background */ |
|
--panel-bg: #37474f; /* Panel background */ |
|
|
|
/* Border colors */ |
|
--border-color: #404040; /* Default border color */ |
|
|
|
/* Text colors */ |
|
--text-color: #ffffff; /* Primary text color */ |
|
--text-muted: #adb5bd; /* Secondary/muted text */ |
|
|
|
/* Input/form colors */ |
|
--input-border: #555; /* Input border color */ |
|
--input-bg: #37474f; /* Input background */ |
|
--input-text-color: #ffffff; /* Input text color */ |
|
|
|
/* Button colors */ |
|
--button-bg: #263238; /* Default button background */ |
|
--button-hover-bg: #1e272c; /* Button hover background */ |
|
--button-text: #ffffff; /* Button text color */ |
|
--button-hover-border: #6c757d; /* Button hover border */ |
|
|
|
/* Theme/accent colors */ |
|
--primary: #00bcd4; /* Primary accent (cyan) */ |
|
--primary-bg: rgba(0, 188, 212, 0.2); /* Primary background tint */ |
|
--secondary: #6c757d; /* Secondary color */ |
|
|
|
/* Status colors */ |
|
--success: #28a745; /* Success/positive */ |
|
--success-bg: #1e4620; /* Success background (dark) */ |
|
--success-text: #d4edda; /* Success text (light) */ |
|
--info: #17a2b8; /* Info/neutral */ |
|
--warning: #ff3e00; /* Warning (Svelte orange) */ |
|
--warning-bg: #4d1f00; /* Warning background (dark) */ |
|
--danger: #dc3545; /* Danger/error */ |
|
--danger-bg: #4d1319; /* Danger background (dark) */ |
|
--danger-text: #f8d7da; /* Danger text (light) */ |
|
--error-bg: #4d1319; /* Error background */ |
|
--error-text: #f8d7da; /* Error text */ |
|
|
|
/* Code block colors */ |
|
--code-bg: #1e272c; /* Code block background */ |
|
--code-text: #ffffff; /* Code text color */ |
|
|
|
/* Tab colors */ |
|
--tab-inactive-bg: #1a1a1a; /* Inactive tab background */ |
|
|
|
/* Link/accent colors */ |
|
--accent-color: #007bff; /* Link color */ |
|
--accent-hover-color: #0056b3; /* Link hover color */ |
|
} |
|
|
|
/* ============================================================================= |
|
CUSTOM STYLES |
|
Add your custom CSS rules below. These will override any default styles. |
|
============================================================================= */ |
|
|
|
/* Example: Custom header styling |
|
.header { |
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); |
|
} |
|
*/ |
|
|
|
/* Example: Custom button styling |
|
.btn { |
|
border-radius: 8px; |
|
text-transform: uppercase; |
|
letter-spacing: 0.5px; |
|
} |
|
*/ |
|
|
|
/* Example: Custom card styling |
|
.card { |
|
border-radius: 12px; |
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); |
|
} |
|
*/ |
|
` |
|
|
|
// CSSVariablesTemplate contains only CSS variable definitions |
|
const CSSVariablesTemplate = `/* |
|
* CSS Variables Override for ORLY Relay |
|
* ====================================== |
|
* |
|
* This file contains only CSS variable definitions. |
|
* Edit values here to customize colors without touching component styles. |
|
* |
|
* For full CSS customization (including component styles), |
|
* edit custom.css instead. |
|
*/ |
|
|
|
/* Light theme variables */ |
|
:root { |
|
--bg-color: #ddd; |
|
--header-bg: #eee; |
|
--sidebar-bg: #eee; |
|
--card-bg: #f8f9fa; |
|
--panel-bg: #f8f9fa; |
|
--border-color: #dee2e6; |
|
--text-color: #444444; |
|
--text-muted: #6c757d; |
|
--input-border: #ccc; |
|
--input-bg: #ffffff; |
|
--input-text-color: #495057; |
|
--button-bg: #ddd; |
|
--button-hover-bg: #eee; |
|
--button-text: #444444; |
|
--button-hover-border: #adb5bd; |
|
--primary: #00bcd4; |
|
--primary-bg: rgba(0, 188, 212, 0.1); |
|
--secondary: #6c757d; |
|
--success: #28a745; |
|
--success-bg: #d4edda; |
|
--success-text: #155724; |
|
--info: #17a2b8; |
|
--warning: #ff3e00; |
|
--warning-bg: #fff3cd; |
|
--danger: #dc3545; |
|
--danger-bg: #f8d7da; |
|
--danger-text: #721c24; |
|
--error-bg: #f8d7da; |
|
--error-text: #721c24; |
|
--code-bg: #f8f9fa; |
|
--code-text: #495057; |
|
--tab-inactive-bg: #bbb; |
|
--accent-color: #007bff; |
|
--accent-hover-color: #0056b3; |
|
} |
|
|
|
/* Dark theme variables */ |
|
body.dark-theme { |
|
--bg-color: #263238; |
|
--header-bg: #1e272c; |
|
--sidebar-bg: #1e272c; |
|
--card-bg: #37474f; |
|
--panel-bg: #37474f; |
|
--border-color: #404040; |
|
--text-color: #ffffff; |
|
--text-muted: #adb5bd; |
|
--input-border: #555; |
|
--input-bg: #37474f; |
|
--input-text-color: #ffffff; |
|
--button-bg: #263238; |
|
--button-hover-bg: #1e272c; |
|
--button-text: #ffffff; |
|
--button-hover-border: #6c757d; |
|
--primary: #00bcd4; |
|
--primary-bg: rgba(0, 188, 212, 0.2); |
|
--secondary: #6c757d; |
|
--success: #28a745; |
|
--success-bg: #1e4620; |
|
--success-text: #d4edda; |
|
--info: #17a2b8; |
|
--warning: #ff3e00; |
|
--warning-bg: #4d1f00; |
|
--danger: #dc3545; |
|
--danger-bg: #4d1319; |
|
--danger-text: #f8d7da; |
|
--error-bg: #4d1319; |
|
--error-text: #f8d7da; |
|
--code-bg: #1e272c; |
|
--code-text: #ffffff; |
|
--tab-inactive-bg: #1a1a1a; |
|
--accent-color: #007bff; |
|
--accent-hover-color: #0056b3; |
|
} |
|
` |
|
|
|
// GenericCSSTemplate is the CSS template for generic/white-label branding |
|
const GenericCSSTemplate = `/* |
|
* Custom Branding CSS - White Label Template |
|
* ========================================== |
|
* |
|
* This file is loaded AFTER the default styles, so any rules here |
|
* will override the defaults. You can customize: |
|
* |
|
* 1. CSS Variables (colors, spacing, etc.) |
|
* 2. Component styles (buttons, cards, headers, etc.) |
|
* 3. Add completely custom styles |
|
* |
|
* Restart the relay to apply changes. |
|
* |
|
* For variable-only overrides, edit variables.css instead. |
|
*/ |
|
|
|
/* ============================================================================= |
|
LIGHT THEME VARIABLES - Professional Blue-Gray |
|
============================================================================= */ |
|
|
|
html, body { |
|
/* Background colors */ |
|
--bg-color: #F0F4F8; /* Light gray-blue background */ |
|
--header-bg: #FFFFFF; /* Clean white header */ |
|
--sidebar-bg: #FFFFFF; /* Clean white sidebar */ |
|
--card-bg: #FFFFFF; /* White cards */ |
|
--panel-bg: #FFFFFF; /* White panels */ |
|
|
|
/* Border colors */ |
|
--border-color: #E2E8F0; /* Subtle gray border */ |
|
|
|
/* Text colors */ |
|
--text-color: #334155; /* Dark slate text */ |
|
--text-muted: #64748B; /* Muted slate */ |
|
|
|
/* Input/form colors */ |
|
--input-border: #CBD5E1; /* Light slate border */ |
|
--input-bg: #FFFFFF; /* White input */ |
|
--input-text-color: #334155; /* Dark slate text */ |
|
|
|
/* Button colors */ |
|
--button-bg: #F1F5F9; /* Light slate button */ |
|
--button-hover-bg: #E2E8F0; /* Slightly darker on hover */ |
|
--button-text: #334155; /* Dark slate text */ |
|
--button-hover-border: #94A3B8; /* Medium slate border */ |
|
|
|
/* Theme/accent colors - Professional Blue */ |
|
--primary: #4080C0; /* Professional blue */ |
|
--primary-bg: rgba(64, 128, 192, 0.1); /* Light blue tint */ |
|
--secondary: #64748B; /* Slate gray */ |
|
|
|
/* Status colors */ |
|
--success: #22C55E; /* Green */ |
|
--success-bg: #DCFCE7; /* Light green */ |
|
--success-text: #166534; /* Dark green */ |
|
--info: #3B82F6; /* Blue */ |
|
--warning: #F59E0B; /* Amber */ |
|
--warning-bg: #FEF3C7; /* Light amber */ |
|
--danger: #EF4444; /* Red */ |
|
--danger-bg: #FEE2E2; /* Light red */ |
|
--danger-text: #991B1B; /* Dark red */ |
|
--error-bg: #FEE2E2; /* Light red */ |
|
--error-text: #991B1B; /* Dark red */ |
|
|
|
/* Code block colors */ |
|
--code-bg: #F8FAFC; /* Very light slate */ |
|
--code-text: #334155; /* Dark slate */ |
|
|
|
/* Tab colors */ |
|
--tab-inactive-bg: #E2E8F0; /* Light slate */ |
|
|
|
/* Link/accent colors */ |
|
--accent-color: #4080C0; /* Professional blue */ |
|
--accent-hover-color: #2563EB; /* Darker blue */ |
|
} |
|
|
|
/* ============================================================================= |
|
DARK THEME VARIABLES - Professional Dark |
|
============================================================================= */ |
|
|
|
body.dark-theme { |
|
/* Background colors */ |
|
--bg-color: #0F172A; /* Dark navy */ |
|
--header-bg: #1E293B; /* Slate gray */ |
|
--sidebar-bg: #1E293B; /* Slate gray */ |
|
--card-bg: #1E293B; /* Slate gray */ |
|
--panel-bg: #1E293B; /* Slate gray */ |
|
|
|
/* Border colors */ |
|
--border-color: #334155; /* Medium slate */ |
|
|
|
/* Text colors */ |
|
--text-color: #F8FAFC; /* Almost white */ |
|
--text-muted: #94A3B8; /* Muted slate */ |
|
|
|
/* Input/form colors */ |
|
--input-border: #475569; /* Slate border */ |
|
--input-bg: #1E293B; /* Slate background */ |
|
--input-text-color: #F8FAFC; /* Light text */ |
|
|
|
/* Button colors */ |
|
--button-bg: #334155; /* Slate button */ |
|
--button-hover-bg: #475569; /* Lighter on hover */ |
|
--button-text: #F8FAFC; /* Light text */ |
|
--button-hover-border: #64748B; /* Medium slate */ |
|
|
|
/* Theme/accent colors */ |
|
--primary: #60A5FA; /* Lighter blue for dark mode */ |
|
--primary-bg: rgba(96, 165, 250, 0.2); /* Blue tint */ |
|
--secondary: #94A3B8; /* Muted slate */ |
|
|
|
/* Status colors */ |
|
--success: #4ADE80; /* Bright green */ |
|
--success-bg: #166534; /* Dark green */ |
|
--success-text: #DCFCE7; /* Light green */ |
|
--info: #60A5FA; /* Light blue */ |
|
--warning: #FBBF24; /* Bright amber */ |
|
--warning-bg: #78350F; /* Dark amber */ |
|
--danger: #F87171; /* Light red */ |
|
--danger-bg: #7F1D1D; /* Dark red */ |
|
--danger-text: #FEE2E2; /* Light red */ |
|
--error-bg: #7F1D1D; /* Dark red */ |
|
--error-text: #FEE2E2; /* Light red */ |
|
|
|
/* Code block colors */ |
|
--code-bg: #0F172A; /* Dark navy */ |
|
--code-text: #F8FAFC; /* Light text */ |
|
|
|
/* Tab colors */ |
|
--tab-inactive-bg: #1E293B; /* Slate */ |
|
|
|
/* Link/accent colors */ |
|
--accent-color: #60A5FA; /* Light blue */ |
|
--accent-hover-color: #93C5FD; /* Lighter blue */ |
|
} |
|
|
|
/* ============================================================================= |
|
PRIMARY BUTTON TEXT COLOR FIX |
|
Ensures buttons with primary background have white text for contrast |
|
============================================================================= */ |
|
|
|
/* Target all common button patterns that use primary background */ |
|
button[class*="-btn"], |
|
button[class*="submit"], |
|
button[class*="action"], |
|
button[class*="save"], |
|
button[class*="add"], |
|
button[class*="create"], |
|
button[class*="connect"], |
|
button[class*="refresh"], |
|
button[class*="retry"], |
|
button[class*="send"], |
|
button[class*="apply"], |
|
button[class*="execute"], |
|
button[class*="run"], |
|
.primary-action, |
|
.action-button, |
|
.permission-badge, |
|
[class*="badge"] { |
|
color: #FFFFFF !important; |
|
} |
|
|
|
/* More specific override for any button that visually appears to have primary bg */ |
|
/* This uses a broad selector with low impact on non-primary buttons */ |
|
html:not(.dark-theme) button:not([disabled]) { |
|
/* Default to inherit, primary buttons will be caught above */ |
|
} |
|
|
|
/* ============================================================================= |
|
CUSTOM STYLES |
|
Add your custom CSS rules below. These will override any default styles. |
|
============================================================================= */ |
|
|
|
/* Example: Custom header styling |
|
.header { |
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); |
|
} |
|
*/ |
|
|
|
/* Example: Custom button styling |
|
.btn { |
|
border-radius: 6px; |
|
font-weight: 500; |
|
} |
|
*/ |
|
|
|
/* Example: Custom card styling |
|
.card { |
|
border-radius: 8px; |
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); |
|
} |
|
*/ |
|
` |
|
|
|
// GenericCSSVariablesTemplate contains CSS variables for generic/white-label branding |
|
const GenericCSSVariablesTemplate = `/* |
|
* CSS Variables Override - White Label Template |
|
* ============================================== |
|
* |
|
* This file contains only CSS variable definitions. |
|
* Edit values here to customize colors without touching component styles. |
|
* |
|
* For full CSS customization (including component styles), |
|
* edit custom.css instead. |
|
*/ |
|
|
|
/* Light theme variables - Professional Blue-Gray */ |
|
/* Applied to both html and body for maximum compatibility */ |
|
html, body { |
|
--bg-color: #F0F4F8; |
|
--header-bg: #FFFFFF; |
|
--sidebar-bg: #FFFFFF; |
|
--card-bg: #FFFFFF; |
|
--panel-bg: #FFFFFF; |
|
--border-color: #E2E8F0; |
|
--text-color: #334155; |
|
--text-muted: #64748B; |
|
--input-border: #CBD5E1; |
|
--input-bg: #FFFFFF; |
|
--input-text-color: #334155; |
|
--button-bg: #F1F5F9; |
|
--button-hover-bg: #E2E8F0; |
|
--button-text: #334155; |
|
--button-hover-border: #94A3B8; |
|
--primary: #4080C0; |
|
--primary-bg: rgba(64, 128, 192, 0.1); |
|
--secondary: #64748B; |
|
--success: #22C55E; |
|
--success-bg: #DCFCE7; |
|
--success-text: #166534; |
|
--info: #3B82F6; |
|
--warning: #F59E0B; |
|
--warning-bg: #FEF3C7; |
|
--danger: #EF4444; |
|
--danger-bg: #FEE2E2; |
|
--danger-text: #991B1B; |
|
--error-bg: #FEE2E2; |
|
--error-text: #991B1B; |
|
--code-bg: #F8FAFC; |
|
--code-text: #334155; |
|
--tab-inactive-bg: #E2E8F0; |
|
--accent-color: #4080C0; |
|
--accent-hover-color: #2563EB; |
|
} |
|
|
|
/* Dark theme variables - Professional Dark */ |
|
body.dark-theme { |
|
--bg-color: #0F172A; |
|
--header-bg: #1E293B; |
|
--sidebar-bg: #1E293B; |
|
--card-bg: #1E293B; |
|
--panel-bg: #1E293B; |
|
--border-color: #334155; |
|
--text-color: #F8FAFC; |
|
--text-muted: #94A3B8; |
|
--input-border: #475569; |
|
--input-bg: #1E293B; |
|
--input-text-color: #F8FAFC; |
|
--button-bg: #334155; |
|
--button-hover-bg: #475569; |
|
--button-text: #F8FAFC; |
|
--button-hover-border: #64748B; |
|
--primary: #60A5FA; |
|
--primary-bg: rgba(96, 165, 250, 0.2); |
|
--secondary: #94A3B8; |
|
--success: #4ADE80; |
|
--success-bg: #166534; |
|
--success-text: #DCFCE7; |
|
--info: #60A5FA; |
|
--warning: #FBBF24; |
|
--warning-bg: #78350F; |
|
--danger: #F87171; |
|
--danger-bg: #7F1D1D; |
|
--danger-text: #FEE2E2; |
|
--error-bg: #7F1D1D; |
|
--error-text: #FEE2E2; |
|
--code-bg: #0F172A; |
|
--code-text: #F8FAFC; |
|
--tab-inactive-bg: #1E293B; |
|
--accent-color: #60A5FA; |
|
--accent-hover-color: #93C5FD; |
|
} |
|
`
|
|
|