фикс кассы + кнопки на стойке в мнеме
This commit is contained in:
parent
36998bbcce
commit
8b177097a3
9 changed files with 218 additions and 16 deletions
15
CHANGELOG.md
15
CHANGELOG.md
|
|
@ -2,6 +2,21 @@
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## [2.9] — 2026-04-28
|
||||||
|
|
||||||
|
### Добавлено
|
||||||
|
|
||||||
|
- **Режим отображения IP-адреса в видео-окне** — новая настройка в разделе «Видео окна»: выбор между двумя вариантами:
|
||||||
|
- **Кнопка с IP-адресом** (по умолчанию) — кнопка `🔗 IP:порт` добавляется рядом с заголовком окна
|
||||||
|
- **Кнопка «Лещ»** — кнопка с фиксированным текстом «Лещ» и tooltip-подсказкой с URL устройства
|
||||||
|
- **Поддержка нового типа noVNC-страницы** — страница «Платёжный терминал» теперь корректно определяется как noVNC по `<title>` (работает до инициализации React)
|
||||||
|
|
||||||
|
### Исправлено
|
||||||
|
|
||||||
|
- **Переадресация кассы** — пофиксил редирект на кассе, оттуда тоже перекидывает на порт 5000
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## [2.8] — 2026-03-23
|
## [2.8] — 2026-03-23
|
||||||
|
|
||||||
### Добавлено
|
### Добавлено
|
||||||
|
|
|
||||||
98
CLAUDE.md
Normal file
98
CLAUDE.md
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
# CAPS Enhancer — Chrome Extension
|
||||||
|
|
||||||
|
Chrome-расширение (Manifest V3) для улучшения интерфейса CAPS/UTP: навигационные кнопки, автологин, замена логотипа, редирект noVNC.
|
||||||
|
|
||||||
|
## Архитектура
|
||||||
|
|
||||||
|
```
|
||||||
|
caps_enhancer-1/
|
||||||
|
├── manifest.json # MV3, update URL, content scripts on <all_urls>
|
||||||
|
├── js/
|
||||||
|
│ ├── background.js # Service worker: проверка обновлений (chrome.alarms)
|
||||||
|
│ ├── content.js # Инициализация: роутинг по типу страницы
|
||||||
|
│ ├── popup.js # Popup: быстрые переключатели
|
||||||
|
│ ├── options.js # Страница настроек: загрузка/сохранение конфига
|
||||||
|
│ └── strings.js # Все строки UI (русский)
|
||||||
|
├── modules/
|
||||||
|
│ ├── core.js # CONFIG, утилиты, loadConfig(), updateConfig()
|
||||||
|
│ ├── mnemoschema.js # Кнопки «Мнемосхема» и «Лещ» на главной странице
|
||||||
|
│ ├── video-window.js # Кнопка с IP или «Лещ» в заголовке видео-окна
|
||||||
|
│ ├── novnc-redirect.js # Редирект или кнопка на noVNC-странице
|
||||||
|
│ ├── auto-login.js # Автологин с профилями и правилами подсетей
|
||||||
|
│ └── close-handler.js # ESC закрывает видео-окна и карточки
|
||||||
|
├── options.html / popup.html
|
||||||
|
├── css/options.css / popup.css
|
||||||
|
└── server/ # Деплой: .crx, nginx.conf, .reg, update.xml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Конфигурация (core.js CONFIG)
|
||||||
|
|
||||||
|
Хранится в `chrome.storage.sync` под ключом `config`. Структура:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
debug: { enabled, modules: { core, video, mnemo, close, content, novnc, autologin } },
|
||||||
|
ports: { video: '5000', scheme: '8080' },
|
||||||
|
videoWindow: {
|
||||||
|
ipButtonMode: 'button' // 'button' — кнопка «🔗 IP:порт», 'clickable-title' — кнопка «Лещ»
|
||||||
|
},
|
||||||
|
novnc: { mode: 'button'|'redirect', redirectDelay: 500 },
|
||||||
|
mnemo: { mode: 'host-port'|'host-scheme', skipOnDomain, addVideoButton, addSchemeButton },
|
||||||
|
autologin: { enabled, profilePage1, profilePage2, subnetRules, profiles },
|
||||||
|
disabledSites: [],
|
||||||
|
replaceLogo: true,
|
||||||
|
uiVisible: true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
При добавлении нового поля конфига: обновить `CONFIG` в `core.js`, `updateConfig()` в `core.js`, `DEFAULT_CONFIG` в `options.js`, добавить элемент в `options.html`, загрузку/сохранение в `options.js`, строки в `strings.js`.
|
||||||
|
|
||||||
|
## Типы страниц (core.checkPageType)
|
||||||
|
|
||||||
|
| Тип | Признаки |
|
||||||
|
|-----|---------|
|
||||||
|
| `isMainPage` | логотип CleverPark + noscript-маркер |
|
||||||
|
| `isVideoPage` | noscript-маркер, нет логотипа |
|
||||||
|
| `isNoVNCPage` | noscript "You need to enable JavaScript" + один из признаков ниже |
|
||||||
|
|
||||||
|
**Признаки noVNC (второй критерий):**
|
||||||
|
- `div[class*="app_AppWindow__"]` — старый тип страницы (CSS-модульный класс React-компонента)
|
||||||
|
- `document.title` содержит `"Платёжный терминал"` — новый тип страницы (присутствует в HTML сразу при загрузке, до инициализации React)
|
||||||
|
|
||||||
|
**Важно:** не использовать DOM-элементы, которые появляются после загрузки React (например `div#app.App`, `div.multi-find-menu-container`) — они не существуют в момент первой проверки, что приводит к неверному определению типа страницы.
|
||||||
|
|
||||||
|
## Модуль video-window.js
|
||||||
|
|
||||||
|
Добавляет ссылку на IP устройства в заголовок видео-окна. Два режима (настройка `videoWindow.ipButtonMode`):
|
||||||
|
|
||||||
|
- **`button`** (по умолчанию): кнопка `🔗 IP:5000` после `<p>` в заголовке. Tooltip не задан.
|
||||||
|
- **`clickable-title`**: кнопка **«Лещ»** с тем же стилем и тем же переходом. При наведении — нативный tooltip с полным URL (`button.title`).
|
||||||
|
|
||||||
|
Оба режима используют `core.createStyledButton()` — одинаковая стилизация. IP извлекается из src видео-элемента `img[src*=":8888/screen"][src*="/getVideo/"]` через regex `/\/getVideo\/http:\/\/([^\/:?#]+)(?::(\d+))?/i`.
|
||||||
|
|
||||||
|
## Модуль novnc-redirect.js
|
||||||
|
|
||||||
|
Два режима (`novnc.mode`):
|
||||||
|
|
||||||
|
- **`redirect`**: автоматический переход на `http://<host>:<videoPort>`. Редирект происходит если текущий порт **не равен** целевому (не жёсткая привязка к порту 80).
|
||||||
|
- **`button`**: фиксированная кнопка в правом верхнем углу. После добавления следит за удалением через `MutationObserver` (15 сек) и добавляет кнопку снова — защита от React-рендеринга, который может очищать body.
|
||||||
|
|
||||||
|
Важно: у кнопки задан `dataset.capsUiDisplay = 'inline-block'`, чтобы `applyVisibility` не сбрасывала её display в `''`.
|
||||||
|
|
||||||
|
## Видимость UI (Shift+F1)
|
||||||
|
|
||||||
|
- `data-caps-ui="true"` — элементы расширения (скрываются при `uiVisible=false`)
|
||||||
|
- `data-caps-ui-display` — исходное значение `display`, восстанавливается при `uiVisible=true`; **обязательно задавать** при создании кнопок с нестандартным display
|
||||||
|
- `data-caps-original="true"` — оригинальные элементы (скрываются при `uiVisible=true`)
|
||||||
|
- Логотип меняет `src` вместо `display`
|
||||||
|
|
||||||
|
## Паттерны
|
||||||
|
|
||||||
|
- Каждый модуль — IIFE, расширяет `window.VideoIPRedirect`
|
||||||
|
- Общие утилиты: `core.createStyledButton()`, `core.debugLog()`, `core.retryWithBackoff()`
|
||||||
|
- Конфиг обновляется через `chrome.runtime.onMessage` → `CONFIG_UPDATED`
|
||||||
|
- Строки: все тексты UI только в `js/strings.js`, доступ через `getStr('options.key')`
|
||||||
|
|
||||||
|
## Деплой
|
||||||
|
|
||||||
|
См. [DEPLOY.md](DEPLOY.md). Кратко: упаковать `.crx` → загрузить на сервер → обновить `update.xml` с новой версией → версия в `manifest.json`.
|
||||||
|
|
@ -50,6 +50,7 @@ const DEFAULT_CONFIG = {
|
||||||
{ name: 'Профиль 1', username: '', password: '', autoSubmit: true, delay: 500 }
|
{ name: 'Профиль 1', username: '', password: '', autoSubmit: true, delay: 500 }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
videoWindow: { ipButtonMode: 'button' },
|
||||||
disabledSites: [],
|
disabledSites: [],
|
||||||
replaceLogo: true,
|
replaceLogo: true,
|
||||||
uiVisible: true
|
uiVisible: true
|
||||||
|
|
@ -70,6 +71,7 @@ const elements = {
|
||||||
mnemoSkipOnDomain: document.getElementById('mnemo-skip-on-domain'),
|
mnemoSkipOnDomain: document.getElementById('mnemo-skip-on-domain'),
|
||||||
mnemoAddSchemeButton: document.getElementById('mnemo-add-scheme-button'),
|
mnemoAddSchemeButton: document.getElementById('mnemo-add-scheme-button'),
|
||||||
mnemoAddVideoButton: document.getElementById('mnemo-add-video-button'),
|
mnemoAddVideoButton: document.getElementById('mnemo-add-video-button'),
|
||||||
|
videoIpButtonMode: document.getElementById('video-ip-button-mode'),
|
||||||
replaceLogo: document.getElementById('replace-logo'),
|
replaceLogo: document.getElementById('replace-logo'),
|
||||||
uiVisible: document.getElementById('ui-visible'),
|
uiVisible: document.getElementById('ui-visible'),
|
||||||
autologinEnabled: document.getElementById('autologin-enabled'),
|
autologinEnabled: document.getElementById('autologin-enabled'),
|
||||||
|
|
@ -350,6 +352,9 @@ function loadSettings() {
|
||||||
elements.portVideo.value = config.ports?.video || DEFAULT_CONFIG.ports.video;
|
elements.portVideo.value = config.ports?.video || DEFAULT_CONFIG.ports.video;
|
||||||
elements.portScheme.value = config.ports?.scheme || DEFAULT_CONFIG.ports.scheme;
|
elements.portScheme.value = config.ports?.scheme || DEFAULT_CONFIG.ports.scheme;
|
||||||
|
|
||||||
|
// Видео окна
|
||||||
|
elements.videoIpButtonMode.value = config.videoWindow?.ipButtonMode || DEFAULT_CONFIG.videoWindow.ipButtonMode;
|
||||||
|
|
||||||
// noVNC
|
// noVNC
|
||||||
elements.novncRedirect.checked = (config.novnc?.mode || DEFAULT_CONFIG.novnc.mode) === 'redirect';
|
elements.novncRedirect.checked = (config.novnc?.mode || DEFAULT_CONFIG.novnc.mode) === 'redirect';
|
||||||
elements.redirectDelay.value = config.novnc?.redirectDelay || DEFAULT_CONFIG.novnc.redirectDelay;
|
elements.redirectDelay.value = config.novnc?.redirectDelay || DEFAULT_CONFIG.novnc.redirectDelay;
|
||||||
|
|
@ -440,6 +445,9 @@ function saveSettings() {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ports: { video: portVideo, scheme: portScheme },
|
ports: { video: portVideo, scheme: portScheme },
|
||||||
|
videoWindow: {
|
||||||
|
ipButtonMode: elements.videoIpButtonMode.value
|
||||||
|
},
|
||||||
novnc: {
|
novnc: {
|
||||||
mode: elements.novncRedirect.checked ? 'redirect' : 'button',
|
mode: elements.novncRedirect.checked ? 'redirect' : 'button',
|
||||||
redirectDelay: parseInt(elements.redirectDelay.value) || 500
|
redirectDelay: parseInt(elements.redirectDelay.value) || 500
|
||||||
|
|
@ -493,6 +501,7 @@ function resetSettings() {
|
||||||
|
|
||||||
elements.portVideo.value = DEFAULT_CONFIG.ports.video;
|
elements.portVideo.value = DEFAULT_CONFIG.ports.video;
|
||||||
elements.portScheme.value = DEFAULT_CONFIG.ports.scheme;
|
elements.portScheme.value = DEFAULT_CONFIG.ports.scheme;
|
||||||
|
elements.videoIpButtonMode.value = DEFAULT_CONFIG.videoWindow.ipButtonMode;
|
||||||
elements.novncRedirect.checked = DEFAULT_CONFIG.novnc.mode === 'redirect';
|
elements.novncRedirect.checked = DEFAULT_CONFIG.novnc.mode === 'redirect';
|
||||||
elements.redirectDelay.value = DEFAULT_CONFIG.novnc.redirectDelay;
|
elements.redirectDelay.value = DEFAULT_CONFIG.novnc.redirectDelay;
|
||||||
elements.mnemoMode.value = DEFAULT_CONFIG.mnemo.mode;
|
elements.mnemoMode.value = DEFAULT_CONFIG.mnemo.mode;
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,13 @@ window.CAPS_STRINGS = {
|
||||||
headerTitle: "Настройки CAPS Enhancer",
|
headerTitle: "Настройки CAPS Enhancer",
|
||||||
headerSubtitle: "Настройте расширение под свои нужды",
|
headerSubtitle: "Настройте расширение под свои нужды",
|
||||||
|
|
||||||
|
// Видео окна
|
||||||
|
videoWindowTitle: "🎬 Видео окна",
|
||||||
|
videoIpButtonModeLabel: "Режим отображения IP-адреса",
|
||||||
|
videoIpButtonModeDesc: "Как отображать ссылку на настройки устройства в заголовке видео-окна",
|
||||||
|
videoIpButtonModeButton: "Кнопка с IP-адресом (🔗 IP:порт)",
|
||||||
|
videoIpButtonModeClickable: "Кнопка «Лещ» (без IP-адреса)",
|
||||||
|
|
||||||
// noVNC (страница экрана)
|
// noVNC (страница экрана)
|
||||||
novncTitle: "🖥️ Страница экрана",
|
novncTitle: "🖥️ Страница экрана",
|
||||||
novncRedirectLabel: "Включить переадресацию",
|
novncRedirectLabel: "Включить переадресацию",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "CAPS Enhancer",
|
"name": "CAPS Enhancer",
|
||||||
"version": "2.8",
|
"version": "2.9",
|
||||||
"description": "Добавляет всякие фичи для УТП в CAPS",
|
"description": "Добавляет всякие фичи для УТП в CAPS",
|
||||||
"update_url": "https://git.ihateamerica.ru/caps_enhancer/update.xml",
|
"update_url": "https://git.ihateamerica.ru/caps_enhancer/update.xml",
|
||||||
"icons": {
|
"icons": {
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,11 @@ let CONFIG = {
|
||||||
// Замена логотипа CleverPark на иконку CAPS (icons/caps.svg)
|
// Замена логотипа CleverPark на иконку CAPS (icons/caps.svg)
|
||||||
replaceLogo: true,
|
replaceLogo: true,
|
||||||
|
|
||||||
|
// Настройки видео-окна
|
||||||
|
videoWindow: {
|
||||||
|
ipButtonMode: 'button' // 'button' — отдельная кнопка с IP, 'clickable-title' — заголовок окна становится кликабельным
|
||||||
|
},
|
||||||
|
|
||||||
// Показывать ли улучшения расширения (кнопки, логотип)
|
// Показывать ли улучшения расширения (кнопки, логотип)
|
||||||
uiVisible: true
|
uiVisible: true
|
||||||
};
|
};
|
||||||
|
|
@ -123,6 +128,11 @@ function updateConfig(newConfig) {
|
||||||
CONFIG.replaceLogo = newConfig.replaceLogo;
|
CONFIG.replaceLogo = newConfig.replaceLogo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Обновляем videoWindow
|
||||||
|
if (newConfig.videoWindow) {
|
||||||
|
Object.assign(CONFIG.videoWindow, newConfig.videoWindow);
|
||||||
|
}
|
||||||
|
|
||||||
// Обновляем uiVisible
|
// Обновляем uiVisible
|
||||||
if (typeof newConfig.uiVisible === 'boolean') {
|
if (typeof newConfig.uiVisible === 'boolean') {
|
||||||
CONFIG.uiVisible = newConfig.uiVisible;
|
CONFIG.uiVisible = newConfig.uiVisible;
|
||||||
|
|
@ -252,9 +262,12 @@ function checkNoVNCPage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Признак 2: div с классом начинающимся на "app_AppWindow__"
|
// Признак 2: div с классом начинающимся на "app_AppWindow__"
|
||||||
const appWindow = document.querySelector('div[class*="app_AppWindow__"]');
|
// ИЛИ title страницы содержит "Платёжный терминал" (присутствует сразу при загрузке)
|
||||||
const hasAppWindow = !!appWindow;
|
const hasAppWindow = !!(
|
||||||
|
document.querySelector('div[class*="app_AppWindow__"]') ||
|
||||||
|
document.title.includes('Платёжный терминал')
|
||||||
|
);
|
||||||
|
|
||||||
// Страница noVNC определяется по наличию обоих признаков
|
// Страница noVNC определяется по наличию обоих признаков
|
||||||
return hasNoVNCNoscript && hasAppWindow;
|
return hasNoVNCNoscript && hasAppWindow;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,9 +52,9 @@
|
||||||
debugLog('Текущий хост:', currentHost);
|
debugLog('Текущий хост:', currentHost);
|
||||||
debugLog('Текущий порт:', currentPort);
|
debugLog('Текущий порт:', currentPort);
|
||||||
|
|
||||||
// Проверяем, что мы на 80 порту
|
// Проверяем, что мы не уже на целевом порту
|
||||||
if (currentPort !== '80' && currentPort !== '') {
|
if (currentPort === core.TARGET_PORT_VIDEO) {
|
||||||
debugLog('❌ Не на 80 порту, переадресация не нужна');
|
debugLog('❌ Уже на целевом порту, переадресация не нужна');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,6 +105,7 @@
|
||||||
);
|
);
|
||||||
|
|
||||||
button.setAttribute('data-caps-ui', 'true');
|
button.setAttribute('data-caps-ui', 'true');
|
||||||
|
button.dataset.capsUiDisplay = 'inline-block';
|
||||||
|
|
||||||
// Дополнительные стили для позиционирования
|
// Дополнительные стили для позиционирования
|
||||||
button.style.position = 'fixed';
|
button.style.position = 'fixed';
|
||||||
|
|
@ -117,8 +118,18 @@
|
||||||
|
|
||||||
// Добавляем кнопку на страницу
|
// Добавляем кнопку на страницу
|
||||||
document.body.appendChild(button);
|
document.body.appendChild(button);
|
||||||
|
|
||||||
debugLog('✅ Кнопка настроек добавлена');
|
debugLog('✅ Кнопка настроек добавлена');
|
||||||
|
|
||||||
|
// React-приложение может заменить содержимое body — следим за удалением кнопки
|
||||||
|
const observer = new MutationObserver(() => {
|
||||||
|
if (!document.querySelector('.novnc-settings-btn')) {
|
||||||
|
debugLog('⚠️ Кнопка удалена, добавляем снова');
|
||||||
|
document.body.appendChild(button);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
observer.observe(document.body, { childList: true, subtree: false });
|
||||||
|
setTimeout(() => observer.disconnect(), 15000);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,12 +66,12 @@
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Функция для добавления кнопки в заголовок
|
// Режим А: добавить отдельную кнопку рядом с заголовком
|
||||||
function addButtonToHeader(header, ip) {
|
function addButtonToHeader(header, ip) {
|
||||||
if (header.querySelector('.video-redirect-btn')) {
|
if (header.querySelector('.video-redirect-btn')) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const button = core.createStyledButton(
|
const button = core.createStyledButton(
|
||||||
`🔗 ${ip}:${core.TARGET_PORT_VIDEO}`,
|
`🔗 ${ip}:${core.TARGET_PORT_VIDEO}`,
|
||||||
(e) => {
|
(e) => {
|
||||||
|
|
@ -81,7 +81,7 @@
|
||||||
'video-redirect-btn'
|
'video-redirect-btn'
|
||||||
);
|
);
|
||||||
button.setAttribute('data-caps-ui', 'true');
|
button.setAttribute('data-caps-ui', 'true');
|
||||||
|
|
||||||
const p = header.querySelector('p');
|
const p = header.querySelector('p');
|
||||||
if (p) {
|
if (p) {
|
||||||
p.style.display = 'inline-block';
|
p.style.display = 'inline-block';
|
||||||
|
|
@ -90,7 +90,36 @@
|
||||||
} else {
|
} else {
|
||||||
header.appendChild(button);
|
header.appendChild(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Режим Б: кнопка «Лещ» вместо IP-адреса
|
||||||
|
function addLeshButtonToHeader(header, ip) {
|
||||||
|
if (header.querySelector('.video-redirect-btn')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const button = core.createStyledButton(
|
||||||
|
'Лещ',
|
||||||
|
(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
window.open(`http://${ip}:${core.TARGET_PORT_VIDEO}`, '_blank');
|
||||||
|
},
|
||||||
|
'video-redirect-btn'
|
||||||
|
);
|
||||||
|
button.setAttribute('data-caps-ui', 'true');
|
||||||
|
button.title = `http://${ip}:${core.TARGET_PORT_VIDEO}`;
|
||||||
|
|
||||||
|
const p = header.querySelector('p');
|
||||||
|
if (p) {
|
||||||
|
p.style.display = 'inline-block';
|
||||||
|
p.style.marginRight = '8px';
|
||||||
|
p.insertAdjacentElement('afterend', button);
|
||||||
|
} else {
|
||||||
|
header.appendChild(button);
|
||||||
|
}
|
||||||
|
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,8 +145,14 @@
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
addButtonToHeader(activeWindow.header, ipData.ip);
|
const mode = core.CONFIG.videoWindow?.ipButtonMode || 'button';
|
||||||
debugLog('✅ Кнопка добавлена');
|
if (mode === 'clickable-title') {
|
||||||
|
addLeshButtonToHeader(activeWindow.header, ipData.ip);
|
||||||
|
debugLog('✅ Кнопка «Лещ» добавлена');
|
||||||
|
} else {
|
||||||
|
addButtonToHeader(activeWindow.header, ipData.ip);
|
||||||
|
debugLog('✅ Кнопка добавлена');
|
||||||
|
}
|
||||||
debugLog('========== КОНЕЦ ==========');
|
debugLog('========== КОНЕЦ ==========');
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -127,8 +162,7 @@
|
||||||
window.VideoIPRedirect.VideoWindow = {
|
window.VideoIPRedirect.VideoWindow = {
|
||||||
addButtonToVideoWindow: addButtonToActiveWindow,
|
addButtonToVideoWindow: addButtonToActiveWindow,
|
||||||
removeButtonsFromWindow: function() {
|
removeButtonsFromWindow: function() {
|
||||||
const btns = document.querySelectorAll('.video-redirect-btn');
|
document.querySelectorAll('.video-redirect-btn').forEach(btn => btn.remove());
|
||||||
btns.forEach(btn => btn.remove());
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
15
options.html
15
options.html
|
|
@ -21,6 +21,21 @@
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="settings-grid">
|
<div class="settings-grid">
|
||||||
<div class="settings-col">
|
<div class="settings-col">
|
||||||
|
<!-- Настройки видео-окна -->
|
||||||
|
<section class="settings-section">
|
||||||
|
<h2 data-i18n="options.videoWindowTitle">🎬 Видео окна</h2>
|
||||||
|
<div class="setting-item">
|
||||||
|
<label for="video-ip-button-mode">
|
||||||
|
<span class="label-text" data-i18n="options.videoIpButtonModeLabel">Режим кнопки с IP-адресом</span>
|
||||||
|
<span class="label-description" data-i18n="options.videoIpButtonModeDesc">Как отображать ссылку на настройки устройства в заголовке видео-окна</span>
|
||||||
|
</label>
|
||||||
|
<select id="video-ip-button-mode">
|
||||||
|
<option value="button" data-i18n="options.videoIpButtonModeButton">Кнопка с IP-адресом (🔗 IP:порт)</option>
|
||||||
|
<option value="clickable-title" data-i18n="options.videoIpButtonModeClickable">Кнопка «Лещ» (без IP-адреса)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<!-- Настройки noVNC (страница экрана) -->
|
<!-- Настройки noVNC (страница экрана) -->
|
||||||
<section class="settings-section">
|
<section class="settings-section">
|
||||||
<h2 data-i18n="options.novncTitle">🖥️ Страница экрана</h2>
|
<h2 data-i18n="options.novncTitle">🖥️ Страница экрана</h2>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue