universal-dashboard/README.md

43 KiB
Raw Blame History

Панель экосистемы

Статическая навигационная панель для быстрого доступа к сервисам инфраструктуры. Поддерживает гибкую иерархию данных, поиск, дерево навигации, несколько режимов отображения и настройку внешнего вида.

Стек: HTML, CSS, vanilla JavaScript. Без сборщиков и фреймворков.

Оглавление

Возможности

  • Древовидная навигация слева в стиле файлового менеджера
  • Каталог карточек справа с collapsible-разделами
  • Поиск по названию, тегам, типу сервиса и родительским разделам
  • 4 режима отображения: плитки, список, компактные плитки, компактный список
  • Темы: светлая / тёмная, 4 пресета акцента + свой градиент
  • Узоры панелей — 13 встроенных пресетов + свой URL/SVG; отдельно для nav / section / group / item; слайдеры прозрачности, высоты и перехода
  • Разделение слоёв — подложка и blur, чтобы вложенные узоры не «наезжали» друг на друга
  • Экспорт / импорт / сброс настроек ⚙ в JSON-файл
  • Настройки через popup (иконка ⚙)
  • Изменяемая ширина бокового меню и высота верхней шапки (drag, можно скрыть ручки)
  • Габариты — лимиты ширины контента, меню и шапки (⚙, для администратора)
  • Колонки каталога — число элементов в строке для каждого из 4 режимов отображения
  • Сохранение состояния в localStorage (настройки UI, раскрытие веток)
  • Конфигурация через JSON — локальный объект или загрузка по URL
  • Гибкая иерархия: полная вложенность, раздел без подраздела, плоский список

Быстрый старт

Панель использует fetch, поэтому её нужно открывать через HTTP-сервер, а не напрямую как file://.

cd ecosystem
npx serve .

