@@ -29,7 +29,6 @@ export * from './room';
|
||||
export * from './room/events';
|
||||
export * from './room/widgets';
|
||||
export * from './soundboard';
|
||||
export * from './theme';
|
||||
export * from './ui-settings';
|
||||
export * from './user';
|
||||
export * from './utils';
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
import { NitroLogger } from '@nitrots/nitro-renderer';
|
||||
import { GetConfigurationValue } from '../nitro';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Custom theme ecosystem (graphics-only, runtime-loaded).
|
||||
//
|
||||
// A "theme" is a folder on the server (NOT bundled in the build) made of:
|
||||
// <base>/index.json -> { "themes": [ { id, name, author? } ] }
|
||||
// <base>/<id>/theme.json -> { name, pieces: [ { id, name, file } ] }
|
||||
// <base>/<id>/<file>.css -> one CSS "piece" (cards, chat, catalog, ...)
|
||||
//
|
||||
// Each enabled piece is injected as a <link> in <head>. If a piece fails to
|
||||
// load (404 / network) the link removes itself, so the UI falls back to the
|
||||
// default look for that piece (per-piece fallback, never breaks the client).
|
||||
//
|
||||
// The base url is configurable via ui-config ("theme.base.url") so themes can
|
||||
// live anywhere (and never need a client rebuild to add/change them).
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface ThemeInfo
|
||||
{
|
||||
id: string;
|
||||
name: string;
|
||||
author?: string;
|
||||
}
|
||||
|
||||
export interface ThemePiece
|
||||
{
|
||||
id: string;
|
||||
name: string;
|
||||
file: string;
|
||||
}
|
||||
|
||||
export interface ThemeManifest
|
||||
{
|
||||
name: string;
|
||||
pieces: ThemePiece[];
|
||||
}
|
||||
|
||||
const LINK_ATTR = 'data-nitro-theme';
|
||||
|
||||
export const GetThemeBaseUrl = (): string =>
|
||||
GetConfigurationValue<string>('theme.base.url', 'custom-themes').replace(/\/+$/, '');
|
||||
|
||||
export const FetchThemeIndex = async (): Promise<ThemeInfo[]> =>
|
||||
{
|
||||
try
|
||||
{
|
||||
const response = await fetch(`${ GetThemeBaseUrl() }/index.json`, { cache: 'no-cache' });
|
||||
|
||||
if(!response.ok) return [];
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
return Array.isArray(data?.themes) ? data.themes.filter((t: any) => t && t.id) : [];
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
NitroLogger.warn('[ThemeManager] index.json non caricabile, nessun tema custom', error);
|
||||
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
export const FetchThemeManifest = async (themeId: string): Promise<ThemeManifest> =>
|
||||
{
|
||||
if(!themeId) return null;
|
||||
|
||||
try
|
||||
{
|
||||
const response = await fetch(`${ GetThemeBaseUrl() }/${ themeId }/theme.json`, { cache: 'no-cache' });
|
||||
|
||||
if(!response.ok) return null;
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if(!data || !Array.isArray(data.pieces)) return null;
|
||||
|
||||
return {
|
||||
name: data.name ?? themeId,
|
||||
pieces: data.pieces.filter((p: any) => p && p.id && p.file)
|
||||
};
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
NitroLogger.warn(`[ThemeManager] manifest non valido per tema "${ themeId }" -> fallback default`, error);
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const ClearTheme = (): void =>
|
||||
{
|
||||
document.head.querySelectorAll(`link[${ LINK_ATTR }]`).forEach(node => node.remove());
|
||||
};
|
||||
|
||||
export const ApplyThemePieces = (themeId: string, pieces: ThemePiece[]): void =>
|
||||
{
|
||||
ClearTheme();
|
||||
|
||||
if(!themeId || !pieces || !pieces.length) return;
|
||||
|
||||
const base = GetThemeBaseUrl();
|
||||
|
||||
for(const piece of pieces)
|
||||
{
|
||||
const link = document.createElement('link');
|
||||
|
||||
link.rel = 'stylesheet';
|
||||
link.setAttribute(LINK_ATTR, piece.id);
|
||||
link.href = `${ base }/${ themeId }/${ piece.file }`;
|
||||
|
||||
// Per-piece fallback: a broken piece removes itself, leaving the default.
|
||||
link.onerror = () =>
|
||||
{
|
||||
NitroLogger.warn(`[ThemeManager] pezzo tema rotto "${ themeId }/${ piece.file }" -> fallback default`);
|
||||
link.remove();
|
||||
};
|
||||
|
||||
document.head.appendChild(link);
|
||||
}
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export * from './ThemeManager';
|
||||
@@ -4,7 +4,4 @@ export class LocalStorageKeys
|
||||
public static CATALOG_SKIP_PURCHASE_CONFIRMATION: string = 'catalogSkipPurchaseConfirmation';
|
||||
public static CHAT_WINDOW_ENABLED: string = 'chatWindowEnabled';
|
||||
public static CHAT_TRANSLATION_SETTINGS: string = 'chatTranslationSettings';
|
||||
public static CATALOG_CLASSIC_STYLE: string = 'catalogClassicStyle';
|
||||
public static THEME_ACTIVE: string = 'nitroThemeActive';
|
||||
public static THEME_PIECES: string = 'nitroThemePieces';
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 1010 B |
|
Before Width: | Height: | Size: 676 B |
|
Before Width: | Height: | Size: 255 B |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 206 B |
|
Before Width: | Height: | Size: 133 B |
|
Before Width: | Height: | Size: 547 B |
|
Before Width: | Height: | Size: 322 B |
|
Before Width: | Height: | Size: 211 B |
|
Before Width: | Height: | Size: 265 B |
|
Before Width: | Height: | Size: 274 B |
|
Before Width: | Height: | Size: 198 B |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 113 B |
|
Before Width: | Height: | Size: 299 B |
|
Before Width: | Height: | Size: 293 B |
|
Before Width: | Height: | Size: 153 B |
|
Before Width: | Height: | Size: 147 B |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 442 B |
|
Before Width: | Height: | Size: 426 B |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 443 B |
|
Before Width: | Height: | Size: 423 B |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 437 B |
|
Before Width: | Height: | Size: 598 B |
|
Before Width: | Height: | Size: 428 B |
|
Before Width: | Height: | Size: 428 B |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 358 B |
|
Before Width: | Height: | Size: 286 B |
|
Before Width: | Height: | Size: 174 B |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 445 B |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 447 B |
|
Before Width: | Height: | Size: 452 B |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 430 B |
|
Before Width: | Height: | Size: 430 B |
|
Before Width: | Height: | Size: 138 B |
|
Before Width: | Height: | Size: 431 B |
|
Before Width: | Height: | Size: 430 B |
|
Before Width: | Height: | Size: 426 B |
|
Before Width: | Height: | Size: 431 B |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 182 B |
|
Before Width: | Height: | Size: 175 B |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 441 B |
|
Before Width: | Height: | Size: 447 B |
|
Before Width: | Height: | Size: 455 B |
|
Before Width: | Height: | Size: 430 B |
|
Before Width: | Height: | Size: 213 B |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |