refactor: brand logo customization and emoji font selection

This commit is contained in:
3252a8
2026-05-13 14:02:35 +03:00
parent e21a0758c3
commit eec5635541
19 changed files with 895 additions and 155 deletions
+20 -11
View File
@@ -6,7 +6,7 @@
import { createAccountStore } from "./lib/webapp/stores/accountStore.js";
import { Tooltip } from "$components/ui/primitives.js";
import BrandMark from "./BrandMark.svelte";
import BrandMark from "$lib/webapp/BrandMark.svelte";
import PreviewBoard from "./PreviewBoard.svelte";
import AdminPanel from "./admin/AdminPanel.svelte";
import WebAppShell from "./webapp/WebAppShell.svelte";
@@ -29,7 +29,12 @@
WEBAPP_LANGUAGE_ORDER,
} from "./lib/webapp/constants.js";
import { applyFavicon, readJsonScript, structuredCloneSafe } from "./lib/webapp/browser.js";
import {
applyFavicon,
normalizeBrand,
readJsonScript,
structuredCloneSafe,
} from "./lib/webapp/browser.js";
import { createApiClient } from "./lib/webapp/publicApi.js";
import { createI18n } from "./lib/webapp/i18n.js";
import { normalizedEmail, telegramName } from "./lib/webapp/formatters.js";
@@ -241,6 +246,13 @@
$: brandTitle = CFG.title || "/minishop";
$: brandEmoji = CFG.logoEmoji || "🫥";
$: brandEmojiFont = CFG.logoEmojiFont || "system";
$: brand = normalizeBrand({
title: brandTitle,
logoUrl: CFG.logoUrl,
emoji: brandEmoji,
emojiFont: brandEmojiFont,
});
$: accent = CFG.primaryColor || "#00fe7a";
$: plans = data?.plans?.length ? data.plans : DEV_MOCK.data.plans;
$: methods = data?.payment_methods?.length ? data.payment_methods : [];
@@ -344,7 +356,7 @@
: telegramLoginUnavailable
? t("wa_auth_telegram_not_configured")
: "";
$: applyFavicon(CFG.logoUrl, brandEmoji);
$: applyFavicon(brand);
$: syncBodyScrollLock(
paymentModalOpen ||
changeModalOpen ||
@@ -903,7 +915,7 @@
<div class="app-shell" style={`--accent: ${accent};`}>
{#if mode === "loading"}
<div class="loader">
<BrandMark class="brand-mark-lg" logoUrl={CFG.logoUrl} emoji={brandEmoji} />
<BrandMark {brand} size="md" />
<div>{t("wa_loading")}</div>
</div>
{:else if mode === "login"}
@@ -911,7 +923,7 @@
{screen}
{CFG}
{brandTitle}
{brandEmoji}
{brand}
bind:email={$authStore.email}
bind:emailCode={$authStore.emailCode}
{pendingEmail}
@@ -952,8 +964,7 @@
onSettingsSaved={handleAdminPersistedSaved}
onTariffsSaved={handleAdminPersistedSaved}
{brandTitle}
logoUrl={CFG.logoUrl}
logoEmoji={brandEmoji}
{brand}
appVersion={CFG.appVersion}
appRepositoryUrl={CFG.appRepositoryUrl}
{currentLang}
@@ -966,9 +977,8 @@
<WebAppShell
{screen}
{activeTab}
{CFG}
{brandTitle}
{brandEmoji}
{brand}
{devicesEnabled}
{hasUnlinkedIdentity}
{isAdmin}
@@ -981,9 +991,8 @@
>
{#if screen === "home"}
<HomeScreen
{CFG}
{appSettings}
{brandEmoji}
{brand}
{brandTitle}
{canChangeTariff}
{currentTariffName}
-75
View File
@@ -1,75 +0,0 @@
<script>
import { onDestroy } from "svelte";
import { cn } from "./lib/utils.js";
const LOGO_LOAD_TIMEOUT_MS = 2600;
export let logoUrl = "";
export let emoji = "🫥";
let className = "";
export { className as class };
let loaded = false;
let failed = false;
let lastLogoUrl = "";
let logoLoadTimer = null;
let logoLoadTimerUrl = "";
$: normalizedLogoUrl = String(logoUrl || "").trim();
$: normalizedEmoji = String(emoji || "🫥").trim() || "🫥";
$: if (normalizedLogoUrl !== lastLogoUrl) {
lastLogoUrl = normalizedLogoUrl;
loaded = false;
failed = false;
}
$: if (normalizedLogoUrl && !loaded && !failed) armLogoLoadTimeout();
$: if (!normalizedLogoUrl || loaded || failed) clearLogoLoadTimeout();
onDestroy(clearLogoLoadTimeout);
function clearLogoLoadTimeout() {
if (logoLoadTimer) {
window.clearTimeout(logoLoadTimer);
logoLoadTimer = null;
}
logoLoadTimerUrl = "";
}
function armLogoLoadTimeout() {
if (typeof window === "undefined") return;
if (logoLoadTimer && logoLoadTimerUrl === normalizedLogoUrl) return;
clearLogoLoadTimeout();
logoLoadTimerUrl = normalizedLogoUrl;
logoLoadTimer = window.setTimeout(() => {
if (logoLoadTimerUrl === normalizedLogoUrl && !loaded) failed = true;
}, LOGO_LOAD_TIMEOUT_MS);
}
</script>
<div
class={cn(
"brand-mark",
normalizedLogoUrl && !failed && !loaded && "brand-mark-loading",
normalizedLogoUrl && !failed && loaded && "brand-mark-loaded",
className
)}
aria-busy={normalizedLogoUrl && !failed && !loaded ? "true" : undefined}
>
{#if normalizedLogoUrl && !failed}
{#if !loaded}
<span class="brand-mark-spinner" aria-hidden="true"></span>
{/if}
<img
class:loaded
src={normalizedLogoUrl}
alt=""
loading="lazy"
decoding="async"
on:load={() => (loaded = true)}
on:error={() => (failed = true)}
/>
{:else}
<span>{normalizedEmoji}</span>
{/if}
</div>
@@ -24,7 +24,7 @@
import { Select } from "$components/ui/primitives.js";
import { AdminBadge, AdminButton } from "$components/patterns/admin/index.js";
import BrandMark from "../BrandMark.svelte";
import BrandMark from "$lib/webapp/BrandMark.svelte";
import AdsSection from "./sections/AdsSection.svelte";
import BroadcastSection from "./sections/BroadcastSection.svelte";
import LogsSection from "./sections/LogsSection.svelte";
@@ -71,9 +71,8 @@
export let onSectionChange = () => {};
export let onSettingsSaved = () => {};
export let onTariffsSaved = () => {};
export let brand = {};
export let brandTitle = "/minishop";
export let logoUrl = "";
export let logoEmoji = "🫥";
export let appVersion = "dev+local";
export let appRepositoryUrl = "https://github.com/3252a8/remnawave-minishop";
export let currentLang = "ru";
@@ -180,8 +179,7 @@
function readReduceMotion() {
return (
typeof window !== "undefined" &&
window.matchMedia("(prefers-reduced-motion: reduce)").matches
typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches
);
}
@@ -423,7 +421,7 @@
<aside class="admin-sidebar" aria-label={at("sidebar_navigation", {}, "Навигация админки")}>
<div class="admin-sidebar-brand">
<BrandMark class="admin-brand-mark" {logoUrl} emoji={logoEmoji} />
<BrandMark class="admin-brand-mark" {brand} />
<div>
<strong class="admin-brand-title">{brandTitle}</strong>
<small>{at("panel_title", {}, "Админ-панель")}</small>
@@ -1,7 +1,12 @@
<script>
import { ChevronRight, Eye, EyeOff, X } from "$components/ui/icons.js";
import { Accordion, Switch } from "$components/ui/primitives.js";
import { AdminBadge, AdminButton, AdminEmptyState } from "$components/patterns/admin/index.js";
import {
AdminBadge,
AdminButton,
AdminEmptyState,
AdminSelect,
} from "$components/patterns/admin/index.js";
import { getContext, onMount } from "svelte";
export let at;
@@ -162,6 +167,15 @@
value={valueFor(field) || ""}
on:input={(e) => settingsStore.markDirty(field.key, e.currentTarget.value)}
/>
{:else if field.choices && field.choices.length > 0}
<AdminSelect
class="admin-setting-select"
value={valueFor(field) || ""}
items={field.choices}
ariaLabel={fieldLabelText(field)}
placeholder={field.placeholder || fieldLabelText(field)}
onValueChange={(value) => settingsStore.markDirty(field.key, value)}
/>
{:else if field.type === "int" || field.type === "float"}
<input
class="input"
@@ -0,0 +1,369 @@
<script>
import { onDestroy, onMount } from "svelte";
import { cn } from "../utils.js";
import { animatedEmojiAssetUrls, normalizeBrand } from "./browser.js";
const LOGO_LOAD_TIMEOUT_MS = 10000;
const EMOJI_FONT_OPTIONS = {
"noto-color": {
cssFamily: "Noto Color Emoji",
stylesheet: (text) =>
`https://fonts.googleapis.com/css2?family=Noto+Color+Emoji&display=swap&text=${encodeURIComponent(text)}`,
},
"noto-emoji": {
cssFamily: "Noto Emoji",
stylesheet: (text) =>
`https://fonts.googleapis.com/css2?family=Noto+Emoji:wght@700&display=swap&text=${encodeURIComponent(text)}`,
},
twemoji: {
cssFamily: "Twemoji Mozilla",
stylesheet: () => "https://cdn.jsdelivr.net/npm/twemoji-colr-font@15.0.3/twemoji.css",
},
openmoji: {
cssFamily: "OpenMoji Color",
stylesheet: () => "https://cdn.jsdelivr.net/npm/@openmoji/font@15.1.0/css/openmoji-color.css",
},
};
export let brand = {};
export let logoUrl = "";
export let emoji = "";
export let emojiFont = "";
export let size = "sm";
export let animate = false;
let className = "";
export { className as class };
const SIZE_CLASSES = {
sm: "",
md: "brand-mark-lg",
lg: "brand-mark-xl",
xl: "brand-mark-xl",
};
let loaded = false;
let failed = false;
let lastLogoUrl = "";
let logoLoadTimer = null;
let logoLoadTimerUrl = "";
let fontLoaded = false;
let loadedFontKey = "";
let animatedEmojiError = false;
let animatedEmojiStaticFallback = false;
let lastAnimatedEmoji = "";
$: normalizedBrand = normalizeBrand({
...brand,
logoUrl: logoUrl || brand?.logoUrl,
emoji: emoji || brand?.emoji || brand?.logoEmoji,
emojiFont: emojiFont || brand?.emojiFont || brand?.logoEmojiFont,
});
$: normalizedLogoUrl = normalizedBrand.logoUrl;
$: normalizedEmoji = normalizedBrand.emoji;
$: normalizedEmojiFont = normalizedBrand.emojiFont;
$: sizeClass = SIZE_CLASSES[size] || "";
$: animatedEmojiAssets = animatedEmojiAssetUrls(normalizedEmoji);
$: animatedEmojiSrc = animatedEmojiAssets.gif;
$: animatedEmojiFallbackSrc = animatedEmojiAssets.webp;
$: useAnimatedEmoji =
!normalizedLogoUrl &&
normalizedEmojiFont === "noto-color-animated" &&
animatedEmojiSrc &&
!animatedEmojiError;
$: if (normalizedLogoUrl !== lastLogoUrl) {
lastLogoUrl = normalizedLogoUrl;
loaded = false;
failed = false;
}
$: if (normalizedLogoUrl && !loaded && !failed) armLogoLoadTimeout();
$: if (!normalizedLogoUrl || loaded || failed) clearLogoLoadTimeout();
$: if (`${normalizedEmojiFont}:${normalizedEmoji}` !== lastAnimatedEmoji) {
lastAnimatedEmoji = `${normalizedEmojiFont}:${normalizedEmoji}`;
animatedEmojiError = false;
animatedEmojiStaticFallback = false;
}
onDestroy(() => {
clearLogoLoadTimeout();
});
onMount(() => {
loadEmojiFont(normalizedEmojiFont, normalizedEmoji);
});
$: if (normalizedEmojiFont && normalizedEmoji) {
loadEmojiFont(normalizedEmojiFont, normalizedEmoji);
}
function loadEmojiFont(font, text) {
if (typeof document === "undefined") return;
if (font === "system" || font === "noto-color-animated" || !font) {
fontLoaded = true;
loadedFontKey = "system";
return;
}
const fontOption = EMOJI_FONT_OPTIONS[font];
if (!fontOption) {
fontLoaded = true;
loadedFontKey = font;
return;
}
const fontUrl = fontOption.stylesheet(text);
const fontKey = `${font}:${text}`;
if (loadedFontKey === fontKey) return;
fontLoaded = false;
loadedFontKey = fontKey;
const existing = document.querySelector(`link[data-brand-emoji-font="${fontKey}"]`);
if (existing) {
fontLoaded = true;
return;
}
const link = document.createElement("link");
link.rel = "stylesheet";
link.href = fontUrl;
link.dataset.brandEmojiFont = fontKey;
link.onload = () => {
fontLoaded = true;
if (document.fonts && fontOption.cssFamily) {
document.fonts.load(`1em "${fontOption.cssFamily}"`, text).finally(() => {
fontLoaded = true;
});
}
};
link.onerror = () => {
fontLoaded = true;
};
document.head.appendChild(link);
}
function getEmojiFontClass(font) {
if (font === "noto-color") return "emoji-font-noto-color";
if (font === "noto-emoji") return "emoji-font-noto-emoji";
if (font === "twemoji") return "emoji-font-twemoji";
if (font === "openmoji") return "emoji-font-openmoji";
if (font === "apple") return "emoji-font-apple";
if (font === "segoe") return "emoji-font-segoe";
if (font === "noto-local") return "emoji-font-noto-local";
return "";
}
function clearLogoLoadTimeout() {
if (logoLoadTimer) {
window.clearTimeout(logoLoadTimer);
logoLoadTimer = null;
}
logoLoadTimerUrl = "";
}
function armLogoLoadTimeout() {
if (typeof window === "undefined") return;
if (logoLoadTimer && logoLoadTimerUrl === normalizedLogoUrl) return;
clearLogoLoadTimeout();
logoLoadTimerUrl = normalizedLogoUrl;
logoLoadTimer = window.setTimeout(() => {
if (logoLoadTimerUrl === normalizedLogoUrl && !loaded) failed = true;
}, LOGO_LOAD_TIMEOUT_MS);
}
</script>
<div
class={cn(
"brand-mark",
sizeClass,
animate && "brand-mark-animate",
normalizedLogoUrl && !failed && !loaded && "brand-mark-loading",
normalizedLogoUrl && !failed && loaded && "brand-mark-loaded",
className
)}
aria-busy={normalizedLogoUrl && !failed && !loaded ? "true" : undefined}
>
{#if normalizedLogoUrl && !failed}
{#if !loaded}
<span class="brand-mark-spinner" aria-hidden="true"></span>
{/if}
<img
class:loaded
src={normalizedLogoUrl}
alt=""
loading="eager"
decoding="async"
fetchpriority="high"
on:load={() => {
loaded = true;
clearLogoLoadTimeout();
}}
on:error={() => {
failed = true;
clearLogoLoadTimeout();
}}
/>
{:else if useAnimatedEmoji}
<img
class="brand-mark-animated-emoji loaded"
src={animatedEmojiStaticFallback ? animatedEmojiFallbackSrc : animatedEmojiSrc}
alt=""
loading="eager"
decoding="async"
fetchpriority="high"
on:error={() => {
if (!animatedEmojiStaticFallback && animatedEmojiFallbackSrc) {
animatedEmojiStaticFallback = true;
} else {
animatedEmojiError = true;
}
}}
/>
{:else}
<span
class={cn("brand-mark-emoji", getEmojiFontClass(normalizedEmojiFont))}
style="opacity: {fontLoaded ? 1 : 0}; transition: opacity 0.2s ease;"
>
{normalizedEmoji}
</span>
{/if}
</div>
<style>
.brand-mark {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
flex-shrink: 0;
overflow: visible;
font-size: 1.625rem;
}
.brand-mark img {
width: 100%;
height: 100%;
object-fit: contain;
opacity: 0;
transition: opacity 0.2s ease;
}
.brand-mark img.loaded {
opacity: 1;
}
.brand-mark.brand-mark-lg {
width: 4.125rem;
height: 4.125rem;
font-size: 2.875rem;
}
.brand-mark.brand-mark-xl {
width: 6rem;
height: 6rem;
font-size: 4.375rem;
}
.brand-mark img.brand-mark-animated-emoji {
object-fit: contain;
transform: scale(1.08);
}
.brand-mark.brand-mark-animate {
animation: brand-mark-pulse 2s ease-in-out infinite;
}
.brand-mark-spinner {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
}
.brand-mark-spinner::after {
content: "";
width: 1rem;
height: 1rem;
border: 2px solid currentColor;
border-bottom-color: transparent;
border-radius: 50%;
animation: brand-mark-spin 0.8s linear infinite;
opacity: 0.5;
}
@keyframes brand-mark-spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
@keyframes brand-mark-pulse {
0%,
100% {
transform: scale(1);
}
50% {
transform: scale(1.05);
}
}
.brand-mark-emoji {
color: inherit;
font-size: 1em;
line-height: 1;
display: inline-flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
transform: translateY(0.02em);
transform-origin: center;
}
.brand-mark-xl .brand-mark-emoji {
font-size: 1em;
}
.brand-mark-lg .brand-mark-emoji {
font-size: 1em;
}
.emoji-font-noto-color {
font-family: "Noto Color Emoji", sans-serif;
}
.emoji-font-noto-emoji {
color: var(--accent);
font-family: "Noto Emoji", sans-serif;
font-weight: 700;
}
.emoji-font-twemoji {
font-family: "Twemoji Mozilla", sans-serif;
}
.emoji-font-openmoji {
font-family: "OpenMoji Color", sans-serif;
}
.emoji-font-apple {
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
}
.emoji-font-segoe {
font-family: "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji", sans-serif;
}
.emoji-font-noto-local {
font-family: "Noto Color Emoji", "Noto Emoji", sans-serif;
}
</style>
+48 -10
View File
@@ -26,19 +26,57 @@ export function escapeHtml(value) {
.replaceAll("'", "&#39;");
}
export function applyFavicon(logoUrl, emoji) {
export function normalizeBrand(brand = {}) {
return {
title: String(brand.title || "/minishop").trim() || "/minishop",
logoUrl: String(brand.logoUrl || "").trim(),
emoji: String(brand.emoji || brand.logoEmoji || "🫥").trim() || "🫥",
emojiFont: String(brand.emojiFont || brand.logoEmojiFont || "system").trim() || "system",
};
}
export function emojiToCodepoints(value) {
return Array.from(String(value || "").trim())
.map((char) => char.codePointAt(0)?.toString(16))
.filter(Boolean)
.join("_");
}
export function animatedEmojiAssetUrls(emoji) {
const codepoints = emojiToCodepoints(emoji);
if (!codepoints) return { gif: "", webp: "" };
return {
gif: `/webapp-emoji/${codepoints}/512.gif`,
webp: `/webapp-emoji/${codepoints}/512.webp`,
};
}
export function brandFaviconHref(brand = {}) {
const normalizedBrand = normalizeBrand(brand);
if (normalizedBrand.logoUrl) return normalizedBrand.logoUrl;
if (normalizedBrand.emojiFont === "noto-color-animated") {
return animatedEmojiAssetUrls(normalizedBrand.emoji).gif;
}
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><text x="50%" y="50%" dominant-baseline="central" text-anchor="middle" font-size="52">${escapeHtml(normalizedBrand.emoji)}</text></svg>`;
return `data:image/svg+xml,${encodeURIComponent(svg)}`;
}
export function applyFavicon(brand = {}) {
if (typeof document === "undefined") return;
const favicon = document.getElementById("app-favicon");
if (!favicon) return;
const normalizedLogoUrl = String(logoUrl || "").trim();
if (normalizedLogoUrl) {
favicon.setAttribute("href", normalizedLogoUrl);
return;
const href = brandFaviconHref(brand);
favicon.setAttribute("href", href);
if (href.startsWith("data:image/svg+xml")) {
favicon.setAttribute("type", "image/svg+xml");
} else if (href.endsWith(".gif")) {
favicon.setAttribute("type", "image/gif");
} else if (href.endsWith(".webp")) {
favicon.setAttribute("type", "image/webp");
} else {
favicon.removeAttribute("type");
}
const normalizedEmoji = String(emoji || "????").trim() || "????";
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><text x="50%" y="50%" dominant-baseline="central" text-anchor="middle" font-size="52">${escapeHtml(normalizedEmoji)}</text></svg>`;
const encoded = encodeURIComponent(svg);
favicon.setAttribute("href", `data:image/svg+xml,${encoded}`);
}
@@ -4,6 +4,7 @@ export const DEV_MOCK = {
primaryColor: "#00fe7a",
logoUrl: "",
logoEmoji: "🫥",
logoEmojiFont: "system",
apiBase: "/api",
supportUrl: "https://t.me/support",
privacyPolicyUrl: "https://example.com/privacy",
@@ -205,6 +205,7 @@
}
.admin-setting-control .input,
.admin-setting-control .admin-setting-select,
.admin-setting-control input[type="text"],
.admin-setting-control input[type="number"] {
flex: 1 1 160px;
@@ -290,4 +291,4 @@
.admin-message-actions .admin-btn {
height: 36px;
}
}
+2 -2
View File
@@ -109,7 +109,7 @@ a {
width: 38px;
height: 38px;
flex: 0 0 auto;
overflow: hidden;
overflow: visible;
place-items: center;
color: var(--accent);
font-size: 26px;
@@ -2316,4 +2316,4 @@ a {
white-space: nowrap;
min-width: 0;
}
}
}
@@ -7,15 +7,14 @@
Smartphone,
} from "$components/ui/icons.js";
import BrandMark from "../BrandMark.svelte";
import BrandMark from "$lib/webapp/BrandMark.svelte";
export let activeTab = "home";
export let brand = {};
export let brandTitle = "";
export let devicesEnabled = false;
export let hasUnlinkedIdentity = false;
export let isAdmin = false;
export let logoEmoji = "";
export let logoUrl = "";
export let onAdmin = () => {};
export let onDevices = () => {};
export let onHome = () => {};
@@ -26,7 +25,7 @@
<nav class:bottom-nav-devices={devicesEnabled} class="bottom-nav" aria-label={t("wa_navigation")}>
<div class="rail-brand" aria-hidden="true">
<BrandMark {logoUrl} emoji={logoEmoji} />
<BrandMark {brand} />
<strong>{brandTitle}</strong>
</div>
<button class:active={activeTab === "home"} type="button" onclick={onHome}>
@@ -1,12 +1,11 @@
<script>
import BrandMark from "../BrandMark.svelte";
import BrandMark from "$lib/webapp/BrandMark.svelte";
import BottomNav from "./BottomNav.svelte";
export let screen;
export let activeTab;
export let CFG;
export let brand = {};
export let brandTitle;
export let brandEmoji;
export let devicesEnabled;
export let hasUnlinkedIdentity;
export let isAdmin;
@@ -22,7 +21,7 @@
{#if screen === "invite" || screen === "devices" || screen === "settings"}
<header class="app-header accent-title">
<div class="brand-row">
<BrandMark logoUrl={CFG.logoUrl} emoji={brandEmoji} />
<BrandMark {brand} />
<strong>{brandTitle}</strong>
</div>
</header>
@@ -32,12 +31,11 @@
<BottomNav
{activeTab}
{brand}
{brandTitle}
{devicesEnabled}
{hasUnlinkedIdentity}
{isAdmin}
logoEmoji={brandEmoji}
logoUrl={CFG.logoUrl}
onAdmin={openAdminPanel}
onDevices={goDevices}
onHome={goHome}
@@ -3,7 +3,7 @@
import { Tooltip } from "$components/ui/primitives.js";
import Button from "$components/ui/button.svelte";
import BrandMark from "../../BrandMark.svelte";
import BrandMark from "$lib/webapp/BrandMark.svelte";
import Card from "$components/ui/card.svelte";
import Input from "$components/ui/input.svelte";
import Spinner from "$components/ui/spinner.svelte";
@@ -11,8 +11,8 @@
export let screen;
export let CFG;
export let brand = {};
export let brandTitle;
export let brandEmoji;
export let email;
export let emailCode;
export let pendingEmail;
@@ -87,7 +87,7 @@
{:else}
<div class="auth-card-wrap">
<div class="login-brand login-brand-auth">
<BrandMark class="brand-mark-xl" logoUrl={CFG.logoUrl} emoji={brandEmoji} />
<BrandMark {brand} size="xl" />
<h1>{brandTitle}</h1>
</div>
<Card class="auth-card">
@@ -9,7 +9,7 @@
RefreshCw,
} from "$components/ui/icons.js";
import BrandMark from "../../BrandMark.svelte";
import BrandMark from "$lib/webapp/BrandMark.svelte";
import Button from "$components/ui/button.svelte";
import Card from "$components/ui/card.svelte";
import { LinearProgress } from "$components/patterns/webapp/index.js";
@@ -25,9 +25,8 @@
activeSubscriptionTermLabel as activeSubscriptionTermLabelFn,
} from "../../lib/webapp/traffic.js";
export let CFG = {};
export let appSettings = {};
export let brandEmoji = "";
export let brand = {};
export let brandTitle = "";
export let canChangeTariff = false;
export let premiumTrafficTopupBarClickable = false;
@@ -83,7 +82,7 @@
<main class="home-layout">
<div class="login-brand home-brand">
<BrandMark class="brand-mark-xl" logoUrl={CFG.logoUrl} emoji={brandEmoji} />
<BrandMark {brand} size="xl" />
<h1>{brandTitle}</h1>
</div>