Get started
Дизайн імпорту користувацької теми Tweakcn
Дизайн імпорту користувацької теми Tweakcn
Статус: схвалено в терміналі 2026-04-22
Підсумок
Додати рівно один браузерно-локальний слот користувацької теми Control UI, який можна імпортувати з посилання поширення tweakcn. Наявні вбудовані сімейства тем залишаються claw, knot і dash. Нове сімейство custom поводиться як звичайне сімейство тем OpenClaw і підтримує режими light, dark і system, коли імпортований payload tweakcn містить набори токенів і для світлої, і для темної теми.
Імпортована тема зберігається лише в поточному профілі браузера разом з рештою налаштувань Control UI. Вона не записується в конфігурацію gateway і не синхронізується між пристроями чи браузерами.
Проблема
Система тем Control UI зараз замкнена на трьох жорстко закодованих сімействах тем:
ui/src/ui/theme.tsui/src/ui/views/config.tsui/src/styles/base.css
Користувачі можуть перемикатися між вбудованими сімействами та варіантами режимів, але не можуть додати тему з tweakcn без редагування CSS репозиторію. Запитаний результат менший за загальну систему темізації: залишити три вбудовані теми й додати один керований користувачем імпортований слот, який можна замінити з посилання tweakcn.
Цілі
- Залишити наявні вбудовані сімейства тем без змін.
- Додати рівно один імпортований користувацький слот, а не бібліотеку тем.
- Приймати посилання поширення tweakcn або прямий URL
https://tweakcn.com/r/themes/{id}. - Зберігати імпортовану тему лише в локальному сховищі браузера.
- Забезпечити роботу імпортованого слота з наявними елементами керування режимами
light,darkіsystem. - Зберегти безпечну поведінку в разі помилок: невдалий імпорт ніколи не ламає активну тему UI.
Не цілі
- Жодної багатотемної бібліотеки чи браузерно-локального списку імпортів.
- Жодного збереження на боці gateway або синхронізації між пристроями.
- Жодного довільного редактора CSS або редактора сирого JSON теми.
- Жодного автоматичного завантаження віддалених ресурсів шрифтів із tweakcn.
- Жодної спроби підтримувати payload-и tweakcn, які відкривають лише один режим.
- Жодного загальнорепозиторного рефакторингу темізації поза швами, потрібними для Control UI.
Уже ухвалені користувацькі рішення
- Залишити три вбудовані теми.
- Додати один слот імпорту на основі tweakcn.
- Зберігати імпортовану тему в браузері, а не в конфігурації gateway.
- Підтримувати
light,darkіsystemдля імпортованого слота. - Перезапис користувацького слота наступним імпортом є очікуваною поведінкою.
Рекомендований підхід
Додати четвертий ідентифікатор сімейства тем, custom, до моделі тем Control UI. Сімейство custom стає доступним для вибору лише тоді, коли наявний валідний імпорт tweakcn. Імпортований payload нормалізується в специфічний для OpenClaw запис користувацької теми й зберігається в локальному сховищі браузера разом з рештою налаштувань UI.
Під час виконання OpenClaw рендерить керований тег <style>, який визначає розв’язані блоки CSS-змінних користувацької теми:
:root[data-theme="custom"] { ... }
:root[data-theme="custom-light"] { ... }
Це зберігає змінні користувацької теми в межах сімейства custom і запобігає витоку inline CSS-змінних у вбудовані сімейства.
Архітектура
Модель теми
Оновити ui/src/ui/theme.ts:
- Розширити
ThemeName, щоб включитиcustom. - Розширити
ResolvedTheme, щоб включитиcustomіcustom-light. - Розширити
VALID_THEME_NAMES. - Оновити
resolveTheme(), щобcustomвіддзеркалював наявну поведінку сімейств:custom + dark->customcustom + light->custom-lightcustom + system->customабоcustom-lightзалежно від налаштування ОС
Для custom не додаються застарілі псевдоніми.
Модель збереження
Розширити збереження UiSettings в ui/src/ui/storage.ts одним необов’язковим payload-ом користувацької теми:
customTheme?: ImportedCustomTheme
Рекомендована форма збереження:
type ImportedCustomTheme = {
sourceUrl: string;
themeId: string;
label: string;
importedAt: string;
light: Record<string, string>;
dark: Record<string, string>;
};
Примітки:
sourceUrlзберігає початкове введення користувача після нормалізації.themeId— це ідентифікатор теми tweakcn, витягнутий з URL.label— це полеnametweakcn, якщо воно наявне, інакшеCustom.lightіdark— це вже нормалізовані мапи токенів OpenClaw, а не сирі payload-и tweakcn.- Імпортований payload живе поруч з іншими браузерно-локальними налаштуваннями й серіалізується в тому самому документі local-storage.
- Якщо збережені дані користувацької теми відсутні або невалідні під час завантаження, ігнорувати payload і повертатися до
theme: "claw", коли збережене сімейство булоcustom.
Застосування під час виконання
Додати вузький менеджер stylesheet користувацької теми в runtime Control UI, поруч із ui/src/ui/app-settings.ts і ui/src/ui/theme.ts.
Обов’язки:
- Створювати або оновлювати один стабільний тег
<style id="openclaw-custom-theme">уdocument.head. - Виводити CSS лише тоді, коли існує валідний payload користувацької теми.
- Очищати вміст style-тега, коли payload очищено.
- Тримати CSS вбудованих сімейств у
ui/src/styles/base.css; не вставляти імпортовані токени в stylesheet, що зберігається в репозиторії.
Цей менеджер запускається щоразу, коли налаштування завантажуються, зберігаються, імпортуються або очищаються.
Селектори світлого режиму
Реалізація має віддавати перевагу data-theme-mode="light" для світлих стилів між сімействами, а не спеціальній обробці custom-light. Якщо наявний селектор прив’язаний до data-theme="light" і має застосовуватися до кожного світлого сімейства, розширити його в межах цієї роботи.
UX імпорту
Оновити ui/src/ui/views/config.ts у розділі Appearance:
- Додати картку теми
Customпоруч ізClaw,KnotіDash. - Показувати картку як вимкнену, коли імпортованої користувацької теми немає.
- Додати панель імпорту під сіткою тем із:
- одним текстовим полем для посилання поширення tweakcn або URL
/r/themes/{id} - однією кнопкою
Import - одним шляхом
Replace, коли користувацький payload уже існує - однією дією
Clear, коли користувацький payload уже існує
- одним текстовим полем для посилання поширення tweakcn або URL
- Показувати мітку імпортованої теми та хост джерела, коли payload існує.
- Якщо активна тема —
custom, імпорт заміни застосовується негайно. - Якщо активна тема не
custom, імпорт лише зберігає новий payload, доки користувач не вибере карткуCustom.
Швидкий вибір теми в ui/src/ui/views/config-quick.ts також має показувати Custom лише тоді, коли payload існує.
Парсинг URL і віддалене завантаження
Браузерний шлях імпорту приймає:
https://tweakcn.com/themes/{id}https://tweakcn.com/r/themes/{id}
Реалізація має нормалізувати обидві форми до:
https://tweakcn.com/r/themes/{id}
Після цього браузер напряму отримує нормалізований endpoint /r/themes/{id}.
Використати вузький валідатор schema для зовнішнього payload. Бажано використати zod, бо це недовірена зовнішня межа.
Обов’язкові віддалені поля:
- top-level
nameяк необов’язковий рядок cssVars.themeяк необов’язковий об’єктcssVars.lightяк об’єктcssVars.darkяк об’єкт
Якщо cssVars.light або cssVars.dark відсутній, відхилити імпорт. Це навмисно: схвалена продуктова поведінка — повна підтримка режимів, а не best-effort синтез відсутньої сторони.
Мапінг токенів
Не дзеркалити змінні tweakcn сліпо. Нормалізувати обмежену підмножину в токени OpenClaw і виводити решту в helper.
Токени, що імпортуються напряму
З кожного блока режиму tweakcn:
backgroundforegroundcardcard-foregroundpopoverpopover-foregroundprimaryprimary-foregroundsecondarysecondary-foregroundmutedmuted-foregroundaccentaccent-foregrounddestructivedestructive-foregroundborderinputringradius
Із спільного cssVars.theme, коли він наявний:
font-sansfont-mono
Якщо блок режиму перевизначає font-sans, font-mono або radius, перемагає локальне для режиму значення.
Токени, виведені для OpenClaw
Імпортер виводить змінні лише для OpenClaw з імпортованих базових кольорів:
--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
Правила виведення живуть у чистому helper, щоб їх можна було тестувати незалежно. Точні формули змішування кольорів є деталлю реалізації, але helper має задовольняти дві умови:
- зберігати читабельний контраст, близький до задуму імпортованої теми
- створювати стабільний output для того самого імпортованого payload
Токени, проігноровані у v1
Ці токени tweakcn навмисно ігноруються в першій версії:
chart-*sidebar-*font-serifshadow-*tracking-*letter-spacingspacing
Це утримує scope на токенах, які поточному Control UI фактично потрібні.
Шрифти
Рядки стеків шрифтів імпортуються, якщо наявні, але OpenClaw не завантажує віддалені ресурси шрифтів у v1. Якщо імпортований стек посилається на шрифти, недоступні в браузері, застосовується звичайна поведінка fallback.
Поведінка в разі помилки
Невдалі імпорти мають завершуватися закрито.
- Невалідний формат URL: показати inline-помилку валідації, не виконувати fetch.
- Непідтримуваний host або форма path: показати inline-помилку валідації, не виконувати fetch.
- Мережева помилка, не-OK відповідь або malformed JSON: показати inline-помилку, залишити поточний збережений payload без змін.
- Помилка schema або відсутні блоки light/dark: показати inline-помилку, залишити поточний збережений payload без змін.
- Дія очищення:
- видаляє збережений користувацький payload
- видаляє вміст керованого style-тега користувацької теми
- якщо
customактивний, перемикає сімейство тем назад наclaw
- Невалідний збережений payload користувацької теми під час першого завантаження:
- ігнорувати збережений payload
- не виводити користувацький CSS
- якщо збережене сімейство тем було
custom, повернутися доclaw
У жоден момент невдалий імпорт не має залишати активний документ із частково застосованими користувацькими CSS-змінними.
Файли, які очікувано зміняться під час реалізації
Основні файли:
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-и:
ui/src/ui/custom-theme.ts
Тести:
ui/src/ui/app-settings.test.tsui/src/ui/storage.node.test.tsui/src/ui/views/config.browser.test.ts- нові сфокусовані тести для парсингу URL і нормалізації payload
Тестування
Мінімальне покриття реалізації:
- розпарсити URL посилання поширення в ідентифікатор теми tweakcn
- нормалізувати
/themes/{id}і/r/themes/{id}у fetch URL - відхиляти непідтримувані host-и та malformed id
- валідувати форму payload tweakcn
- мапити валідний payload tweakcn у нормалізовані мапи світлих і темних токенів OpenClaw
- завантажувати й зберігати користувацький payload у браузерно-локальних налаштуваннях
- розв’язувати
customдляlight,darkіsystem - вимикати вибір
Custom, коли payload відсутній - застосовувати імпортовану тему негайно, коли
customуже активний - повертатися до
claw, коли активну користувацьку тему очищено
Ціль ручної перевірки:
- імпортувати відому тему tweakcn із Settings
- перемикатися між
light,darkіsystem - перемикатися між
customі вбудованими сімействами - перезавантажити сторінку й підтвердити, що імпортована користувацька тема зберігається локально
Нотатки щодо rollout
Ця функція навмисно мала. Якщо користувачі пізніше попросять кілька імпортованих тем, перейменування, експорт або синхронізацію між пристроями, розглядати це як подальший дизайн. Не будувати заздалегідь абстракцію бібліотеки тем у цій реалізації.