Доработки для интеграции
This commit is contained in:
@@ -3,3 +3,10 @@
|
|||||||
Линтер: oxlint
|
Линтер: oxlint
|
||||||
Библиотека компонентов: consta (https://www.npmjs.com/package/@consta/uikit)
|
Библиотека компонентов: consta (https://www.npmjs.com/package/@consta/uikit)
|
||||||
Стили (цвета) взяты из презентации
|
Стили (цвета) взяты из презентации
|
||||||
|
|
||||||
|
## Документация
|
||||||
|
|
||||||
|
- [`docs/portal-front-integration.md`](docs/portal-front-integration.md) — связка с portal-front: запуск в dev, деплой в prod, тестовый стенд, диагностика
|
||||||
|
- [`docs/backend-quickstart.md`](docs/backend-quickstart.md) — короткий гайд по встраиванию в поле Bitrix
|
||||||
|
- [`docs/bitrix-integration.md`](docs/bitrix-integration.md) — полная спецификация для бэкенда: контракт postMessage, формат JSON, варианты деплоя
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,15 @@
|
|||||||
|
|
||||||
1. Собрать в репозитории `portal-chart-builder`:
|
1. Собрать в репозитории `portal-chart-builder`:
|
||||||
```
|
```
|
||||||
VITE_BASE_PATH=/local/diagram-app/ npm run build
|
npm run build
|
||||||
```
|
```
|
||||||
- `VITE_BASE_PATH` — подпуть, под которым будет отдаваться приложение. Должен совпадать с тем, куда положите `dist/` на шаге 2.
|
- Переменные окружения не нужны. Ссылки на JS/CSS внутри `index.html` относительные (`base: './'` в `vite.config.ts`), поэтому сборка работает из любого каталога, куда её положат, — подпуть на этапе сборки знать не требуется.
|
||||||
- `VITE_BITRIX_ORIGIN` здесь **не нужен**: приложение и портал на одном домене, origin хоста приложение берёт из `window.location.origin` в рантайме. Задавать переменную нужно только если хост на другом origin — например, локальная разработка (dev-сервер хоста и `vite preview` этого приложения на разных портах) или деплой отдельным контейнером (см. `bitrix-integration.md`, раздел 7, вариант B).
|
- `VITE_BITRIX_ORIGIN` здесь **не нужен**: приложение и портал на одном домене, origin хоста приложение берёт из `window.location.origin` в рантайме. Задавать переменную нужно только если хост на другом origin — например, локальная разработка (dev-сервер хоста и `vite preview` этого приложения на разных портах) или деплой отдельным контейнером (см. `bitrix-integration.md`, раздел 7, вариант B).
|
||||||
2. Скопировать содержимое `dist/` в `local/diagram-app/` внутри каталога сайта на сервере (тот же каталог, где лежит остальной код портала — `bitrix_www`/аналог на проде).
|
2. Скопировать содержимое `dist/` в `local/diagram-app/` внутри каталога сайта на сервере (тот же каталог, где лежит остальной код портала — `bitrix_www`/аналог на проде).
|
||||||
3. Готово — но с оговоркой: приложение доступно **не** по фиксированному `index.html`.
|
3. Готово — но с оговоркой: приложение доступно **не** по фиксированному `index.html`.
|
||||||
|
|
||||||
**Имя `index.html` в `dist/` — с хешем содержимого**, например `index-2fddeca6.html`, а не просто `index.html` (см. `vite.config.ts`, плагин `hashIndexHtml` — хеширует и сам HTML-файл, не только JS/CSS, которые Vite и так хеширует по умолчанию). Хеш меняется при каждой значимой пересборке и не меняется, если содержимое не менялось. Значит:
|
**Имя `index.html` в `dist/` — с хешем содержимого**, например `index-2fddeca6.html`, а не просто `index.html` (см. `vite.config.ts`, плагин `hashIndexHtml` — хеширует и сам HTML-файл, не только JS/CSS, которые Vite и так хеширует по умолчанию). Хеш меняется при каждой значимой пересборке и не меняется, если содержимое не менялось. Значит:
|
||||||
- **`src` у iframe нельзя хардкодить строкой** — нужно на своей стороне подставлять актуальное имя файла (например, взять единственный `index-*.html` в `local/diagram-app/`, или через отдельный метод получения актуального имени — решение за бэкендом).
|
- **`src` у iframe нельзя хардкодить строкой** — нужно на своей стороне подставлять актуальное имя файла. Готовый способ: рядом с билдом сборка кладёт `app.json` со **стабильным** именем и актуальным именем точки входа внутри — `{ "index": "index-2fddeca6.html" }`. Прочитать его можно и с бэкенда, и прямо из браузера (`fetch('app.json', { cache: 'no-store' })`). Альтернативы — взять единственный `index-*.html` в каталоге или отдать имя отдельным методом; выбор за бэкендом.
|
||||||
- Ручной cache-busting (`?v=...`) для JS/CSS/HTML **больше не нужен**: раз URL меняется вместе с содержимым, старый закэшированный файл просто никогда больше не запрашивается, как только известно новое имя.
|
- Ручной cache-busting (`?v=...`) для JS/CSS/HTML **больше не нужен**: раз URL меняется вместе с содержимым, старый закэшированный файл просто никогда больше не запрашивается, как только известно новое имя.
|
||||||
- **После каждого редеплоя старые `index-<hash>.html`/`index-<hash>.js`/`index-<hash>.css` от прошлых версий стоит убирать** из `local/diagram-app/` — они не удаляются сами, и если способ поиска актуального файла — «взять единственный файл по маске», их накопление сломает эту логику.
|
- **После каждого редеплоя старые `index-<hash>.html`/`index-<hash>.js`/`index-<hash>.css` от прошлых версий стоит убирать** из `local/diagram-app/` — они не удаляются сами, и если способ поиска актуального файла — «взять единственный файл по маске», их накопление сломает эту логику.
|
||||||
|
|
||||||
|
|||||||
@@ -219,16 +219,23 @@ Backend не обязан ничего валидировать глубже «
|
|||||||
|
|
||||||
Из этого следует главное отличие от более ранней версии данного документа: **`$appUrl` в PHP-классе (раздел 3) больше нельзя хардкодить как строку** — конкретное имя `index-<hash>.html` меняется при каждой пересборке приложения. Бэкенду нужен свой способ узнавать актуальное имя файла на момент рендера страницы (например, взять единственный `index-*.html` из каталога деплоя, или через отдельный метод/эндпоинт, который вернёт актуальное имя, — механизм на усмотрение бэкенда, здесь не фиксирован, уточняйте на момент внедрения).
|
Из этого следует главное отличие от более ранней версии данного документа: **`$appUrl` в PHP-классе (раздел 3) больше нельзя хардкодить как строку** — конкретное имя `index-<hash>.html` меняется при каждой пересборке приложения. Бэкенду нужен свой способ узнавать актуальное имя файла на момент рендера страницы (например, взять единственный `index-*.html` из каталога деплоя, или через отдельный метод/эндпоинт, который вернёт актуальное имя, — механизм на усмотрение бэкенда, здесь не фиксирован, уточняйте на момент внедрения).
|
||||||
|
|
||||||
|
**Готовый механизм — `app.json` рядом с билдом.** Тот же плагин `hashIndexHtml` кладёт в `dist/` файл со стабильным, никогда не меняющимся именем:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "index": "index-2fddeca6.html" }
|
||||||
|
```
|
||||||
|
|
||||||
|
Его можно прочитать как с бэкенда при рендере страницы, так и на клиенте — `fetch('<каталог>/app.json', { cache: 'no-store' })`, — и уже к полученному имени дописывать `?mode=view`. `no-store` здесь обязателен: имя файла стабильно, значит сам манифест попадает под тот же 30-дневный `Cache-Control`, что и остальная статика. Этим сейчас пользуется portal-front (`src/helpers/chart-builder.ts`), пока у бэка нет своего метода.
|
||||||
|
|
||||||
Взамен это полностью снимает необходимость в отдельном cache-busting query-параметре (`?v=...`) — раньше он был обязателен и для самого `index.html`, и (до появления хеша у самого файла) для `index.js`/`index.css` внутри него. Теперь URL меняется вместе с содержимым: старый закэшированный `index-OLDHASH.html` просто никогда больше не запрашивается, как только хост узнаёт новое имя — что убирает разом оба уровня кэш-проблемы, описанной в более ранней версии этого раздела.
|
Взамен это полностью снимает необходимость в отдельном cache-busting query-параметре (`?v=...`) — раньше он был обязателен и для самого `index.html`, и (до появления хеша у самого файла) для `index.js`/`index.css` внутри него. Теперь URL меняется вместе с содержимым: старый закэшированный `index-OLDHASH.html` просто никогда больше не запрашивается, как только хост узнаёт новое имя — что убирает разом оба уровня кэш-проблемы, описанной в более ранней версии этого раздела.
|
||||||
|
|
||||||
Порядок действий:
|
Порядок действий:
|
||||||
|
|
||||||
1. В репозитории `portal-chart-builder` собрать статику с двумя build-time переменными:
|
1. В репозитории `portal-chart-builder` собрать статику с двумя build-time переменными:
|
||||||
```
|
```
|
||||||
VITE_BASE_PATH=/local/diagram-app/ npm run build
|
npm run build
|
||||||
```
|
```
|
||||||
`VITE_BITRIX_ORIGIN` в этом варианте не задаётся — см. примечание в конце раздела.
|
Переменные окружения не нужны. `VITE_BITRIX_ORIGIN` в этом варианте не задаётся — см. примечание в конце раздела. Пути к JS/CSS внутри `index.html` относительные (`base: './'` в `vite.config.ts`), поэтому один и тот же `dist/` работает из любого каталога размещения — раньше это задавалось переменной `VITE_BASE_PATH`, которая должна была точно совпадать с каталогом деплоя и при расхождении давала пустой iframe (html грузится, его JS уходит в 404).
|
||||||
`VITE_BASE_PATH` — путь, под которым будет отдаваться приложение (нужен, чтобы ссылки на JS/CSS внутри `dist/index.html` были корректными под этим подпутём; см. `vite.config.ts`). По умолчанию, если переменная не задана, `base` — `/` (не влияет на локальную разработку, `npm run dev`).
|
|
||||||
2. Скопировать содержимое `dist/` в `local/diagram-app/` внутри host-каталога, который смонтирован как `bitrix_www` (на текущем дев-стенде это `/home/nikit/bitrix/www/local/diagram-app/` — точный путь для конкретного стенда уточняется у того, кто его поднимал/администрирует).
|
2. Скопировать содержимое `dist/` в `local/diagram-app/` внутри host-каталога, который смонтирован как `bitrix_www` (на текущем дев-стенде это `/home/nikit/bitrix/www/local/diagram-app/` — точный путь для конкретного стенда уточняется у того, кто его поднимал/администрирует).
|
||||||
3. В PHP-классе (раздел 3): `$appUrl = $appOrigin . '/local/diagram-app/' . $actualIndexFileName;`, `$appOrigin` — origin портала (без пути), `$actualIndexFileName` — актуальное имя `index-<hash>.html` в `local/diagram-app/`, полученное способом на усмотрение бэкенда (см. врезку про кэш выше — хардкодить `index.html` строкой уже нельзя).
|
3. В PHP-классе (раздел 3): `$appUrl = $appOrigin . '/local/diagram-app/' . $actualIndexFileName;`, `$appOrigin` — origin портала (без пути), `$actualIndexFileName` — актуальное имя `index-<hash>.html` в `local/diagram-app/`, полученное способом на усмотрение бэкенда (см. врезку про кэш выше — хардкодить `index.html` строкой уже нельзя).
|
||||||
4. Обновление после правок в React-приложении — тот же `npm run build` + копирование поверх старых файлов. Пересборка/перезапуск контейнеров Bitrix не нужны. Старый `index-<hash>.html` от предыдущей сборки при этом не удаляется автоматически — если способ поиска актуального файла (шаг 3) предполагает «взять единственный файл по маске», после нескольких деплоев в каталоге стоит убирать файлы прошлых версий, иначе маска перестанет быть однозначной.
|
4. Обновление после правок в React-приложении — тот же `npm run build` + копирование поверх старых файлов. Пересборка/перезапуск контейнеров Bitrix не нужны. Старый `index-<hash>.html` от предыдущей сборки при этом не удаляется автоматически — если способ поиска актуального файла (шаг 3) предполагает «взять единственный файл по маске», после нескольких деплоев в каталоге стоит убирать файлы прошлых версий, иначе маска перестанет быть однозначной.
|
||||||
|
|||||||
@@ -0,0 +1,153 @@
|
|||||||
|
# Связка с portal-front: запуск в dev, деплой в prod, тестовый стенд
|
||||||
|
|
||||||
|
Документ про встраивание конструктора в **portal-front** — React-приложение
|
||||||
|
портала. Про встраивание в админку Bitrix (пользовательский тип свойства
|
||||||
|
инфоблока) — отдельные документы:
|
||||||
|
[`bitrix-integration.md`](./bitrix-integration.md) и
|
||||||
|
[`backend-quickstart.md`](./backend-quickstart.md).
|
||||||
|
|
||||||
|
## 1. Как это устроено
|
||||||
|
|
||||||
|
Портал открывает конструктор в `<iframe>` **в режиме просмотра** и передаёт ему
|
||||||
|
схему через `postMessage`. Три вещи, которые надо знать, прежде чем что-то
|
||||||
|
запускать.
|
||||||
|
|
||||||
|
**Имя точки входа не константа.** Сборка выдаёт `index-<hash>.html`
|
||||||
|
(см. `vite.config.ts`, плагин `hashIndexHtml`), имя меняется при каждой
|
||||||
|
значимой пересборке. Рядом кладётся манифест со стабильным именем:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "index": "index-300d7ef5.html" }
|
||||||
|
```
|
||||||
|
|
||||||
|
Портал читает его при открытии модалки — `fetch('app.json', { cache: 'no-store' })`
|
||||||
|
в `src/helpers/chart-builder.ts` — и уже к полученному имени дописывает
|
||||||
|
параметры. Поэтому выкладка конструктора **не требует пересборки портала**.
|
||||||
|
|
||||||
|
**Режим просмотра включается параметром.** Портал сам дописывает `?mode=view`:
|
||||||
|
редактор стартует в просмотре, переключатель режимов скрыт, `diagram:change`
|
||||||
|
портал игнорирует. В этом режиме приложение **всегда** берёт origin хоста из
|
||||||
|
`window.location.origin` и игнорирует `VITE_BITRIX_ORIGIN` — переменная нужна
|
||||||
|
только админке Bitrix, где встраивание идёт в режиме редактирования.
|
||||||
|
|
||||||
|
**Пути внутри сборки относительные** (`base: './'`), поэтому один и тот же
|
||||||
|
`dist/` работает из любого каталога размещения и переменной с путём больше нет.
|
||||||
|
|
||||||
|
Контракт обмена — `bitrix-integration.md`, раздел 4: приложение шлёт
|
||||||
|
`diagram:ready`, хост в ответ `diagram:init` с JSON-строкой схемы. На стороне
|
||||||
|
портала это `src/hooks/useDiagramBridge.ts`; если `diagram:ready` не пришёл за
|
||||||
|
10 секунд, в модалке появляется «Не удалось загрузить схему».
|
||||||
|
|
||||||
|
## 2. Dev
|
||||||
|
|
||||||
|
Портал на `http://localhost:5173`, конструктор на `http://localhost:5800`.
|
||||||
|
Порты разные, но портал проксирует конструктор на свой origin — без этого
|
||||||
|
режим просмотра не заработает (приложение отправит сообщения на 5800, то есть
|
||||||
|
самому себе, а не порталу).
|
||||||
|
|
||||||
|
Прокси уже прописан в `vite.config.ts` портала:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
'/local/modules/vniigaz.bpmn/assets': {
|
||||||
|
target: 'http://localhost:5800',
|
||||||
|
changeOrigin: false,
|
||||||
|
rewrite: (path) => path.replace('/local/modules/vniigaz.bpmn/assets', '')
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Порядок запуска:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cd D:\Development\Work\portal-chart-builder; npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cd D:\Development\Work\portal-chart-builder; npm run preview
|
||||||
|
```
|
||||||
|
|
||||||
|
Порт 5800 зафиксирован в `vite.config.ts` (`preview.port` + `strictPort`), флаги
|
||||||
|
передавать не нужно. Если порт занят, `preview` упадёт с ошибкой, а не уедет на
|
||||||
|
соседний порт молча — иначе прокси портала указывал бы в пустоту.
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cd D:\Development\Work\portal-front; npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Переменные окружения не нужны ни там, ни там.
|
||||||
|
|
||||||
|
`npm run dev` самого конструктора для связки **не подходит**: `postMessage`-мост
|
||||||
|
включается только в production-сборке (`isProd` в `src/App.tsx`), в dev работает
|
||||||
|
`devAdapter` поверх localStorage. После правок в конструкторе — заново
|
||||||
|
`npm run build`; `preview` перезапускать не нужно, он читает файлы с диска.
|
||||||
|
|
||||||
|
Проверка, что связка поднята:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
curl.exe -s http://localhost:5173/local/modules/vniigaz.bpmn/assets/app.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Пустой ответ — не поднят `preview` или не собран конструктор.
|
||||||
|
|
||||||
|
## 3. Prod (портал, бэк и конструктор на одном домене)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cd D:\Development\Work\portal-chart-builder; npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
Дальше **очистить** каталог `local/modules/vniigaz.bpmn/assets/` в дереве сайта
|
||||||
|
и скопировать туда содержимое `dist/`. Именно очистить: имена файлов содержат
|
||||||
|
хеш, старые сборки не перезаписываются, а накапливаются — и ломают запасной
|
||||||
|
способ поиска точки входа «взять единственный `index-*.html`».
|
||||||
|
|
||||||
|
Портал пересобирается **только когда меняется сам портал**:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cd D:\Development\Work\portal-front; npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
Проверка после выкладки:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
curl.exe -s https://ВАШ-ДОМЕН/local/modules/vniigaz.bpmn/assets/app.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Имя в ответе должно совпадать с реально лежащим файлом. Открыть `index-<hash>.html`
|
||||||
|
напрямую в браузере тоже полезно: вечное «Загрузка диаграммы…» там — норма,
|
||||||
|
приложение ждёт `diagram:init` от родителя, которого при прямом открытии нет.
|
||||||
|
Значит, статика раздаётся правильно.
|
||||||
|
|
||||||
|
## 4. Тестовый стенд и админка Bitrix
|
||||||
|
|
||||||
|
Отличие одно: там встраивание идёт **в режиме редактирования**, без `?mode=view`,
|
||||||
|
и только в этом случае учитывается `VITE_BITRIX_ORIGIN`.
|
||||||
|
|
||||||
|
Рекомендация — не задавать её вообще (удалить `.env.production.local`): origin
|
||||||
|
возьмётся из `window.location.origin` и совпадёт с любым адресом, по которому
|
||||||
|
открыли админку — `localhost:8590`, IP или домен. Приложение и админка на одном
|
||||||
|
домене, так что это корректно всегда.
|
||||||
|
|
||||||
|
Если переменная всё-таки задаётся, её значение должно совпадать с адресной
|
||||||
|
строкой браузера **посимвольно, включая порт**: `http://192.168.100.146` и
|
||||||
|
`http://192.168.100.146:8590` — разные origin. При расхождении обмен молча не
|
||||||
|
состоится, ошибок в консоли не будет.
|
||||||
|
|
||||||
|
Второе требование к встраивающей стороне — не писать имя `index-<hash>.html`
|
||||||
|
строкой, а брать актуальное из `app.json` (или искать единственный `index-*.html`,
|
||||||
|
но тогда очистка каталога при деплое обязательна). Подробности — в
|
||||||
|
`backend-quickstart.md`, раздел 1.
|
||||||
|
|
||||||
|
## 5. Переменные окружения
|
||||||
|
|
||||||
|
| Переменная | Dev | Prod (портал) | Админка Bitrix |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `VITE_BASE_PATH` | не существует, `base: './'` | то же | то же |
|
||||||
|
| `VITE_BITRIX_ORIGIN` | игнорируется в `mode=view` | игнорируется в `mode=view` | учитывается; лучше не задавать |
|
||||||
|
|
||||||
|
## 6. Если схема не появилась
|
||||||
|
|
||||||
|
| Что видно | Причина | Что делать |
|
||||||
|
|---|---|---|
|
||||||
|
| «Не удалось загрузить схему» в модалке | манифест не отдался либо не пришёл `diagram:ready` за 10 секунд | проверить `app.json` через curl, затем в Network — дошёл ли `index-<hash>.js` |
|
||||||
|
| Пустой iframe, в консоли 404 на `index-<hash>.js` | файлы сборки не докопированы или скопированы не в тот каталог | перекопировать `dist/` целиком |
|
||||||
|
| Приложение висит на «Загрузка диаграммы…» | `diagram:ready` ушёл не туда: origin хоста не совпал с ожидаемым | для админки — сверить `VITE_BITRIX_ORIGIN` с адресной строкой; для dev — проверить, что конструктор открывается через прокси портала, а не напрямую с 5800 |
|
||||||
|
| Портал открывает старую версию схемы | в каталоге лежат файлы прошлых сборок, `app.json` указывает на новый, а старый html закеширован браузером | очистить каталог при деплое; хеш в имени сам по себе кэш не победит, если старый файл остаётся на месте |
|
||||||
@@ -28,6 +28,7 @@ import "@xyflow/react/dist/style.css";
|
|||||||
import { Button } from "@consta/uikit/Button";
|
import { Button } from "@consta/uikit/Button";
|
||||||
import { ChoiceGroup } from "@consta/uikit/ChoiceGroup";
|
import { ChoiceGroup } from "@consta/uikit/ChoiceGroup";
|
||||||
import { Breadcrumbs } from "@consta/uikit/Breadcrumbs";
|
import { Breadcrumbs } from "@consta/uikit/Breadcrumbs";
|
||||||
|
import { IconArrowRight } from "@consta/icons/IconArrowRight";
|
||||||
import { nodeTypes, NodesWithChildrenContext, ModeContext } from "./nodes";
|
import { nodeTypes, NodesWithChildrenContext, ModeContext } from "./nodes";
|
||||||
import { NodeInspector } from "./NodeInspector";
|
import { NodeInspector } from "./NodeInspector";
|
||||||
import { EdgeInspector } from "./EdgeInspector";
|
import { EdgeInspector } from "./EdgeInspector";
|
||||||
@@ -755,6 +756,13 @@ function DiagramCanvas({ initialValue, onChange, lockedMode }: DiagramEditorProp
|
|||||||
edges.find((edge) => edge.id === selectedEdgeId)
|
edges.find((edge) => edge.id === selectedEdgeId)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
// В режиме просмотра панель — не постоянная часть интерфейса, а всплывающая
|
||||||
|
// деталь выбранной фигуры: без выбора её нечем наполнить (подсказки про
|
||||||
|
// «Дублировать»/«Удалить» там неуместны), поэтому холст занимает всю ширину.
|
||||||
|
// В режиме редактирования панель нужна всегда — там она несёт инструменты,
|
||||||
|
// а не только описание выбранного узла.
|
||||||
|
const isAsideVisible = mode === "edit" || Boolean(selectedNode || selectedEdge);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="diagram-shell">
|
<div className="diagram-shell">
|
||||||
<div className="diagram-canvas" ref={canvasWrapperRef}>
|
<div className="diagram-canvas" ref={canvasWrapperRef}>
|
||||||
@@ -891,7 +899,22 @@ function DiagramCanvas({ initialValue, onChange, lockedMode }: DiagramEditorProp
|
|||||||
</NodesWithChildrenContext.Provider>
|
</NodesWithChildrenContext.Provider>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{isAsideVisible && (
|
||||||
<aside className="diagram-aside">
|
<aside className="diagram-aside">
|
||||||
|
{mode === "view" && (
|
||||||
|
<div className="diagram-aside__close">
|
||||||
|
<Button
|
||||||
|
size="s"
|
||||||
|
view="clear"
|
||||||
|
form="round"
|
||||||
|
onlyIcon
|
||||||
|
iconLeft={IconArrowRight}
|
||||||
|
title="Скрыть панель"
|
||||||
|
aria-label="Скрыть панель"
|
||||||
|
onClick={onPaneClick}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{selectedNode ?
|
{selectedNode ?
|
||||||
<NodeInspector
|
<NodeInspector
|
||||||
nodeId={selectedNode.id}
|
nodeId={selectedNode.id}
|
||||||
@@ -936,6 +959,7 @@ function DiagramCanvas({ initialValue, onChange, lockedMode }: DiagramEditorProp
|
|||||||
</details>
|
</details>
|
||||||
)}
|
)}
|
||||||
</aside>
|
</aside>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,6 +178,16 @@
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Кнопка «скрыть панель» — только в режиме просмотра. Прижата вправо, к
|
||||||
|
* внешнему краю: стрелка указывает наружу, в сторону, куда панель уезжает.
|
||||||
|
*/
|
||||||
|
.diagram-aside__close {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-bottom: -8px;
|
||||||
|
}
|
||||||
|
|
||||||
.diagram-aside__json summary {
|
.diagram-aside__json summary {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
|||||||
+32
-13
@@ -1,7 +1,7 @@
|
|||||||
import { createHash } from 'node:crypto'
|
import { createHash } from 'node:crypto'
|
||||||
import { existsSync, readFileSync, renameSync } from 'node:fs'
|
import { existsSync, readFileSync, renameSync, writeFileSync } from 'node:fs'
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import { defineConfig, loadEnv, type Plugin } from 'vite'
|
import { defineConfig, type Plugin } from 'vite'
|
||||||
import react from '@vitejs/plugin-react'
|
import react from '@vitejs/plugin-react'
|
||||||
|
|
||||||
// Vite always writes the HTML entry under its source name (index.html stays
|
// Vite always writes the HTML entry under its source name (index.html stays
|
||||||
@@ -14,6 +14,9 @@ import react from '@vitejs/plugin-react'
|
|||||||
// (confirmed empirically — it never showed up in generateBundle's `bundle`),
|
// (confirmed empirically — it never showed up in generateBundle's `bundle`),
|
||||||
// so this hashes and renames the file directly on disk after the whole build
|
// so this hashes and renames the file directly on disk after the whole build
|
||||||
// has finished writing it, rather than fighting the bundler's own emit step.
|
// has finished writing it, rather than fighting the bundler's own emit step.
|
||||||
|
// Stable entry point for hosts: never hashed, always present next to the build.
|
||||||
|
const MANIFEST_FILE_NAME = 'app.json'
|
||||||
|
|
||||||
function hashIndexHtml(): Plugin {
|
function hashIndexHtml(): Plugin {
|
||||||
let outDir = 'dist'
|
let outDir = 'dist'
|
||||||
return {
|
return {
|
||||||
@@ -29,24 +32,40 @@ function hashIndexHtml(): Plugin {
|
|||||||
// Hashing the final content (already contains the hashed JS/CSS refs)
|
// Hashing the final content (already contains the hashed JS/CSS refs)
|
||||||
// means this name only changes when something meaningful actually did.
|
// means this name only changes when something meaningful actually did.
|
||||||
const hash = createHash('md5').update(readFileSync(htmlPath)).digest('hex').slice(0, 8)
|
const hash = createHash('md5').update(readFileSync(htmlPath)).digest('hex').slice(0, 8)
|
||||||
renameSync(htmlPath, path.resolve(outDir, `index-${hash}.html`))
|
const fileName = `index-${hash}.html`
|
||||||
|
renameSync(htmlPath, path.resolve(outDir, fileName))
|
||||||
|
|
||||||
|
// Manifest with a STABLE name, so an embedding host can look the hashed
|
||||||
|
// entry up at runtime instead of hardcoding it (and re-deploying itself
|
||||||
|
// on every rebuild of this app). See docs/bitrix-integration.md §7.
|
||||||
|
writeFileSync(
|
||||||
|
path.resolve(outDir, MANIFEST_FILE_NAME),
|
||||||
|
`${JSON.stringify({ index: fileName }, null, 2)}
|
||||||
|
`,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig(({ mode }) => {
|
export default defineConfig(() => {
|
||||||
// VITE_BASE_PATH is read here (config/Node context), so it needs an explicit
|
|
||||||
// loadEnv call — unlike VITE_BITRIX_ORIGIN, which Vite injects into
|
|
||||||
// import.meta.env automatically because it's only read from app code.
|
|
||||||
const env = loadEnv(mode, process.cwd(), '')
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// Needed when the build is served from a subpath (e.g. local/diagram-app/
|
// Relative, so the build works from any directory it gets deployed to
|
||||||
// inside the Bitrix docroot) instead of domain root — see
|
// without being told where that is. The host always opens a concrete file
|
||||||
// docs/bitrix-integration.md §7.
|
// (index-<hash>.html, see hashIndexHtml below), so asset URLs resolve
|
||||||
base: env.VITE_BASE_PATH || '/',
|
// against that file's own location — there is no client-side routing that
|
||||||
|
// would need an absolute base. This replaces the earlier VITE_BASE_PATH:
|
||||||
|
// a build-time path that had to match the deploy directory exactly, and
|
||||||
|
// silently produced a blank iframe when it didn't (html loads, its JS 404s).
|
||||||
|
base: './',
|
||||||
plugins: [react(), hashIndexHtml()],
|
plugins: [react(), hashIndexHtml()],
|
||||||
|
// Порт фиксирован: portal-front проксирует сюда сборку в dev-режиме
|
||||||
|
// (см. docs/portal-front-integration.md, раздел 2). strictPort, чтобы
|
||||||
|
// при занятом порте preview падал, а не уезжал на соседний молча.
|
||||||
|
preview: {
|
||||||
|
port: 5800,
|
||||||
|
strictPort: true,
|
||||||
|
},
|
||||||
build: {
|
build: {
|
||||||
// Same content hash Vite computes by default — just without its default
|
// Same content hash Vite computes by default — just without its default
|
||||||
// "assets/" subfolder prefix. The deploy target's own folder is itself
|
// "assets/" subfolder prefix. The deploy target's own folder is itself
|
||||||
|
|||||||
Reference in New Issue
Block a user