diff --git a/ui_server.py b/ui_server.py
index e1d8f8a..ad6a446 100644
--- a/ui_server.py
+++ b/ui_server.py
@@ -1039,6 +1039,15 @@ HTML = """
.netron-state { min-width: 0; color: #8e99a6; font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.netron-panel { min-height: 420px; margin-top: 8px; overflow: hidden; border: 1px solid #2b3037; background: #fff; }
.netron-frame { display: block; width: 100%; height: min(68vh, 720px); border: 0; background: #fff; }
+ .netron-tab { height: 100%; min-height: 0; overflow: auto; padding: 10px; }
+ .netron-page-panel { width: min(1400px, 100%) !important; min-height: 100%; margin: 0 auto; border: 1px solid var(--ui-border); background: var(--ui-surface); }
+ .netron-page-section { min-height: 100%; padding: 16px; border: 0; }
+ .netron-page-head { display: flex; align-items: end; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
+ .netron-page-head h2 { margin: 0; }
+ .netron-model-field { min-width: min(420px, 100%); display: grid; gap: 4px; color: var(--ui-muted); font-size: 12px; }
+ .netron-model-field select { min-width: 0; height: 34px; border: 1px solid var(--ui-border); border-radius: var(--ui-radius); background: var(--ui-input); color: var(--ui-text); padding: 0 10px; font: inherit; }
+ .netron-page-section .netron-panel { min-height: calc(100vh - 230px); }
+ .netron-page-section .netron-frame { height: min(74vh, 860px); }
.theme-select { width: 112px; height: 28px; border: 1px solid #2b3037; background: #101317; color: #dce3ea; padding: 0 5px; font: 11px Segoe UI, Arial, sans-serif; }
.control-form { display: grid; grid-template-columns: 1fr 1fr; align-items: start; }
.settings-group { min-width: 0; margin: 0; padding: 10px; border: 0; border-bottom: 1px solid #2b3037; display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
@@ -1212,7 +1221,7 @@ HTML = """
.theme-select, .accent-select, .accent-custom { border-radius: 8px; transition: border-color .2s ease, box-shadow .2s ease, transform .2s ease; }
.theme-select:hover, .accent-select:hover, .accent-custom:hover { border-color: var(--ui-accent) !important; transform: translateY(-1px); }
main { height: calc(100vh - 52px); }
- .stream-tab, .archive-tab, .settings-tab, .model-tab { padding: 10px; }
+ .stream-tab, .archive-tab, .settings-tab, .model-tab, .netron-tab { padding: 10px; }
.stage, .settings-panel, .model-panel, .archive-panel, .log-drawer, .player-box, .netron-panel { border-radius: var(--ui-radius); box-shadow: var(--ui-shadow); }
.stage { isolation: isolate; }
.stage::after { content: ""; position: absolute; inset: 0; pointer-events: none; border-radius: inherit; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ui-accent) 8%, transparent); }
@@ -1537,6 +1546,7 @@ HTML = """
+
+
+
+
+
+
+
+
+
+
Netron
+ Архитектура модели до инференса
+
+
+
@@ -3674,23 +3696,23 @@ HTML = """
}
async function loadModels() {
const data = await fetch('/api/models', {cache: 'no-store'}).then(r => r.json());
- const select = $('modelSelect');
- select.innerHTML = '';
- for (const model of data.models || []) {
+ const selects = [$('modelSelect'), $('netronModelSelect')].filter(Boolean);
+ selects.forEach(select => { select.innerHTML = ''; });
+ for (const model of data.models || []) selects.forEach(select => {
const option = document.createElement('option');
option.value = model.path;
option.textContent = `${model.name} · ${mb(model.size)}`;
select.appendChild(option);
- }
+ });
const selected = data.selected || data.default || '';
- if (!select.options.length) {
- select.innerHTML = '';
- } else if (selected) {
- select.value = selected;
- }
- if (select.value) {
- setValue('modelPath', select.value);
- setText('modelState', `${select.selectedOptions[0]?.textContent || 'модель выбрана'}`);
+ selects.forEach(select => {
+ if (!select.options.length) select.innerHTML = '';
+ else if (selected) select.value = selected;
+ });
+ const modelSelect = $('modelSelect');
+ if (modelSelect?.value) {
+ setValue('modelPath', modelSelect.value);
+ setText('modelState', `${modelSelect.selectedOptions[0]?.textContent || 'модель выбрана'}`);
}
}
function resetNetron() {
@@ -3699,7 +3721,7 @@ HTML = """
setText('netronState', 'граф Netron не загружен');
}
async function openNetron() {
- const path = $('modelSelect').value || $('modelPath').value;
+ const path = $('netronModelSelect')?.value || $('modelSelect').value || $('modelPath').value;
if (!path) throw new Error('выберите файл модели .pt');
setText('netronState', 'запуск Netron...');
const response = await fetch('/api/model/netron?path=' + enc(path), {cache: 'no-store'});
@@ -4078,12 +4100,13 @@ HTML = """
$('playerOverlay').hidden = true;
}
function switchTab(name, updateHash = true) {
- if (!['stream', 'archive', 'settings', 'model'].includes(name)) name = 'stream';
+ if (!['stream', 'archive', 'settings', 'model', 'netron'].includes(name)) name = 'stream';
document.querySelectorAll('.tab').forEach(btn => btn.classList.toggle('active', btn.dataset.tab === name));
$('streamTab').hidden = name !== 'stream';
$('archiveTab').hidden = name !== 'archive';
$('settingsTab').hidden = name !== 'settings';
$('modelTab').hidden = name !== 'model';
+ $('netronTab').hidden = name !== 'netron';
if (name === 'settings' && typeof applyStoredConfigLayout === 'function') requestAnimationFrame(applyStoredConfigLayout);
if (name === 'model' && typeof applyNetworkLayout === 'function') requestAnimationFrame(applyNetworkLayout);
if (name === 'archive') loadArchive();
@@ -4320,6 +4343,14 @@ HTML = """
$('decorIntensity').addEventListener('input', e => applyAppearanceSetting('decor', e.target.value));
document.querySelectorAll('[data-setting]').forEach(button => button.addEventListener('click', () => applyAppearanceSetting(button.dataset.setting, button.dataset.value)));
$('modelSelect').addEventListener('change', e => {
+ setValue('modelPath', e.target.value);
+ setValue('netronModelSelect', e.target.value);
+ setText('modelState', e.target.selectedOptions[0]?.textContent || 'модель выбрана');
+ resetNetron();
+ controlDirty = true;
+ });
+ $('netronModelSelect').addEventListener('change', e => {
+ setValue('modelSelect', e.target.value);
setValue('modelPath', e.target.value);
setText('modelState', e.target.selectedOptions[0]?.textContent || 'модель выбрана');
resetNetron();