chore(i18n): refresh uk translations
This commit is contained in:
parent
2689782a41
commit
f4a9e453f3
@ -0,0 +1,327 @@
|
||||
---
|
||||
x-i18n:
|
||||
generated_at: "2026-04-25T01:40:02Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: cccaaa1b3e472279b7548ad5af5d50162db9e99a731e06be796de64ee9f8c8d8
|
||||
source_path: superpowers/specs/2026-04-22-tweakcn-custom-theme-import-design.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
# Дизайн імпорту користувацької теми 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.ts`
|
||||
- `ui/src/ui/views/config.ts`
|
||||
- `ui/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-змінних:
|
||||
|
||||
```css
|
||||
:root[data-theme="custom"] { ... }
|
||||
:root[data-theme="custom-light"] { ... }
|
||||
```
|
||||
|
||||
Це дозволяє ізолювати змінні користувацької теми в межах сімейства `custom` і не дає вбудованим сімействам успадковувати інлайнові CSS-змінні.
|
||||
|
||||
## Архітектура
|
||||
|
||||
### Модель теми
|
||||
|
||||
Оновити `ui/src/ui/theme.ts`:
|
||||
|
||||
- Розширити `ThemeName`, додавши `custom`.
|
||||
- Розширити `ResolvedTheme`, додавши `custom` і `custom-light`.
|
||||
- Розширити `VALID_THEME_NAMES`.
|
||||
- Оновити `resolveTheme()`, щоб `custom` віддзеркалювала наявну поведінку сімейств:
|
||||
- `custom + dark` -> `custom`
|
||||
- `custom + light` -> `custom-light`
|
||||
- `custom + system` -> `custom` або `custom-light` залежно від уподобань ОС
|
||||
|
||||
Для `custom` не додаються жодні застарілі псевдоніми.
|
||||
|
||||
### Модель збереження
|
||||
|
||||
Розширити збереження `UiSettings` у `ui/src/ui/storage.ts`, додавши один необов’язковий payload користувацької теми:
|
||||
|
||||
- `customTheme?: ImportedCustomTheme`
|
||||
|
||||
Рекомендована форма збережених даних:
|
||||
|
||||
```ts
|
||||
type ImportedCustomTheme = {
|
||||
sourceUrl: string;
|
||||
themeId: string;
|
||||
label: string;
|
||||
importedAt: string;
|
||||
light: Record<string, string>;
|
||||
dark: Record<string, string>;
|
||||
};
|
||||
```
|
||||
|
||||
Примітки:
|
||||
|
||||
- `sourceUrl` зберігає початкове введення користувача після нормалізації.
|
||||
- `themeId` — це ідентифікатор теми tweakcn, витягнутий з URL.
|
||||
- `label` — це поле tweakcn `name`, якщо воно присутнє, інакше `Custom`.
|
||||
- `light` і `dark` — це вже нормалізовані мапи токенів OpenClaw, а не сирі payload tweakcn.
|
||||
- Імпортований payload зберігається поруч з іншими браузерно-локальними налаштуваннями й серіалізується в тому самому документі локального сховища.
|
||||
- Якщо під час завантаження збережені дані користувацької теми відсутні або невалідні, ігнорувати payload і повертатися до `theme: "claw"`, якщо збереженим сімейством було `custom`.
|
||||
|
||||
### Застосування під час виконання
|
||||
|
||||
Додати вузький менеджер стилів користувацької теми в runtime Control UI, поруч із `ui/src/ui/app-settings.ts` і `ui/src/ui/theme.ts`.
|
||||
|
||||
Обов’язки:
|
||||
|
||||
- Створювати або оновлювати один стабільний тег `<style id="openclaw-custom-theme">` у `document.head`.
|
||||
- Генерувати CSS лише тоді, коли присутній валідний payload користувацької теми.
|
||||
- Видаляти вміст тега стилю, коли payload очищено.
|
||||
- Залишити CSS вбудованих сімейств у `ui/src/styles/base.css`; не вставляти імпортовані токени до відстежуваного в репозиторії стилю.
|
||||
|
||||
Цей менеджер запускається щоразу, коли налаштування завантажуються, зберігаються, імпортуються або очищуються.
|
||||
|
||||
### Селектори світлого режиму
|
||||
|
||||
Реалізація має віддавати перевагу `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 `custom` уже існує
|
||||
- одна дія `Clear`, якщо payload `custom` уже існує
|
||||
- Показувати мітку імпортованої теми та хост джерела, якщо 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}`.
|
||||
|
||||
Використовувати вузький валідатор схеми для зовнішнього payload. Бажано застосувати zod, оскільки це недовірена зовнішня межа.
|
||||
|
||||
Обов’язкові поля віддалених даних:
|
||||
|
||||
- верхньорівневе `name` як необов’язковий рядок
|
||||
- `cssVars.theme` як необов’язковий об’єкт
|
||||
- `cssVars.light` як об’єкт
|
||||
- `cssVars.dark` як об’єкт
|
||||
|
||||
Якщо відсутній `cssVars.light` або `cssVars.dark`, відхилити імпорт. Це зроблено навмисно: затверджена продуктова поведінка — повна підтримка режимів, а не найкраща можлива синтезація відсутньої сторони.
|
||||
|
||||
## Мапування токенів
|
||||
|
||||
Не віддзеркалювати змінні tweakcn безрозбірно. Нормалізувати обмежену підмножину до токенів OpenClaw, а решту виводити в допоміжній функції.
|
||||
|
||||
### Токени, що імпортуються напряму
|
||||
|
||||
З кожного блоку режиму tweakcn:
|
||||
|
||||
- `background`
|
||||
- `foreground`
|
||||
- `card`
|
||||
- `card-foreground`
|
||||
- `popover`
|
||||
- `popover-foreground`
|
||||
- `primary`
|
||||
- `primary-foreground`
|
||||
- `secondary`
|
||||
- `secondary-foreground`
|
||||
- `muted`
|
||||
- `muted-foreground`
|
||||
- `accent`
|
||||
- `accent-foreground`
|
||||
- `destructive`
|
||||
- `destructive-foreground`
|
||||
- `border`
|
||||
- `input`
|
||||
- `ring`
|
||||
- `radius`
|
||||
|
||||
Із спільного `cssVars.theme`, якщо воно присутнє:
|
||||
|
||||
- `font-sans`
|
||||
- `font-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`
|
||||
|
||||
Правила виведення живуть у чистій допоміжній функції, щоб їх можна було тестувати окремо. Точні формули змішування кольорів — це деталь реалізації, але допоміжна функція має задовольняти дві умови:
|
||||
|
||||
- зберігати читабельний контраст, близький до задуму імпортованої теми
|
||||
- давати стабільний результат для одного й того самого імпортованого payload
|
||||
|
||||
### Токени, які ігноруються у v1
|
||||
|
||||
Ці токени tweakcn навмисно ігноруються в першій версії:
|
||||
|
||||
- `chart-*`
|
||||
- `sidebar-*`
|
||||
- `font-serif`
|
||||
- `shadow-*`
|
||||
- `tracking-*`
|
||||
- `letter-spacing`
|
||||
- `spacing`
|
||||
|
||||
Це дозволяє зосередити обсяг робіт на токенах, які поточний Control UI справді використовує.
|
||||
|
||||
### Шрифти
|
||||
|
||||
Рядки стеків шрифтів імпортуються, якщо вони присутні, але OpenClaw не завантажує віддалені ресурси шрифтів у v1. Якщо імпортований стек посилається на шрифти, які недоступні в браузері, застосовується звичайна поведінка fallback.
|
||||
|
||||
## Поведінка при помилках
|
||||
|
||||
Невдалі імпорти мають завершуватися безпечно.
|
||||
|
||||
- Невалідний формат URL: показати вбудовану помилку валідації, не виконувати запит.
|
||||
- Непідтримуваний хост або форма шляху: показати вбудовану помилку валідації, не виконувати запит.
|
||||
- Збій мережі, відповідь не-OK або некоректний JSON: показати вбудовану помилку, не змінювати поточний збережений payload.
|
||||
- Збій схеми або відсутність блоків light/dark: показати вбудовану помилку, не змінювати поточний збережений payload.
|
||||
- Дія очищення:
|
||||
- видаляє збережений payload користувацької теми
|
||||
- видаляє вміст керованого тега стилю користувацької теми
|
||||
- якщо `custom` активна, перемикає сімейство тем назад на `claw`
|
||||
- Невалідний збережений payload користувацької теми при першому завантаженні:
|
||||
- ігнорувати збережений payload
|
||||
- не генерувати користувацький CSS
|
||||
- якщо збереженим сімейством тем було `custom`, повертатися до `claw`
|
||||
|
||||
За жодних обставин невдалий імпорт не повинен залишати активний документ із частково застосованими змінними користувацького CSS.
|
||||
|
||||
## Файли, які, ймовірно, зміняться під час реалізації
|
||||
|
||||
Основні файли:
|
||||
|
||||
- `ui/src/ui/theme.ts`
|
||||
- `ui/src/ui/storage.ts`
|
||||
- `ui/src/ui/app-settings.ts`
|
||||
- `ui/src/ui/views/config.ts`
|
||||
- `ui/src/ui/views/config-quick.ts`
|
||||
- `ui/src/styles/base.css`
|
||||
|
||||
Імовірні нові допоміжні файли:
|
||||
|
||||
- `ui/src/ui/custom-theme.ts`
|
||||
- `ui/src/ui/custom-theme-import.ts`
|
||||
|
||||
Тести:
|
||||
|
||||
- `ui/src/ui/app-settings.test.ts`
|
||||
- `ui/src/ui/storage.node.test.ts`
|
||||
- `ui/src/ui/views/config.browser.test.ts`
|
||||
- нові сфокусовані тести для розбору URL і нормалізації payload
|
||||
|
||||
## Тестування
|
||||
|
||||
Мінімальне покриття реалізації:
|
||||
|
||||
- розібрати URL посилання поширення в ідентифікатор теми tweakcn
|
||||
- нормалізувати `/themes/{id}` і `/r/themes/{id}` до URL отримання
|
||||
- відхиляти непідтримувані хости та некоректні ідентифікатори
|
||||
- валідувати форму payload tweakcn
|
||||
- мапити валідний payload tweakcn у нормалізовані мапи токенів OpenClaw для світлого й темного режимів
|
||||
- завантажувати та зберігати payload користувацької теми в браузерно-локальних налаштуваннях
|
||||
- розв’язувати `custom` для `light`, `dark` і `system`
|
||||
- вимикати вибір `Custom`, коли payload відсутній
|
||||
- застосовувати імпортовану тему негайно, коли `custom` уже активна
|
||||
- повертатися до `claw`, коли активну користувацьку тему очищено
|
||||
|
||||
Ціль ручної перевірки:
|
||||
|
||||
- імпортувати відому тему tweakcn із Settings
|
||||
- перемикатися між `light`, `dark` і `system`
|
||||
- перемикатися між `custom` і вбудованими сімействами
|
||||
- перезавантажити сторінку та підтвердити, що імпортована користувацька тема зберігається локально
|
||||
|
||||
## Нотатки щодо розгортання
|
||||
|
||||
Ця функція навмисно невелика. Якщо користувачі згодом попросять кілька імпортованих тем, перейменування, експорт або синхронізацію між пристроями, розглядати це як наступний дизайн. Не закладати наперед абстракцію бібліотеки тем у цій реалізації.
|
||||
Loading…
Reference in New Issue
Block a user