| assets | ||
| examples | ||
| index.html | ||
| panel-data-flat.example.json | ||
| panel-data-full.example.json | ||
| panel-data-mixed.example.json | ||
| panel-data-section-items.example.json | ||
| panel-data.json | ||
| README.md | ||
| script.js | ||
| settings-top-bar.example.json | ||
| settings.json | ||
| styles.css | ||
Панель экосистемы
Статическая навигационная панель для быстрого доступа к сервисам инфраструктуры. Поддерживает гибкую иерархию данных, поиск, дерево навигации, несколько режимов отображения и настройку внешнего вида.
Стек: HTML, CSS, vanilla JavaScript. Без сборщиков и фреймворков.
Оглавление
- Возможности
- Быстрый старт
- Структура проекта
- Конфигурация
settings.json- Пресет организации (
defaults+features) - Форматы иерархии
- Схема
panel-data - Поля данных
- Примеры данных
- Готовые демо-страницы (
examples/) - Типы сервисов (
serviceMeta) - Рекомендуемые типы сервисов
- Настройки интерфейса (⚙)
- Узоры панелей
- Разделение слоёв узоров
- Свой узор (custom)
- Экспорт и импорт настроек
- Навигация по коду
- Поведение интерфейса
- localStorage
- Разработка
- Ограничения
- Лицензия
Возможности
- Древовидная навигация слева в стиле файлового менеджера
- Каталог карточек справа с 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 |
Чтобы подключить тип:
- Добавь запись в
serviceMetaвашего JSON или вdefaultServiceMetaвscript.js - При необходимости положи отдельную SVG в
assets/ - Укажи
"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 только три слайдера узора (без градиента).
Как узор попадает на экран
preferences→applyPanelPatternPreferences()вscript.js- На
<html>:data-panel-pattern-nav="hex"(и section / group / item) - CSS-переменные:
--panel-pattern-{target}-opacity,-height,-fade-start,--panel-gradient-{target}-* - Встроенные пресеты:
styles.css→--pattern-tile-*и селекторыhtml[data-panel-pattern-*="…"] - Слои на блоке:
::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)
- В панели нужной цели выберите пресет «Свой»
- Появится блок с полями:
- Изображение —
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] |
Добавить новый встроенный пресет
- Добавить id в
availablePanelPatternsи запись вpanelPatternCatalog(script.js) - Определить
--pattern-tile-{id}-imageи-sizeв:root(styles.css) - Добавить строки
html[data-panel-pattern-{nav|section|group|item}="{id}"]для всех четырёх целей - Добавить превью
.pattern-swatch[data-panel-pattern="{id}"]::beforeв секции настроек - При необходимости — 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 |
Раскрытые/свернутые ветки дерева и каталога |
Разработка
Добавить элемент
- Открой
panel-data.json(или свой JSON изdataUrl) - Найди нужный раздел / подраздел или добавь в
items/ корневойitems - Добавь объект с полями
title,type, при необходимостиtags,url,id,number - Обнови страницу
Добавить иконку и тип
- Положи SVG в
assets/(или переиспользуй существующую) - Зарегистрируй тип в
serviceMetaJSON или вdefaultServiceMetaвscript.js
Внешний источник данных
{
"dataUrl": "https://your-server/panel-data.json"
}
Убедись, что сервер отдаёт Content-Type: application/json и разрешает CORS при другом домене.
Ограничения
- Требуется HTTP-сервер (
fetchне работает сfile://) - Внешний
dataUrlдолжен быть доступен по CORS - Настройки UI хранятся локально в браузере и не синхронизируются между устройствами
Лицензия
Приватный проект. Используй и изменяй по своему усмотрению.