Сохранение темы

По умолчанию Web Host определяет светлую/тёмную тему из theme_mode (умолчание фасада) и держит её в памяти — поэтому явный выбор пользователя теряется при следующей перезагрузке. Сохранение темы позволяет этому выбору пережить перезагрузки, храня его в cookie или в localStorage, и загружает его как можно раньше, чтобы не было мигания неправильной темы.

Сохранение целиком живёт в фасаде. Web Host остаётся независимым от хранилища: он лишь порождает событие themeChanged, которое фасад (или любая встраивающая сторона) использует для сохранения выбора.

По согласию. theme_persist по умолчанию равен none — сохранение выключено, если развёртывание явно не установит cookie или localStorage. При умолчании поведение ровно такое же, как раньше (тема всегда берётся из theme_mode и не запоминается между перезагрузками). Ничего не сохраняется, cookie не записывается, а генерируемый скрипт ничего не делает, пока вы не включите эту возможность.

Настройка

Ею управляют два параметра фасада (см. Фронтенд-фасад):

Параметр По умолчанию Значения Описание
theme_persist none none | cookie | localStorage Где хранится выбранный режим. none = текущее поведение.
theme_storage_key @wippy-theme-mode string Ключ cookie / localStorage.

Оба возвращаются публичной конечной точкой конфигурации как themePersist и themeStorageKey, поэтому страницы, раздаваемые вне Web Host, тоже могут их прочитать.

# в параметрах вашей зависимости фасада
- name: theme_persist
  value: cookie
- name: theme_storage_key
  value: "@wippy-theme-mode"
  • cookie — отрисовываемая через Jet оболочка хоста читает cookie на стороне сервера и записывает класс w-theme-* в <html> до отправки ответа, поэтому уже самая первая отрисовка идёт с темой. Без мигания. Лучший вариант по умолчанию.
  • localStorage — сервер не может прочитать localStorage, поэтому сохранённое значение применяется синхронным встроенным скриптом как можно раньше. Кратковременное мигание технически возможно, но минимизировано.

Генерируемый скрипт

Когда сохранение включено, фасад генерирует и раздаёт небольшой скрипт по адресу:

GET /api/public/facade/theme-persist.js

Настроенные ключ и режим в него запечены — на странице настраивать нечего. Подключите его один раз, как можно раньше в <head>:

<script src="/api/public/facade/theme-persist.js"></script>

При загрузке он читает сохранённое значение и применяет класс w-theme-*, затем предоставляет небольшой API:

window.wippyThemePersist = {
  mode,            // 'none' | 'cookie' | 'localStorage'
  key,             // ключ хранилища
  read(),          // -> 'auto' | 'light' | 'dark' | null
  write(mode),     // сохранить режим (ничего не делает при mode === 'none')
  apply(mode),     // переключить класс w-theme-* на <html>
}

Оболочка хоста (index.html / Jet-шаблон index.jet) уже подключает этот скрипт, передаёт сохранённое значение в приложение и сохраняет изменения — трогать её не нужно. Разделы ниже касаются других страниц.

Как это складывается вместе (оболочка хоста)

  1. Первая отрисовка — режим cookie: сервер выставил <html class="w-theme-dark">. Режим localStorage: класс выставил скрипт раннего применения. В любом случае страница оформлена до загрузки бандла.
  2. Начальная загрузка — оболочка передаёт сохранённое значение в хост: themeMode: window.wippyThemePersist.read() ?? cfg.themeMode, поэтому хост применяет тот же режим.
  3. При изменении — хост порождает themeChanged(mode); оболочка сохраняет его: events.on('themeChanged', window.wippyThemePersist.write).

Событие хоста themeChanged

globalEvents — генератор событий, возвращаемый window.initWippyApp(...), — порождает themeChanged(mode) ('auto' | 'light' | 'dark') при инициализации и при каждом изменении темы. Оно не зависит от способа сохранения: хост никогда не обращается к хранилищу; что с этим делать, решают встраивающие стороны.

const events = window.initWippyApp(config, '#app')
events.on('themeChanged', (mode) => {
  // например, сохранить или уведомить родительское окно
})

Страницы вне размещения Wippy

Документ вне контракта переносимых модулей Wippy может уважать и сохранять ту же тему. Нативные кнопки ниже уместны только для такого внешнего статического документа. Страница или компонент Wippy с такими элементами управления должны использовать PrimeVue согласно Контракту переносимого UI. Подключите генерируемый скрипт и вызывайте write() из собственного переключателя:

<head>
  <!-- как можно раньше: применяет сохранённую тему + предоставляет window.wippyThemePersist -->
  <script src="/api/public/facade/theme-persist.js"></script>
  <!-- необязательно: переиспользовать и брендовую тему фасада -->
  <link rel="stylesheet" href="/api/public/facade/variables.css">
</head>
<body>
  <button type="button" data-mode="auto">Auto</button>
  <button type="button" data-mode="light">Light</button>
  <button type="button" data-mode="dark">Dark</button>

  <script>
    document.querySelectorAll('[data-mode]').forEach((btn) => {
      btn.addEventListener('click', () => {
        const mode = btn.dataset.mode
        window.wippyThemePersist.apply(mode)   // обновить <html> сейчас
        window.wippyThemePersist.write(mode)   // сохранить для следующей загрузки / для хоста
      })
    })
  </script>
</body>

Поскольку ключ и режим хранения общие (скрипт генерируется из той же конфигурации фасада), выбор, сделанный на странице входа, напрямую переносится в Web Host, и наоборот.

Если вы предпочитаете не загружать скрипт, можно запросить /api/public/facade/config, прочитать themePersist / themeStorageKey и реализовать чтение/запись самостоятельно — но генерируемый скрипт держит логику хранения в одном месте.

Для собственной страницы с серверным рендерингом (например, Jet-шаблона входа) можно применить тему на сервере ровно так же, как это делает оболочка хоста: прочитать из запроса cookie с именем из theme_storage_key и выдать соответствующий класс на <html>:

<html lang="en"{{ if hasTheme }} class="{{ themeClass }}" style="color-scheme: {{ colorScheme }};"{{ end }}>

где обработчик установил themeClass в w-theme-dark / w-theme-light (а colorScheme в dark / light) на основе cookie. Всё равно подключайте theme-persist.js, чтобы страница могла записывать изменения обратно.