Открой в браузере адрес, который покажет сервер (обычно http://localhost:3000).

Структура проекта

ecosystem/
├── index.html                              # Разметка страницы
├── styles.css                              # Стили, темы, адаптив
├── script.js                               # Логика, рендер, примеры структур
├── settings.json                           # Данные + пресет UI организации
├── settings-top-bar.example.json           # Пример пресета «шапка сверху»
├── panel-data.json                         # Данные навигации (рабочий файл)
├── examples/                               # Готовые демо-страницы (см. examples/README.md)
│   ├── index.html                          # Оглавление примеров
│   ├── shared/                             # styles, script, assets для демо
│   ├── full/                               # section → group → item
│   ├── section-items/                      # section → item
│   ├── flat/                               # плоский items[]
│   └── mixed/                              # items + groups
├── panel-data-full.example.json            # JSON-сниппет (без страницы)
├── assets/                                 # SVG-иконки сервисов
└── README.md

Конфигурация

settings.json

Указывает, откуда загружать данные панели.

Вариант 1 — загрузка из файла или URL:

{
  "dataUrl": "./panel-data.json"
}

Можно указать внешний адрес или любой из example-файлов:

{
  "dataUrl": "./panel-data-flat.example.json"
}

Вариант 2 — inline-объект в том же файле:

{
  "dataUrl": "",
  "data": {
    "title": "Заголовок панели",
    "sections": []
  }
}

Если dataUrl не пустой, он имеет приоритет над полем data.

При ошибке загрузки или невалидном JSON на странице показывается экран с описанием проблемы.

Пресет организации (defaults + features)

В settings.json можно задать дефолтный вид панели для организации и ограничить, что пользователь может менять в ⚙.

{
  "dataUrl": "./panel-data.json",
  "defaults": {
    "navLayout": "sidebar",
    "showTree": true,
    "showResizeHandles": true,
    "theme": "dark",
    "view": "cards",
    "accent": "violet",
    "customAccentFrom": "#8b5cf6",
    "customAccentTo": "#22d3ee",
    "customAccentAngle": 135,
    "panelPatternNav": "hex",
    "panelPatternNavOpacity": 58,
    "panelPatternNavHeight": 38,
    "panelPatternNavFade": 18,
    "panelPatternNavGradientOpacity": 62,
    "panelPatternNavGradientHeight": 34,
    "panelPatternNavGradientFade": 16,
    "panelPatternSection": "hex",
    "panelPatternSectionOpacity": 58,
    "panelPatternSectionHeight": 38,
    "panelPatternSectionFade": 18,
    "panelPatternSectionGradientOpacity": 62,
    "panelPatternSectionGradientHeight": 34,
    "panelPatternSectionGradientFade": 16,
    "panelPatternGroup": "none",
    "panelPatternGroupOpacity": 58,
    "panelPatternGroupHeight": 38,
    "panelPatternGroupFade": 18,
    "panelPatternGroupGradientOpacity": 62,
    "panelPatternGroupGradientHeight": 34,
    "panelPatternGroupGradientFade": 16,
    "panelPatternItem": "none",
    "panelPatternItemOpacity": 58,
    "panelPatternItemHeight": 38,
    "panelPatternItemFade": 18,
    "panelPatternItemGradientOpacity": 62,
    "panelPatternItemGradientHeight": 34,
    "panelPatternItemGradientFade": 16,
    "patternIsolation": 46,
    "showNumbers": false,
    "showTreeTags": true,
    "showTreeType": false,
    "treeMetaLayout": "inline",
    "treeColumns": 1,
    "autoCollapseSearch": true,
    "sidebarWidth": 360,
    "topBarHeight": 140,
    "shellMaxWidth": 2240,
    "sidebarMinWidth": 240,
    "sidebarMaxWidth": 720,
    "sidebarMaxWidthWide": 1040,
    "treeSidebarBaseWidth": 360,
    "treeSidebarColumnWidth": 96,
    "topBarMinHeight": 80,
    "topBarMaxHeight": 560,
    "catalogCardsColumns": 3,
    "catalogListColumns": 1,
    "catalogCompactCardsColumns": 4,
    "catalogCompactListColumns": 2,
    "collapseAnimation": "animated",
    "collapseDuration": 260,
    "searchPlaceholder": "Grafana, S3, журнал, сервер...",
    "settingsPanelWidth": 440
  },
  "limits": {
    "shellMaxWidth": { "min": 1200, "max": 3840 },
    "sidebarMinWidth": { "min": 220, "max": 480 },
    "sidebarMaxWidth": { "min": 400, "max": 1200 },
    "sidebarMaxWidthWide": { "min": 560, "max": 2000 },
    "treeSidebarBaseWidth": { "min": 280, "max": 720 },
    "treeSidebarColumnWidth": { "min": 60, "max": 200 },
    "topBarMinHeight": { "min": 64, "max": 240 },
    "topBarMaxHeight": { "min": 160, "max": 800 },
    "catalogCardsColumns": { "min": 1, "max": 6 },
    "catalogListColumns": { "min": 1, "max": 4 },
    "catalogCompactCardsColumns": { "min": 2, "max": 8 },
    "catalogCompactListColumns": { "min": 1, "max": 6 }
  },
  "features": {
    "allowNavLayoutChange": true,
    "allowShowTreeChange": true,
    "allowSearch": true,
    "allowViewChange": true,
    "allowCatalogColumnsChange": true,
    "allowShowNumbers": true,
    "allowTreeOptions": true,
    "allowSidebarResize": true,
    "allowLayoutLimitsChange": true,
    "allowThemeChange": true,
    "allowAccentChange": true,
    "allowPanelPatternChange": true
  }
}
defaults Варианты Описание
navLayout "sidebar", "top" "sidebar" — панель слева; "top" — шапка с названием и поиском
showTree true, false Показывать дерево навигации (в любом режиме макета)
showResizeHandles true, false Показывать ручки drag-resize меню / шапки
theme "dark", "light" Стартовая цветовая тема
view "cards", "list", "compact-cards", "compact-list" Режим отображения каталога
accent "violet", "cyan", "lime", "amber", "custom" Пресет акцента; при "custom" используются поля градиента ниже
customAccentFrom #rrggbb Цвет «от» для accent: "custom" (по умолчанию #8b5cf6)
customAccentTo #rrggbb Цвет «до» для accent: "custom" (по умолчанию #22d3ee)
customAccentAngle 0–360 Угол градиента в градусах (по умолчанию 135)
panelPatternNav … panelPatternItem см. узоры панелей Тип узора для каждого типа блоков (none, hex, …)
panelPattern{Nav|Section|Group|Item}Opacity 0–100 Прозрачность узора в % (по умолчанию 58)
panelPattern{Nav|Section|Group|Item}Height 0–100 Высота узора в % (по умолчанию 38; 0 — скрыть)
panelPattern{Nav|Section|Group|Item}Fade 0–100 Ширина зоны затухания узора в % высоты блока: 0 — резкий обрыв на линии высоты, 100 — плавное затухание от верха (по умолчанию 18)
panelPattern{Nav|Section|Group|Item}GradientOpacity 0–100 Прозрачность акцентного градиента в % (по умолчанию 62)
panelPattern{Nav|Section|Group|Item}GradientHeight 0–100 Высота градиента в % (по умолчанию 34)
panelPattern{Nav|Section|Group|Item}GradientFade 0–100 Зона затухания градиента (по умолчанию 16)
panelPattern{Nav|Section|Group|Item}CustomImage строка URL Изображение для custom (пусто по умолчанию)
panelPattern{Nav|Section|Group|Item}CustomSize "WxH" в px Размер плитки custom (по умолчанию 32px 32px)
patternIsolation 0–100 Разделение слоёв узоров (подложка + blur; по умолчанию 46)
showNumbers true, false Показывать номера разделов, подразделов и элементов
showTreeTags true, false Теги элементов в дереве (оформление как на карточках)
showTreeType true, false Подпись типа сервиса в дереве
treeMetaLayout "inline", "stacked" Расположение тегов и типа: сбоку от названия или снизу
treeColumns 1–6 (по умолчанию 1) Число колонок элементов внутри папок дерева
autoCollapseSearch true, false При поиске сворачивать ветки дерева без совпадений
sidebarWidth sidebarMinWidth–sidebarMaxWidth (или sidebarMaxWidthWide при treeColumns > 1) Текущая ширина бокового меню (drag-resize); при navLayout: "top" не применяется
topBarHeight topBarMinHeight–topBarMaxHeight (до ~75% высоты окна) Текущая высота верхней шапки (drag-resize); только при navLayout: "top"
shellMaxWidth см. limits.shellMaxWidth Максимальная ширина контейнера .shell (вся страница)
sidebarMinWidth см. limits.sidebarMinWidth Минимальная ширина бокового меню при drag-resize
sidebarMaxWidth см. limits.sidebarMaxWidth Максимальная ширина меню при treeColumns: 1
sidebarMaxWidthWide см. limits.sidebarMaxWidthWide Потолок ширины меню при нескольких колонках дерева
treeSidebarBaseWidth см. limits.treeSidebarBaseWidth База формулы ширины меню: base + columns × step
treeSidebarColumnWidth см. limits.treeSidebarColumnWidth Прирост ширины меню за каждую колонку дерева
topBarMinHeight см. limits.topBarMinHeight Минимальная высота шапки в режиме «Сверху»
topBarMaxHeight см. limits.topBarMaxHeight Максимальная высота шапки (также ограничена ~75% окна)
catalogCardsColumns см. limits.catalogCardsColumns Колонок в строке в режиме «Плитки»
catalogListColumns см. limits.catalogListColumns Колонок в строке в режиме «Список»
catalogCompactCardsColumns см. limits.catalogCompactCardsColumns Колонок в строке в режиме «Компакт плитки»
catalogCompactListColumns см. limits.catalogCompactListColumns Колонок в строке в режиме «Компакт список»
collapseAnimation "instant", "animated" Режим раскрытия списков, дерева и collapsible-блоков
collapseDuration 100–800 Длительность плавного раскрытия в миллисекундах (по умолчанию 260)
searchPlaceholder произвольная строка Текст-подсказка в поле поиска
settingsPanelWidth limits.settingsPanelWidth (по умолчанию 320–720) Ширина popup настроек в px (по умолчанию 440)
limits Формат Описание
shellMaxWidth { "min", "max" } Диапазон слайдера «Ширина контента» в ⚙ → Габариты
sidebarMinWidth { "min", "max" } Диапазон «Меню: мин. ширина»
sidebarMaxWidth { "min", "max" } Диапазон «Меню: макс. (1 кол.)»
sidebarMaxWidthWide { "min", "max" } Диапазон «Меню: макс. (неск. кол.)»
treeSidebarBaseWidth { "min", "max" } Диапазон «Дерево: база ширины»
treeSidebarColumnWidth { "min", "max" } Диапазон «Дерево: ширина колонки»
topBarMinHeight { "min", "max" } Диапазон «Шапка: мин. высота»
topBarMaxHeight { "min", "max" } Диапазон «Шапка: макс. высота»
catalogCardsColumns { "min", "max" } Диапазон колонок каталога «Плитки»
catalogListColumns { "min", "max" } Диапазон колонок каталога «Список»
catalogCompactCardsColumns { "min", "max" } Диапазон колонок «Компакт плитки»
catalogCompactListColumns { "min", "max" } Диапазон колонок «Компакт список»

При treeColumns > 1 фактический потолок ширины меню: min(sidebarMaxWidthWide, treeSidebarBaseWidth + treeColumns × treeSidebarColumnWidth).

shellMaxWidth автоматически не опускается ниже sidebarMaxWidthWide + 320, чтобы оставалось место для каталога.

features Варианты Описание
allowNavLayoutChange true, false Переключатель «Сбоку / Сверху»
allowShowTreeChange true, false Toggle «Показывать дерево» в ⚙ → Макет
allowSearch true, false Поле поиска
allowViewChange true, false Режимы каталога (плитки / список / компакт)
allowCatalogColumnsChange true, false Слайдеры «колонок в строке» для каждого вида каталога
allowShowNumbers true, false Нумерация
allowTreeOptions true, false Колонки дерева (1–6), теги, тип, расположение мета, авто-раскрытие
allowSidebarResize true, false Drag-resize и toggle «Ручки изменения размера»
allowLayoutLimitsChange true, false Раздел ⚙ «Габариты»: лимиты ширины контента и навигации
allowThemeChange true, false Светлая / тёмная тема
allowAccentChange true, false Пресеты и свой градиент
allowPanelPatternChange true, false Выбор узора панелей в ⚙ → Оформление

Пользовательские изменения сохраняются в localStorage, но заблокированные features всегда возвращаются к defaults.

Пример пресета «шапка сверху»: settings-top-bar.example.json — navLayout: "top", дерево по умолчанию скрыто (showTree: false), но его можно включить.

Форматы иерархии

Панель поддерживает три базовых формата. Старый section → group → item работает без изменений.

Формат Структура Дерево Каталог
Полная sections[].groups[].items[] раздел → подраздел → файлы collapsible разделы и подразделы
Раздел → item sections[].items[] раздел → файлы карточки внутри раздела
Плоский список корневой items[] плоский список файлов только сетка карточек
Смешанный sections[].items[] + groups[] прямые файлы, затем подразделы прямые карточки, затем подразделы

Минимум данных: корень должен содержать sections или items. Каждый раздел — groups или items. Каждый элемент — title и type.

Схема panel-data

Абстрактная структура файла данных (без привязки к конкретным сервисам):

{
  "title": "<заголовок панели>",
  "sections": [
    {
      "id": "<slug-раздела>",
      "number": "<опционально>",
      "title": "<название раздела>",
      "description": "<опционально>",
      "items": [
        {
          "id": "<стабильный-ключ>",
          "number": "<опционально>",
          "title": "<название>",
          "type": "<ключ из serviceMeta>",
          "tags": ["<тег>"],
          "url": "<ссылка>"
        }
      ],
      "groups": [
        {
          "id": "<slug-подраздела>",
          "number": "<опционально>",
          "title": "<название подраздела>",
          "description": "<опционально>",
          "items": ["<элементы — та же форма>"]
        }
      ]
    }
  ],
  "items": ["<альтернатива sections: плоский список>"],
  "serviceMeta": {
    "<type-key>": {
      "label": "<подпись типа>",
      "icon": "assets/<иконка>.svg"
    }
  }
}

Используй либо sections, либо корневой items. Внутри раздела — либо groups, либо items, либо оба.

Поля данных

Уровень Поля Описание
Корень title Заголовок панели (sidebar + <title>)
sections Массив разделов (если нет корневого items)
items Плоский список без разделов
serviceMeta Дополнительные или переопределённые типы сервисов
section id, title Обязательные идентификатор и название
number, description Опционально
groups, items Хотя бы одно из двух
group id, title, items Подраздел с элементами
number, description Опционально
item title, type Обязательные название и тип
url Ссылка; "#" — заглушка
tags Массив строк для поиска
id Стабильный ключ (DOM, фокус из дерева)
number Отображаемый номер
icon Опционально: свой путь к SVG (иначе — иконка из type)
  • type — ключ из встроенного defaultServiceMeta или из serviceMeta в JSON; определяет иконку и подпись на карточке
  • id элемента нужен, если нет number — иначе ключ генерируется автоматически

Примеры данных

Готовые демо-страницы — в папке examples/. Корневые panel-data-*.example.json — те же структуры в виде JSON-сниппетов.

Готовые демо-страницы (examples/)

Каждый пример — отдельная страница с index.html, settings.json и panel-data.json. Общие ресурсы в examples/shared/.

cd examples
npx serve .
Путь Формат
/ оглавление примеров
/full/ section → group → item
/section-items/ section → item
/flat/ плоский items[]
/mixed/ items + groups в разделе

Подробнее: examples/README.md.

Типы сервисов (serviceMeta)

Базовые типы заданы в script.js (defaultServiceMeta):

analytics, api, calendar, ceph, chat, cicd, cloudbeaver, config, drive, forum, gitea, grafana, ipmi, kubernetes, ldap, messages, opensearch, postgresql, prometheus, rabbitmq, redmine, rules, social, vault, video, vpn, wiki, xpath, xmpp

Каждый тип имеет собственную SVG в assets/ (например, postgresql.svg, cloudbeaver.svg). Иконка показывается на карточке каталога и в дереве навигации.

Переопределение или добавление типов в JSON:

{
  "serviceMeta": {
    "my-type": {
      "label": "Подпись",
      "icon": "assets/config.svg"
    }
  }
}

Рекомендуемые типы сервисов

Популярные type, которые можно добавить в serviceMeta (иконки — переиспользовать из assets/):

Категория Типы (type) Комментарий
Прокси / балансировка nginx, traefik reverse proxy, ingress
Контейнеры docker, portainer, argocd runtime, UI, GitOps
CI/CD jenkins, gitlab, harbor сборка, репозиторий, registry
Базы данных mysql, mongodb, redis рядом с уже имеющимися postgresql, opensearch
Очереди / стриминг kafka аналог rabbitmq
Мониторинг / логи loki, zabbix, sentry логи, метрики, ошибки
Безопасность / IAM vault, keycloak, consul секреты, SSO, service mesh
Поиск elasticsearch, sonarqube полнотекст, качество кода
Хранилище minio S3-совместимое
IaC terraform инфраструктура как код
Трекеры jira задачи, рядом с redmine

Чтобы подключить тип:

  1. Добавь запись в serviceMeta вашего JSON или в defaultServiceMeta в script.js
  2. При необходимости положи отдельную SVG в assets/
  3. Укажи "type": "nginx" (и т.д.) в элементах

Настройки интерфейса (⚙)

Popup сгруппирован по разделам. Доступные блоки зависят от features в settings.json. Изменения пользователя сохраняются в localStorage (ecosystem-panel-preferences).

Раздел Настройки
Макет Сбоку / Сверху; toggle «Показывать дерево»; toggle «Ручки изменения размера»; слайдер «Ширина настроек»
Габариты Лимиты ширины контента, меню и шапки (сворачиваемый блок; при allowLayoutLimitsChange)
Каталог Вид карточек, колонок в строке для каждого вида, нумерация
Дерево Колонок 1–6, теги, тип, расположение мета, авто-раскрытие (при showTree: true)
Оформление Тема, акцент, узоры по типам блоков, разделение слоёв, свой градиент
(footer) Сброс, экспорт и импорт всех preferences в JSON

Режим «Сверху»

Компактная шапка: заголовок, поиск и ⚙. Дерево по умолчанию скрыто, но его можно включить (showTree: true) — оно появится под шапкой на всю ширину.

Скрытие дерева

Дерево управляется отдельно от макета (showTree). Переключение — в ⚙ → Макет.

Свой градиент

Кнопка «Свой» открывает настройки:

  • угол (0–360°)
  • цвет «от» и «до»

Значения сохраняются в localStorage.

Узоры панелей

Настраиваются в ⚙ → Оформление (если allowPanelPatternChange: true).

Четыре цели (targets)

У каждого типа блока свой независимый набор: пресет, слайдеры и (опционально) свой URL.

Цель Preference (префикс) DOM-элемент Градиент под узором
Панель навигации panelPatternNav* .hero-content да
Раздел panelPatternSection* .catalog-section да
Подраздел panelPatternGroup* .catalog-group да
Элемент panelPatternItem* .nav-card нет (только узор)

Встроенные пресеты (panelPattern*)

id Подпись в ⚙ Описание
none Нет Без повторяющейся текстуры
hex Соты Крупные гексагоны
hex-sm Соты м. Мелкие гексагоны
dots Точки Средняя точечная сетка
dots-sm Точки м. Мелкие кружки
dots-lg Точки кр. Крупные кружки
grid Сетка Линейная сетка
diagonal Диаг. Диагональные полосы
cross Клетка Перекрёстная штриховка
mesh Mesh Точки цвета акцента
tri-sm △ мел. Мелкие треугольники
tri-lg △ кр. Крупные треугольники
diamond Ромбы Ромбовидная сетка
custom Свой Своя текстура (см. ниже)

По умолчанию: навигация и разделы — hex; подразделы и элементы — none.

Слайдеры (для каждой цели)

Сuffix preference UI Диапазон По умолчанию
Opacity Узор: прозрачность 0–100 % 58
Height Узор: высота 0–100 % высоты блока 38
Fade Узор: переход 0–100 % 18
GradientOpacity Градиент: прозрачность 0–100 % 62
GradientHeight Градиент: высота 0–100 % 34
GradientFade Градиент: переход 0–100 % 16

Переход (fade): 0% — резкий обрыв на линии «высоты»; большее значение — плавнее затухание к низу блока.

Для цели Элемент в UI только три слайдера узора (без градиента).

Как узор попадает на экран

  1. preferences → applyPanelPatternPreferences() в script.js
  2. На <html>: data-panel-pattern-nav="hex" (и section / group / item)
  3. CSS-переменные: --panel-pattern-{target}-opacity, -height, -fade-start, --panel-gradient-{target}-*
  4. Встроенные пресеты: styles.css → --pattern-tile-* и селекторы html[data-panel-pattern-*="…"]
  5. Слои на блоке: ::before (градиент), ::after (текстура с mask по высоте/переходу)

Кнопки пресетов в ⚙ не захардкожены в HTML: контейнеры data-pattern-swatches="nav|section|group|item", список — panelPatternCatalog в script.js.

Разделение слоёв узоров

Слайдер «Разделение слоёв» в ⚙ → Оформление (patternIsolation, 0–100 %, по умолчанию 46).

Когда узоры включены на нескольких уровнях вложенности (раздел → подраздел → карточка), текстуры могут визуально накладываться. Настройка добавляет на .catalog-section, .catalog-group и .nav-card:

  • полупрозрачную подложку (--panel-layer-fill);
  • backdrop-filter: blur(...) (--panel-layer-blur).

Так каждый уровень «отрезает» узоры снизу и остаётся читаемой граница. Значение задаётся в settings.json → defaults.patternIsolation и сохраняется в localStorage.

Свой узор (custom)

  1. В панели нужной цели выберите пресет «Свой»
  2. Появится блок с полями:
    • Изображение — panelPattern{Nav|Section|Group|Item}CustomImage
    • Размер плитки — panelPattern*CustomSize (например 32px 32px)

Допустимые форматы URL:

  • data:image/svg+xml,… или data:image/png;base64,…
  • https://… / http://…
  • относительный путь: ./assets/my-pattern.svg, assets/pattern.png
  • javascript: — отклоняется при нормализации

Пример минимального SVG (data URI):

data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Ccircle cx='8' cy='8' r='2' fill='white' fill-opacity='.2'/%3E%3C/svg%3E

При custom без URL узор не рисуется (opacity = 0), пока не задано изображение.

Экспорт и импорт настроек

Внизу popup ⚙ — три кнопки:

Кнопка Действие
Сбросить Подтверждение → panelDefaults из settings.json, очистка ecosystem-panel-preferences
Экспортировать Скачивание ecosystem-panel-preferences.json
Импортировать Выбор JSON; применение с normalizePreferences + enforceFeatureLocks

Формат экспорта:

{
  "version": 1,
  "exportedAt": "2026-06-29T12:00:00.000Z",
  "preferences": { "theme": "dark", "panelPatternNav": "hex", "patternIsolation": 46 }
}

Импорт принимает и обёртку с preferences, и «голый» объект настроек.

Раскрытие списков

В ⚙ → Каталог — режим Резко / Плавно и длительность (100–800 ms). Плавное раскрытие применяется к разделам и подразделам каталога, дереву навигации и схлопывающимся блокам в настройках. Кнопки сворачивания — chevron в стеклянной рамке с акцентной подсветкой при наведении.

Preference Значения По умолчанию
collapseAnimation "instant", "animated" "animated"
collapseDuration 100–800 (ms) 240

Старые ключи (panelPattern, panelPatternOnNav, …) при загрузке автоматически мигрируют в per-target формат, если в localStorage ещё нет panelPatternNav.

Навигация по коду

Быстрый поиск: в script.js и styles.css — секции с разделителем // ===== / /* =====; в index.html — комментарий-оглавление в начале файла.

script.js

Секция Содержимое
Panel pattern catalog and defaults panelPatternCatalog, availablePanelPatterns, defaults по целям
Panel pattern preferences normalize, apply, custom URL, UI (renderPatternSwatches, …)
Settings import, export and reset applyStoredPreferences, export/import JSON
Preferences application applyPreferences, sync контролов ⚙
Collapse state handling анимация раскрытия details

Ключевые функции узоров: applyPanelPatternPreferences, applyPatternIsolationPreference, getPanelPatternPreferenceKey, setupPatternControls.

styles.css

Секция Содержимое
01. Theme Tokens --pattern-tile-*, mapping html[data-panel-pattern-*], --panel-layer-*
05. Sidebar Panel ::before / ::after узоров, layer isolation на catalog/group/card
06. Settings Popup .pattern-swatches, .pattern-custom-panel, .settings-actions

index.html

Блок Атрибуты / классы
Popup ⚙ [data-settings-panel], секции [data-settings-section="…"]
Узоры [data-pattern-targets-panel], [data-pattern-swatches="{target}"]
Разделение слоёв [data-pattern-isolation]
Экспорт [data-settings-reset], [data-settings-export], [data-settings-import]

Добавить новый встроенный пресет

  1. Добавить id в availablePanelPatterns и запись в panelPatternCatalog (script.js)
  2. Определить --pattern-tile-{id}-image и -size в :root (styles.css)
  3. Добавить строки html[data-panel-pattern-{nav|section|group|item}="{id}"] для всех четырёх целей
  4. Добавить превью .pattern-swatch[data-panel-pattern="{id}"]::before в секции настроек
  5. При необходимости — override SVG для html[data-theme="light"]

Значения сохраняются в localStorage и выставляют data-panel-pattern-* на <html> и CSS-переменные --panel-pattern-{nav|section|group|item}-*, --panel-gradient-{target}-*.

Поведение интерфейса

Поиск

Фильтрует каталог и подсвечивает совпадения в дереве и карточках. Ищет по:

  • названию элемента
  • номеру
  • тегам
  • типу сервиса
  • названиям родительских разделов

Дерево навигации

  • Клик по папке — раскрытие/сворачивание
  • Клик по названию — прокрутка к элементу в каталоге
  • Состояние раскрытия сохраняется отдельно для дерева и контента (ecosystem-panel-collapse-state)

Адаптив

  • Touch-устройства (pointer: coarse / hover: none): stacked-layout, меню сверху, ручки resize скрыты
  • Мышь (pointer: fine + hover: hover): sidebar + resizer + каталог в колонках
  • На узких экранах число колонок каталога дополнительно ограничивается в CSS (чтобы сетка не ломалась)

Ширина и габариты layout

Лимиты задаются в settings.json → defaults и limits, настраиваются пользователем в ⚙ → Габариты (если allowLayoutLimitsChange: true):

  • shellMaxWidth — максимальная ширина всей панели (по умолчанию до 2800px)
  • sidebarMaxWidth / sidebarMaxWidthWide — потолок ширины меню (1 колонка дерева / несколько)
  • topBarMinHeight / topBarMaxHeight — диапазон высоты шапки в режиме «Сверху»
  • Текущие sidebarWidth и topBarHeight — drag-resize; сохраняются в localStorage

localStorage

Ключ Содержимое
ecosystem-panel-preferences Тема, акцент, узоры, вид, колонки, patternIsolation, custom-URL, ширина sidebar, export/import
ecosystem-panel-collapse-state Раскрытые/свернутые ветки дерева и каталога

Разработка

Добавить элемент

  1. Открой panel-data.json (или свой JSON из dataUrl)
  2. Найди нужный раздел / подраздел или добавь в items / корневой items
  3. Добавь объект с полями title, type, при необходимости tags, url, id, number
  4. Обнови страницу

Добавить иконку и тип

  1. Положи SVG в assets/ (или переиспользуй существующую)
  2. Зарегистрируй тип в serviceMeta JSON или в defaultServiceMeta в script.js

Внешний источник данных

{
  "dataUrl": "https://your-server/panel-data.json"
}

Убедись, что сервер отдаёт Content-Type: application/json и разрешает CORS при другом домене.

Ограничения

  • Требуется HTTP-сервер (fetch не работает с file://)
  • Внешний dataUrl должен быть доступен по CORS
  • Настройки UI хранятся локально в браузере и не синхронизируются между устройствами

Лицензия

Приватный проект. Используй и изменяй по своему усмотрению.