diff --git a/src/app/(dashboard)/dashboard/settings/components/AppearanceTab.tsx b/src/app/(dashboard)/dashboard/settings/components/AppearanceTab.tsx
index bf173b8b4b..e9ddfcdb06 100644
--- a/src/app/(dashboard)/dashboard/settings/components/AppearanceTab.tsx
+++ b/src/app/(dashboard)/dashboard/settings/components/AppearanceTab.tsx
@@ -282,6 +282,109 @@ export default function AppearanceTab() {
/>
+
+
+
+
+
+ badge
+
+
+
+
{t("whitelabeling")}
+
{t("whitelabelingDesc")}
+
+
+
+
+
+
+
{t("appName")}
+
{t("appNameDesc")}
+
+
updateSetting("instanceName", e.target.value)}
+ placeholder="OmniRoute"
+ maxLength={100}
+ className="h-10 px-3 rounded-lg bg-surface border border-border text-sm text-text-main focus:outline-none focus:border-primary w-48"
+ />
+
+
+
+
+
{t("customLogo")}
+
{t("customLogoDesc")}
+
+
+
updateSetting("customLogoUrl", e.target.value)}
+ className="flex-1 h-10 px-3 rounded-lg bg-surface border border-border text-sm text-text-main focus:outline-none focus:border-primary"
+ placeholder="https://example.com/logo.png"
+ maxLength={2000}
+ />
+ {(settings.customLogoUrl || settings.customLogoBase64) && (
+

{
+ e.currentTarget.style.display = "none";
+ }}
+ />
+ )}
+
+
+
+
+
{t("uploadLogo")}
+
+ {
+ const file = e.target.files?.[0];
+ if (file) {
+ if (file.size > 500 * 1024) {
+ alert("Logo file must be less than 500KB");
+ return;
+ }
+ const reader = new FileReader();
+ reader.onload = (event) => {
+ const base64 = event.target?.result as string;
+ updateSetting("customLogoBase64", base64);
+ };
+ reader.readAsDataURL(file);
+ }
+ }}
+ className="text-sm text-text-muted"
+ />
+
+
+ {(settings.customLogoBase64 || settings.customLogoUrl) && (
+
+
{t("logoPreview")}
+

+
+ )}
+
+
+
);
diff --git a/src/app/(dashboard)/dashboard/settings/page.tsx b/src/app/(dashboard)/dashboard/settings/page.tsx
index 9d0b261022..4da90585ef 100644
--- a/src/app/(dashboard)/dashboard/settings/page.tsx
+++ b/src/app/(dashboard)/dashboard/settings/page.tsx
@@ -23,6 +23,7 @@ import ResilienceTab from "./components/ResilienceTab";
const tabs = [
{ id: "general", labelKey: "general", icon: "settings" },
+ { id: "appearance", labelKey: "appearance", icon: "palette" },
{ id: "ai", labelKey: "ai", icon: "smart_toy" },
{ id: "security", labelKey: "security", icon: "shield" },
{ id: "routing", labelKey: "routing", icon: "route" },
@@ -75,14 +76,9 @@ export default function SettingsPage() {
role="tabpanel"
aria-label={t(tabs.find((t2) => t2.id === activeTab)?.labelKey || "general")}
>
- {activeTab === "general" && (
- <>
-
- >
- )}
+ {activeTab === "general" && }
+
+ {activeTab === "appearance" && }
{activeTab === "ai" && (
diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json
index 522fa600b6..5afdda66ed 100644
--- a/src/i18n/messages/en.json
+++ b/src/i18n/messages/en.json
@@ -1789,6 +1789,15 @@
"themeViolet": "Violet",
"themeOrange": "Orange",
"themeCyan": "Cyan",
+ "whitelabeling": "Branding",
+ "whitelabelingDesc": "Customize the application name and logo",
+ "appName": "Application Name",
+ "appNameDesc": "Display name shown in sidebar and browser tab",
+ "customLogo": "Custom Logo URL",
+ "customLogoDesc": "URL to your custom logo image",
+ "uploadLogo": "Upload Logo",
+ "resetLogo": "Reset to Default",
+ "logoPreview": "Preview",
"promptCache": "Prompt Cache",
"flushCache": "Flush Cache",
"flushing": "Flushing…",
diff --git a/src/shared/validation/settingsSchemas.ts b/src/shared/validation/settingsSchemas.ts
index ac174837a6..76c86b332d 100644
--- a/src/shared/validation/settingsSchemas.ts
+++ b/src/shared/validation/settingsSchemas.ts
@@ -16,6 +16,8 @@ export const updateSettingsSchema = z.object({
requireLogin: z.boolean().optional(),
enableSocks5Proxy: z.boolean().optional(),
instanceName: z.string().max(100).optional(),
+ customLogoUrl: z.string().max(2000).optional(),
+ customLogoBase64: z.string().max(100000).optional(),
corsOrigins: z.string().max(500).optional(),
cloudUrl: z.string().max(500).optional(),
baseUrl: z.string().max(500).optional(),