Зачем это плагину
У API несколько сценариев использования:
- получить текущую тему shell;
- получить список тем;
- создать (временную) тему прямо из плагина, а не просить пользователя руками забивать цвета;
- переключить тему из команды, сценария или по алгоритму;
- синхронизировать внешний UI или webview с палитрой MinaChan.
Встроенные темы сейчас такие:
lightdarkbrand
Что есть в API
В shell зарегистрированы шесть runtime-команд:
ui:theme-currentui:theme-listui:theme-getui:theme-addui:theme-deleteui:theme-activate
Пример ответа ui:theme-get:
{
"theme": {
"id": "brand",
"name": "Brand",
"brightness": "dark",
"builtIn": true,
"colors": {
"background": "#0B0F19",
"surface": "#1A1330",
"primary": "#B794F4",
"secondary": "#FF9CF1",
"text": "#EDE7FF",
"hint": "#9CA3AF",
"appBarBackground": "#0B0F19",
"outline": "#43375F"
}
}
}
Пример запроса ui:theme-get:
{
"id": "Brand"
}
Пример на Python SDK
Отдельной high-level обёртки под Theme API в sdk_python пока нет и я не уверен, что она нужна.
Ниже минимальный рабочий паттерн для запроса текущей темы:
def _request_runtime(self, tag: str, payload, callback) -> None:
responded = {'value': False}
def _finish(data) -> None:
if responded['value']:
return
responded['value'] = True
callback(data)
def _on_response(sender: str, data, in_tag: str) -> None:
_finish(data if isinstance(data, dict) else None)
def _on_complete(sender: str, data, in_tag: str) -> None:
_finish(None)
seq = self.send_message_with_response(
tag,
payload,
on_response=_on_response,
on_complete=_on_complete,
)
if seq < 0:
_finish(None)
def load_shell_theme(self) -> None:
def _on_theme(payload) -> None:
if not isinstance(payload, dict):
return
theme = payload.get('theme')
if not isinstance(theme, dict):
return
colors = theme.get('colors') or {}
self.set_property('lastThemeId', theme.get('id'))
self.set_property('lastPrimaryColor', colors.get('primary'))
self.save_properties()
self._request_runtime('ui:theme-current', {}, _on_theme)
Что здесь важно:
- запрос идёт в shell через message bus, как и любой другой runtime-командой;
- ответ прилетает асинхронно;
- плагину лучше сразу проверять типы payload, а не верить, что там всегда будет корректный
dict; - если тема нужна только для отображения, её обычно достаточно запросить один раз при открытии окна или панели.
Тот же подход работает и в sdk_golang, и в sdk_groovy: там тоже используется обычный callback-ответ через SendMessageWithResponse / sendMessageWithResponse.
Создание своей темы
Самый приятный сценарий у нового API не чтение, а запись. Плагин теперь может принести в MinaChan свою тему, а потом сразу активировать её.
Payload для ui:theme-add:
{
"name": "Night Shift",
"id": "night-shift",
"brightness": "dark",
"colors": {
"background": "#0B1020",
"surface": "#151B2F",
"primary": "#7DD3FC",
"secondary": "#C084FC",
"text": "#E6EEF8",
"hint": "#94A3B8",
"appBarBackground": "#0A0F1D",
"outline": "#334155"
}
}
После успешного ответа можно сразу переключиться:
{
"themeId": "night-shift"
}
Это уже payload для ui:theme-activate.
Надо помнить об ограничениях:
- id должен быть уникальным;
- минимум одна тема должна оставаться в системе.
Типовые ошибки API уже предусмотрены: theme_not_found, theme_id_exists, theme_builtin, theme_last_remaining и несколько ошибок валидации id и name.
Что прилетает в UI-окна
Если плагин работает через gui:set-panel и ui:window-create, в state прилетают поля примерно такого вида:
{
"shellTheme": "midnight-rose",
"shellThemeData": {
"id": "midnight-rose",
"name": "Midnight Rose",
"brightness": "dark",
"builtIn": false,
"colors": {
"background": "#0B0F19",
"surface": "#1A1330",
"primary": "#B794F4",
"secondary": "#FF9CF1",
"text": "#EDE7FF",
"hint": "#9CA3AF",
"appBarBackground": "#0B0F19",
"outline": "#43375F"
}
}
}
Тонкость здесь такая:
- для встроенных тем часто достаточно одного
shellTheme; - для кастомных тем shell дополнительно передаёт
shellThemeData, чтобы окно не потеряло палитру, если это неbrand/light/dark.
Послесловие
Моя главная задумка заключалась в том, чтобы дать возможность гибко настраивать цветовые палитры для тем, обмениваться ими, возможно применять какие-то ИИ-шные штуки.