Get started
Progettazione dell'importazione di temi personalizzati tweakcn
Progettazione dell'importazione di temi personalizzati tweakcn
Stato: approvato nel terminale il 2026-04-22
Riepilogo
Aggiungere esattamente uno slot di tema personalizzato della UI di controllo, locale al browser, importabile da un link di condivisione tweakcn. Le famiglie di temi integrate esistenti restano claw, knot e dash. La nuova famiglia custom si comporta come una normale famiglia di temi OpenClaw e supporta le modalità light, dark e system quando il payload tweakcn importato include sia il set di token chiaro sia quello scuro.
Il tema importato viene archiviato solo nel profilo del browser corrente insieme al resto delle impostazioni della UI di controllo. Non viene scritto nella configurazione del Gateway e non viene sincronizzato tra dispositivi o browser.
Problema
Il sistema di temi della UI di controllo è attualmente chiuso su tre famiglie di temi codificate rigidamente:
ui/src/ui/theme.tsui/src/ui/views/config.tsui/src/styles/base.css
Gli utenti possono passare tra famiglie integrate e varianti di modalità, ma non possono importare un tema da tweakcn senza modificare il CSS del repo. Il risultato richiesto è più limitato di un sistema generale di tematizzazione: mantenere i tre temi integrati e aggiungere uno slot importato controllato dall'utente, sostituibile da un link tweakcn.
Obiettivi
- Mantenere invariate le famiglie di temi integrate esistenti.
- Aggiungere esattamente uno slot personalizzato importato, non una libreria di temi.
- Accettare un link di condivisione tweakcn o un URL diretto
https://tweakcn.com/r/themes/{id}. - Rendere persistente il tema importato solo nello storage locale del browser.
- Far funzionare lo slot importato con i controlli di modalità
light,darkesystemesistenti. - Mantenere sicuro il comportamento in caso di errore: un'importazione non valida non deve mai rompere il tema attivo della UI.
Non obiettivi
- Nessuna libreria multi-tema o lista locale al browser di importazioni.
- Nessuna persistenza lato Gateway o sincronizzazione tra dispositivi.
- Nessun editor CSS arbitrario o editor JSON grezzo del tema.
- Nessun caricamento automatico di asset di font remoti da tweakcn.
- Nessun tentativo di supportare payload tweakcn che espongono una sola modalità.
- Nessun refactor della tematizzazione a livello di repo oltre alle interfacce richieste per la UI di controllo.
Decisioni utente già prese
- Mantenere i tre temi integrati.
- Aggiungere uno slot di importazione alimentato da tweakcn.
- Archiviare il tema importato nel browser, non nella configurazione del Gateway.
- Supportare
light,darkesystemper lo slot importato. - Sovrascrivere lo slot personalizzato con l'importazione successiva è il comportamento previsto.
Approccio consigliato
Aggiungere un quarto id di famiglia di temi, custom, al modello dei temi della UI di controllo. La famiglia custom diventa selezionabile solo quando è presente un'importazione tweakcn valida. Il payload importato viene normalizzato in un record di tema personalizzato specifico di OpenClaw e archiviato nello storage locale del browser insieme al resto delle impostazioni della UI.
A runtime, OpenClaw renderizza un tag <style> gestito che definisce i blocchi di variabili CSS personalizzate risolti:
:root[data-theme="custom"] { ... }
:root[data-theme="custom-light"] { ... }
Questo mantiene le variabili del tema personalizzato limitate alla famiglia custom ed evita di far trapelare variabili CSS inline nelle famiglie integrate.
Architettura
Modello del tema
Aggiornare ui/src/ui/theme.ts:
- Estendere
ThemeNameper includerecustom. - Estendere
ResolvedThemeper includerecustomecustom-light. - Estendere
VALID_THEME_NAMES. - Aggiornare
resolveTheme()in modo checustomrispecchi il comportamento delle famiglie esistenti:custom + dark->customcustom + light->custom-lightcustom + system->customocustom-lightin base alla preferenza del sistema operativo
Non vengono aggiunti alias legacy per custom.
Modello di persistenza
Estendere la persistenza di UiSettings in ui/src/ui/storage.ts con un payload opzionale per il tema personalizzato:
customTheme?: ImportedCustomTheme
Forma archiviata consigliata:
type ImportedCustomTheme = {
sourceUrl: string;
themeId: string;
label: string;
importedAt: string;
light: Record<string, string>;
dark: Record<string, string>;
};
Note:
sourceUrlarchivia l'input originale dell'utente dopo la normalizzazione.themeIdè l'id del tema tweakcn estratto dall'URL.labelè il camponamedi tweakcn quando presente, altrimentiCustom.lightedarksono mappe di token OpenClaw già normalizzate, non payload tweakcn grezzi.- Il payload importato vive accanto alle altre impostazioni locali al browser ed è serializzato nello stesso documento di local storage.
- Se i dati archiviati del tema personalizzato mancano o non sono validi al caricamento, ignorare il payload e tornare a
theme: "claw"quando la famiglia persistita eracustom.
Applicazione runtime
Aggiungere un gestore ristretto del foglio di stile del tema personalizzato nel runtime della UI di controllo, vicino a ui/src/ui/app-settings.ts e ui/src/ui/theme.ts.
Responsabilità:
- Creare o aggiornare un tag
<style id="openclaw-custom-theme">stabile indocument.head. - Emettere CSS solo quando esiste un payload di tema personalizzato valido.
- Rimuovere il contenuto del tag style quando il payload viene cancellato.
- Mantenere il CSS delle famiglie integrate in
ui/src/styles/base.css; non innestare token importati nel foglio di stile versionato nel repo.
Questo gestore viene eseguito ogni volta che le impostazioni vengono caricate, salvate, importate o cancellate.
Selettori della modalità chiara
L'implementazione dovrebbe preferire data-theme-mode="light" per gli stili chiari trasversali alle famiglie, invece di trattare custom-light come caso speciale. Se un selettore esistente è vincolato a data-theme="light" e deve applicarsi a ogni famiglia chiara, ampliarlo come parte di questo lavoro.
UX di importazione
Aggiornare ui/src/ui/views/config.ts nella sezione Aspetto:
- Aggiungere una scheda tema
Personalizzatoaccanto aClaw,KnoteDash. - Mostrare la scheda come disabilitata quando non esiste alcun tema personalizzato importato.
- Aggiungere un pannello di importazione sotto la griglia dei temi con:
- un input di testo per un link di condivisione tweakcn o un URL
/r/themes/{id} - un pulsante
Importa - un percorso
Sostituisciquando esiste già un payload personalizzato - un'azione
Cancellaquando esiste già un payload personalizzato
- un input di testo per un link di condivisione tweakcn o un URL
- Mostrare l'etichetta del tema importato e l'host sorgente quando esiste un payload.
- Se il tema attivo è
custom, l'importazione di una sostituzione viene applicata immediatamente. - Se il tema attivo non è
custom, l'importazione archivia solo il nuovo payload finché l'utente non seleziona la schedaPersonalizzato.
Anche il selettore rapido del tema in ui/src/ui/views/config-quick.ts dovrebbe mostrare Personalizzato solo quando esiste un payload.
Analisi dell'URL e fetch remoto
Il percorso di importazione nel browser accetta:
https://tweakcn.com/themes/{id}https://tweakcn.com/r/themes/{id}
L'implementazione dovrebbe normalizzare entrambe le forme in:
https://tweakcn.com/r/themes/{id}
Il browser quindi effettua direttamente il fetch dell'endpoint /r/themes/{id} normalizzato.
Usare un validatore di schema ristretto per il payload esterno. Uno schema zod è preferito perché questo è un confine esterno non attendibile.
Campi remoti richiesti:
namedi primo livello come stringa opzionalecssVars.themecome oggetto opzionalecssVars.lightcome oggettocssVars.darkcome oggetto
Se manca cssVars.light o cssVars.dark, rifiutare l'importazione. Questo è intenzionale: il comportamento di prodotto approvato è il supporto completo delle modalità, non una sintesi best-effort del lato mancante.
Mappatura dei token
Non replicare alla cieca le variabili tweakcn. Normalizzare un sottoinsieme limitato in token OpenClaw e derivare il resto in un helper.
Token importati direttamente
Da ciascun blocco modalità tweakcn:
backgroundforegroundcardcard-foregroundpopoverpopover-foregroundprimaryprimary-foregroundsecondarysecondary-foregroundmutedmuted-foregroundaccentaccent-foregrounddestructivedestructive-foregroundborderinputringradius
Da cssVars.theme condiviso quando presente:
font-sansfont-mono
Se un blocco modalità sovrascrive font-sans, font-mono o radius, vince il valore locale alla modalità.
Token derivati per OpenClaw
L'importatore deriva variabili solo OpenClaw dai colori di base importati:
--bg-accent--bg-elevated--bg-hover--panel--panel-strong--panel-hover--chrome--chrome-strong--text--text-strong--chat-text--muted--muted-strong--accent-hover--accent-muted--accent-subtle--accent-glow--focus--focus-ring--focus-glow--secondary--secondary-foreground--danger--danger-muted--danger-subtle
Le regole di derivazione vivono in un helper puro, così possono essere testate in modo indipendente. Le formule esatte di miscelazione dei colori sono un dettaglio implementativo, ma l'helper deve soddisfare due vincoli:
- preservare un contrasto leggibile vicino all'intento del tema importato
- produrre output stabile per lo stesso payload importato
Token ignorati nella v1
Questi token tweakcn vengono intenzionalmente ignorati nella prima versione:
chart-*sidebar-*font-serifshadow-*tracking-*letter-spacingspacing
Questo mantiene l'ambito sui token di cui l'attuale UI di controllo ha effettivamente bisogno.
Font
Le stringhe dello stack di font vengono importate se presenti, ma OpenClaw non carica asset di font remoti nella v1. Se lo stack importato fa riferimento a font non disponibili nel browser, si applica il normale comportamento di fallback.
Comportamento in caso di errore
Le importazioni non valide devono fallire in modo chiuso.
- Formato URL non valido: mostrare un errore di validazione inline, non effettuare il fetch.
- Host o forma del percorso non supportati: mostrare un errore di validazione inline, non effettuare il fetch.
- Errore di rete, risposta non OK o JSON malformato: mostrare un errore inline, mantenere intatto il payload attualmente archiviato.
- Errore di schema o blocchi light/dark mancanti: mostrare un errore inline, mantenere intatto il payload attualmente archiviato.
- Azione di cancellazione:
- rimuove il payload personalizzato archiviato
- rimuove il contenuto del tag style personalizzato gestito
- se
customè attivo, riporta la famiglia di temi aclaw
- Payload personalizzato archiviato non valido al primo caricamento:
- ignorare il payload archiviato
- non emettere CSS personalizzato
- se la famiglia di temi persistita era
custom, tornare aclaw
In nessun momento un'importazione non riuscita dovrebbe lasciare il documento attivo con variabili CSS personalizzate parziali applicate.
File che dovrebbero cambiare nell'implementazione
File principali:
ui/src/ui/theme.tsui/src/ui/storage.tsui/src/ui/app-settings.tsui/src/ui/views/config.tsui/src/ui/views/config-quick.tsui/src/styles/base.css
Helper probabilmente nuovi:
ui/src/ui/custom-theme.ts
Test:
ui/src/ui/app-settings.test.tsui/src/ui/storage.node.test.tsui/src/ui/views/config.browser.test.ts- nuovi test mirati per l'analisi degli URL e la normalizzazione dei payload
Test
Copertura minima dell'implementazione:
- analizzare l'URL del link di condivisione nell'id del tema tweakcn
- normalizzare
/themes/{id}e/r/themes/{id}nell'URL di fetch - rifiutare host non supportati e id malformati
- validare la forma del payload tweakcn
- mappare un payload tweakcn valido in mappe di token OpenClaw chiara e scura normalizzate
- caricare e salvare il payload personalizzato nelle impostazioni locali al browser
- risolvere
customperlight,darkesystem - disabilitare la selezione di
Personalizzatoquando non esiste alcun payload - applicare immediatamente il tema importato quando
customè già attivo - tornare a
clawquando il tema personalizzato attivo viene cancellato
Obiettivo di verifica manuale:
- importare un tema tweakcn noto dalle Impostazioni
- passare tra
light,darkesystem - passare tra
custome le famiglie integrate - ricaricare la pagina e confermare che il tema personalizzato importato persiste localmente
Note di rollout
Questa funzionalità è intenzionalmente piccola. Se in seguito gli utenti chiedono più temi importati, rinomina, esportazione o sincronizzazione tra dispositivi, trattarlo come una progettazione successiva. Non pre-costruire un'astrazione di libreria di temi in questa implementazione.