@ -1449,9 +1449,9 @@ HTML = """<!doctype html>
body [ data - density = " spacious " ] . settings - group , body [ data - density = " spacious " ] . model - group { padding : 15 px 16 px ; gap : 10 px 12 px ; }
body [ data - density = " spacious " ] . settings - group , body [ data - density = " spacious " ] . model - group { padding : 15 px 16 px ; gap : 10 px 12 px ; }
body [ data - density = " spacious " ] . quickbar { gap : 10 px ; }
body [ data - density = " spacious " ] . quickbar { gap : 10 px ; }
body [ data - radius = " sharp " ] . stage , body [ data - radius = " sharp " ] . settings - panel , body [ data - radius = " sharp " ] . model - panel , body [ data - radius = " sharp " ] . archive - panel , body [ data - radius = " sharp " ] . log - drawer , body [ data - radius = " sharp " ] . player - box , body [ data - radius = " sharp " ] . netron - panel { border - radius : 5 px ; }
body [ data - radius = " sharp " ] . stage , body [ data - radius = " sharp " ] . settings - panel , body [ data - radius = " sharp " ] . model - panel , body [ data - radius = " sharp " ] . archive - panel , body [ data - radius = " sharp " ] . log - drawer , body [ data - radius = " sharp " ] . player - box , body [ data - radius = " sharp " ] . netron - panel { border - radius : 5 px ; }
body [ data - radius = " sharp " ] button , body [ data - radius = " sharp " ] select , body [ data - radius = " sharp " ] input , body [ data - radius = " sharp " ] textarea , body [ data - radius = " sharp " ] . config - block { border - radius : 4 px ! important ; }
body [ data - radius = " sharp " ] button , body [ data - radius = " sharp " ] select , body [ data - radius = " sharp " ] input , body [ data - radius = " sharp " ] textarea , body [ data - radius = " sharp " ] . config - block , body [ data - radius = " sharp " ] . network - block { border - radius : 4 px ! important ; }
body [ data - radius = " round " ] . stage , body [ data - radius = " round " ] . settings - panel , body [ data - radius = " round " ] . model - panel , body [ data - radius = " round " ] . archive - panel , body [ data - radius = " round " ] . log - drawer , body [ data - radius = " round " ] . player - box , body [ data - radius = " round " ] . netron - panel { border - radius : 20 px ; }
body [ data - radius = " round " ] . stage , body [ data - radius = " round " ] . settings - panel , body [ data - radius = " round " ] . model - panel , body [ data - radius = " round " ] . archive - panel , body [ data - radius = " round " ] . log - drawer , body [ data - radius = " round " ] . player - box , body [ data - radius = " round " ] . netron - panel { border - radius : 20 px ; }
body [ data - radius = " round " ] button , body [ data - radius = " round " ] select , body [ data - radius = " round " ] input , body [ data - radius = " round " ] textarea , body [ data - radius = " round " ] . config - block { border - radius : 14 px ! important ; }
body [ data - radius = " round " ] button , body [ data - radius = " round " ] select , body [ data - radius = " round " ] input , body [ data - radius = " round " ] textarea , body [ data - radius = " round " ] . config - block , body [ data - radius = " round " ] . network - block { border - radius : 14 px ! important ; }
body [ data - contrast = " soft " ] { - - ui - muted : var ( - - ui - muted - contrast ) ; - - ui - border : var ( - - ui - border - contrast ) ; }
body [ data - contrast = " soft " ] { - - ui - muted : var ( - - ui - muted - contrast ) ; - - ui - border : var ( - - ui - border - contrast ) ; }
body [ data - contrast = " high " ] { - - ui - muted : var ( - - ui - muted - contrast ) ; - - ui - border : var ( - - ui - border - contrast ) ; }
body [ data - contrast = " high " ] { - - ui - muted : var ( - - ui - muted - contrast ) ; - - ui - border : var ( - - ui - border - contrast ) ; }
body [ data - motion = " none " ] * , body [ data - motion = " none " ] * : : before , body [ data - motion = " none " ] * : : after { animation - duration : .01 ms ! important ; animation - iteration - count : 1 ! important ; transition - duration : .01 ms ! important ; scroll - behavior : auto ! important ; }
body [ data - motion = " none " ] * , body [ data - motion = " none " ] * : : before , body [ data - motion = " none " ] * : : after { animation - duration : .01 ms ! important ; animation - iteration - count : 1 ! important ; transition - duration : .01 ms ! important ; scroll - behavior : auto ! important ; }
@ -1476,13 +1476,25 @@ HTML = """<!doctype html>
. config - block . settings - group input : not ( [ type = " checkbox " ] ) : not ( [ type = " file " ] ) , . config - block . settings - group select , . config - block . settings - group button { min - height : calc ( 27 px * var ( - - block - scale ) ) ; height : calc ( 27 px * var ( - - block - scale ) ) ; padding - left : calc ( 7 px * var ( - - block - scale ) ) ; padding - right : calc ( 7 px * var ( - - block - scale ) ) ; font - size : clamp ( 8 px , calc ( 12 px * var ( - - block - scale ) ) , 26 px ) ; }
. config - block . settings - group input : not ( [ type = " checkbox " ] ) : not ( [ type = " file " ] ) , . config - block . settings - group select , . config - block . settings - group button { min - height : calc ( 27 px * var ( - - block - scale ) ) ; height : calc ( 27 px * var ( - - block - scale ) ) ; padding - left : calc ( 7 px * var ( - - block - scale ) ) ; padding - right : calc ( 7 px * var ( - - block - scale ) ) ; font - size : clamp ( 8 px , calc ( 12 px * var ( - - block - scale ) ) , 26 px ) ; }
. config - block . settings - group . control - fields , . config - block . settings - group . advanced - grid , . config - block . settings - group . packet - grid { gap : calc ( 6 px * var ( - - block - scale ) ) calc ( 8 px * var ( - - block - scale ) ) ; }
. config - block . settings - group . control - fields , . config - block . settings - group . advanced - grid , . config - block . settings - group . packet - grid { gap : calc ( 6 px * var ( - - block - scale ) ) calc ( 8 px * var ( - - block - scale ) ) ; }
. network - panel { overflow : hidden ; }
. network - panel { overflow : hidden ; }
. network - canvas { position : relative ; display : grid ; grid - template - columns : repeat ( 12 , minmax ( 0 , 1 fr ) ) ; gap : 14 px ; padding : 14 px ; background : radial - gradient ( circle at 10 % 0 % , color - mix ( in srgb , var ( - - ui - accent ) 7 % , transparent ) , transparent 32 % ) , color - mix ( in srgb , var ( - - ui - bg ) 74 % , var ( - - ui - panel ) ) ; }
. network - workspace { display : grid ; grid - template - columns : minmax ( 0 , 1 fr ) ; min - width : 0 ; }
. network - block { grid - column : span 6 ; min - width : 0 ; margin : 0 ; padding : 14 px ; border : 1 px solid var ( - - ui - border ) ! important ; border - radius : 14 px ! important ; background : color - mix ( in srgb , var ( - - ui - panel ) 94 % , var ( - - ui - accent ) ) ! important ; box - shadow : 0 8 px 22 px color - mix ( in srgb , var ( - - ui - bg ) 20 % , transparent ) ; overflow : hidden ; transition : border - color .2 s ease , box - shadow .2 s ease , transform .2 s ease ; }
. network - workspace . has - structure { grid - template - columns : minmax ( 190 px , 260 px ) minmax ( 0 , 1 fr ) ; align - items : start ; }
. network - workspace . has - structure > . network - restore - panel { grid - column : 1 ; border - right : 1 px solid var ( - - ui - border ) ; border - bottom : 0 ; }
. network - workspace . has - structure > . network - canvas { grid - column : 2 ; }
. network - restore - panel { min - height : 0 ; max - height : calc ( 100 vh - 128 px ) ; }
. network - canvas { position : relative ; display : block ; min - height : 720 px ; padding : 14 px ; overflow : auto ; background : radial - gradient ( circle at 10 % 0 % , color - mix ( in srgb , var ( - - ui - accent ) 7 % , transparent ) , transparent 32 % ) , color - mix ( in srgb , var ( - - ui - bg ) 74 % , var ( - - ui - panel ) ) ; }
. network - block { position : absolute ; min - width : 240 px ; min - height : 110 px ; margin : 0 ; padding : 14 px ; border : 1 px solid var ( - - ui - border ) ! important ; border - radius : 14 px ! important ; background : color - mix ( in srgb , var ( - - ui - panel ) 94 % , var ( - - ui - accent ) ) ! important ; box - shadow : 0 8 px 22 px color - mix ( in srgb , var ( - - ui - bg ) 20 % , transparent ) ; overflow : auto ; transition : border - color .2 s ease , box - shadow .2 s ease , transform .2 s ease ; cursor : grab ; touch - action : none ; }
. network - block : hover { border - color : color - mix ( in srgb , var ( - - ui - accent ) 48 % , var ( - - ui - border ) ) ! important ; transform : translateY ( - 2 px ) ; }
. network - block : hover { border - color : color - mix ( in srgb , var ( - - ui - accent ) 48 % , var ( - - ui - border ) ) ! important ; transform : translateY ( - 2 px ) ; }
. network - block > summary { grid - column : 1 / - 1 ; margin : - 2 px 0 5 px ; padding - bottom : 8 px ; border - bottom : 1 px solid color - mix ( in srgb , var ( - - ui - border ) 80 % , transparent ) ; }
. network - block . is - selected { border - color : color - mix ( in srgb , var ( - - ui - accent ) 70 % , var ( - - ui - border ) ) ! important ; box - shadow : 0 0 0 2 px color - mix ( in srgb , var ( - - ui - accent ) 12 % , transparent ) , 0 8 px 22 px color - mix ( in srgb , var ( - - ui - bg ) 20 % , transparent ) ; }
. network - block . is - dragging , . network - block . is - resizing { z - index : 5 ; border - color : var ( - - ui - accent ) ! important ; box - shadow : 0 10 px 28 px color - mix ( in srgb , var ( - - ui - accent ) 16 % , transparent ) ; transform : none ; transition : none ; user - select : none ; }
. network - block . is - deleted , . network - block . is - hidden { display : none ! important ; }
. network - summary { display : flex ; align - items : center ; gap : 6 px ; margin : - 2 px 0 8 px ; padding - bottom : 8 px ; border - bottom : 1 px solid color - mix ( in srgb , var ( - - ui - border ) 80 % , transparent ) ; color : var ( - - ui - text ) ; font - size : 12 px ; font - weight : 800 ; letter - spacing : .08 em ; text - transform : uppercase ; }
. network - summary : : before { content : " ✦ " ; color : var ( - - ui - accent ) ; font - size : 11 px ; }
. network - block . settings - group { - - block - scale : 1 ; padding : calc ( 14 px * var ( - - block - scale ) ) ; gap : calc ( 8 px * var ( - - block - scale ) ) ; }
. network - block . settings - group label , . network - block . settings - group . model - state { gap : calc ( 3 px * var ( - - block - scale ) ) ; font - size : clamp ( 8 px , calc ( 11 px * var ( - - block - scale ) ) , 22 px ) ; }
. network - block . settings - group input : not ( [ type = " checkbox " ] ) : not ( [ type = " file " ] ) , . network - block . settings - group select , . network - block . settings - group button { min - height : calc ( 28 px * var ( - - block - scale ) ) ; height : calc ( 28 px * var ( - - block - scale ) ) ; padding - left : calc ( 7 px * var ( - - block - scale ) ) ; padding - right : calc ( 7 px * var ( - - block - scale ) ) ; font - size : clamp ( 8 px , calc ( 12 px * var ( - - block - scale ) ) , 26 px ) ; }
. network - block . settings - group . network - summary { margin - bottom : calc ( 8 px * var ( - - block - scale ) ) ; padding - bottom : calc ( 8 px * var ( - - block - scale ) ) ; font - size : clamp ( 9 px , calc ( 12 px * var ( - - block - scale ) ) , 24 px ) ; }
. network - block . model - actions { padding - top : 3 px ; }
. network - block . model - actions { padding - top : 3 px ; }
. network - visual - card { margin : 0 14 px 14 px ; padding : 14 px ; border : 1 px solid var ( - - ui - border ) ; border - radius : 14 px ; background : color - mix ( in srgb , var ( - - ui - panel ) 94 % , var ( - - ui - accent ) ) ; box - shadow : 0 8 px 22 px color - mix ( in srgb , var ( - - ui - bg ) 20 % , transparent ) ; }
. network - visual - card { padding : 14 px ; }
. network - visual - card h2 { margin : 0 ; }
body [ data - card - style = " flat " ] . network - block , body [ data - card - style = " flat " ] . network - visual - card { box - shadow : none ; border - radius : 8 px ! important ; }
body [ data - card - style = " flat " ] . network - block , body [ data - card - style = " flat " ] . network - visual - card { box - shadow : none ; border - radius : 8 px ! important ; }
body [ data - card - style = " neon " ] . network - block , body [ data - card - style = " neon " ] . network - visual - card { border - color : color - mix ( in srgb , var ( - - ui - accent ) 48 % , var ( - - ui - border ) ) ! important ; box - shadow : 0 0 0 1 px color - mix ( in srgb , var ( - - ui - accent ) 12 % , transparent ) , 0 10 px 24 px color - mix ( in srgb , var ( - - ui - accent ) 10 % , transparent ) ; }
body [ data - card - style = " neon " ] . network - block , body [ data - card - style = " neon " ] . network - visual - card { border - color : color - mix ( in srgb , var ( - - ui - accent ) 48 % , var ( - - ui - border ) ) ! important ; box - shadow : 0 0 0 1 px color - mix ( in srgb , var ( - - ui - accent ) 12 % , transparent ) , 0 10 px 24 px color - mix ( in srgb , var ( - - ui - accent ) 10 % , transparent ) ; }
@keyframes appearance - in { from { opacity : 0 ; transform : translateY ( - 5 px ) scale ( .98 ) ; } to { opacity : 1 ; transform : none ; } }
@keyframes appearance - in { from { opacity : 0 ; transform : translateY ( - 5 px ) scale ( .98 ) ; } to { opacity : 1 ; transform : none ; } }
@ -1495,9 +1507,12 @@ HTML = """<!doctype html>
. appearance - trigger - chevron { display : none ; }
. appearance - trigger - chevron { display : none ; }
. appearance - panel { position : fixed ; top : 56 px ; right : 8 px ; width : min ( 390 px , calc ( 100 vw - 16 px ) ) ; }
. appearance - panel { position : fixed ; top : 56 px ; right : 8 px ; width : min ( 390 px , calc ( 100 vw - 16 px ) ) ; }
. appearance - options { grid - template - columns : repeat ( 3 , minmax ( 0 , 1 fr ) ) ; }
. appearance - options { grid - template - columns : repeat ( 3 , minmax ( 0 , 1 fr ) ) ; }
. network - canvas { grid - template - columns : 1 fr ; padding : 10 px ; gap : 10 px ; }
. network - workspace . has - structure { grid - template - columns : 1 fr ; }
. network - block { grid - column : 1 ; }
. network - workspace . has - structure > . network - restore - panel , . network - workspace . has - structure > . network - canvas { grid - column : 1 ; }
. network - visual - card { margin : 0 10 px 10 px ; padding : 10 px ; }
. network - workspace . has - structure > . network - restore - panel { border - right : 0 ; border - bottom : 1 px solid var ( - - ui - border ) ; max - height : 42 vh ; }
. network - canvas { min - height : 900 px ; padding : 10 px ; }
. network - block { min - width : 220 px ; max - width : calc ( 100 % - 8 px ) ; }
. network - visual - card { padding : 10 px ; }
}
}
@media ( max - width : 420 px ) { . appearance - options { grid - template - columns : repeat ( 2 , minmax ( 0 , 1 fr ) ) ; } }
@media ( max - width : 420 px ) { . appearance - options { grid - template - columns : repeat ( 2 , minmax ( 0 , 1 fr ) ) ; } }
< / style >
< / style >
@ -1808,9 +1823,21 @@ HTML = """<!doctype html>
< / div >
< / div >
< div id = " modelTab " class = " tab-page model-tab " hidden >
< div id = " modelTab " class = " tab-page model-tab " hidden >
< section class = " model-panel network-panel " >
< section class = " model-panel network-panel " >
< div class = " model-grid network-canvas " >
< div class = " config-toolbar network-toolbar " >
< details class = " model-group settings-group network-block " data - section = " model " open >
< span class = " config-toolbar-title " > Блоки сети < / span >
< summary > Модель < / summary >
< button id = " networkResetLayout " type = " button " > сбросить раскладку < / button >
< button id = " networkStructureToggle " type = " button " aria - expanded = " false " > структура < / button >
< label class = " config-scale-control " title = " Масштаб элементов и текста выбранного сетевого блока " > < span > масштаб блока < / span > < input id = " networkContentScale " type = " range " min = " 50 " max = " 200 " step = " 5 " value = " 100 " aria - label = " Масштаб содержимого выбранного сетевого блока " disabled > < output id = " networkContentScaleValue " > 100 % < / output > < / label >
< span id = " networkLayoutState " class = " config-layout-state " > перетаскивайте блоки за свободное место < / span >
< / div >
< div id = " networkWorkspace " class = " network-workspace " >
< aside id = " networkRestorePanel " class = " config-restore-panel network-restore-panel " hidden >
< span class = " config-restore-title " > Структура сети < / span >
< div id = " networkRestoreList " class = " config-restore-list " > < / div >
< / aside >
< div id = " networkCanvas " class = " model-grid network-canvas " >
< section class = " model-group settings-group network-block " data - section = " model " >
< div class = " network-summary " > Модель < / div >
< label class = " model-wide " > Файл весов
< label class = " model-wide " > Файл весов
< select id = " modelSelect " > < option value = " " > загрузка списка . . . < / option > < / select >
< select id = " modelSelect " > < option value = " " > загрузка списка . . . < / option > < / select >
< / label >
< / label >
@ -1821,9 +1848,9 @@ HTML = """<!doctype html>
< span id = " modelState " class = " model-state " > модель не выбрана < / span >
< span id = " modelState " class = " model-state " > модель не выбрана < / span >
< / div >
< / div >
< label class = " model-wide " > Путь < input id = " modelPath " type = " text " readonly > < / label >
< label class = " model-wide " > Путь < input id = " modelPath " type = " text " readonly > < / label >
< / details >
< / section >
< details class = " model-group settings-group network-block " data - section = " model-params " open >
< section class = " model-group settings-group network-block " data - section = " model-params " >
< summary> Параметры до инференса < / summary >
< div class = " network- summary" > Параметры до инференса < / div >
< label > Устройство
< label > Устройство
< select id = " modelDevice " >
< select id = " modelDevice " >
< option value = " -1 " > CPU < / option >
< option value = " -1 " > CPU < / option >
@ -1837,10 +1864,9 @@ HTML = """<!doctype html>
< label > Размер полного кадра < input id = " modelFull " type = " number " min = " 128 " max = " 4096 " step = " 32 " value = " 1280 " > < / label >
< label > Размер полного кадра < input id = " modelFull " type = " number " min = " 128 " max = " 4096 " step = " 32 " value = " 1280 " > < / label >
< label > Макс . детекций < input id = " modelMaxDet " type = " number " min = " 1 " max = " 300 " step = " 1 " value = " 60 " > < / label >
< label > Макс . детекций < input id = " modelMaxDet " type = " number " min = " 1 " max = " 300 " step = " 1 " value = " 60 " > < / label >
< span class = " model-state model-wide " > Параметры применяются при следующем старте . < / span >
< span class = " model-state model-wide " > Параметры применяются при следующем старте . < / span >
< / details >
< / section >
< / div >
< section class = " netron-section network-visual-card network-block " data - section = " netron " >
< section class = " netron-section network-visual-card " >
< div class = " network-summary " > Визуализация сети < / div >
< h2 > Визуализация сети < / h2 >
< div class = " netron-toolbar " >
< div class = " netron-toolbar " >
< button id = " modelNetron " class = " command primary " type = " button " > Показать в Netron < / button >
< button id = " modelNetron " class = " command primary " type = " button " > Показать в Netron < / button >
< span id = " netronState " class = " netron-state " > граф Netron не загружен < / span >
< span id = " netronState " class = " netron-state " > граф Netron не загружен < / span >
@ -1849,6 +1875,12 @@ HTML = """<!doctype html>
< iframe id = " netronFrame " class = " netron-frame " title = " Визуализация архитектуры сети в Netron " loading = " lazy " sandbox = " allow-scripts allow-same-origin " > < / iframe >
< iframe id = " netronFrame " class = " netron-frame " title = " Визуализация архитектуры сети в Netron " loading = " lazy " sandbox = " allow-scripts allow-same-origin " > < / iframe >
< / div >
< / div >
< / section >
< / section >
< / div >
< / div >
< div id = " networkTrash " class = " config-trash network-trash " role = " button " tabindex = " 0 " aria - label = " Перетащите сетевой блок сюда для удаления " hidden >
< span class = " config-trash-icon " aria - hidden = " true " > ⌫ < / span >
< span > отпустите здесь , чтобы удалить блок < / span >
< / div >
< / section >
< / section >
< / div >
< / div >
< div id = " archiveTab " class = " tab-page archive-tab " hidden >
< div id = " archiveTab " class = " tab-page archive-tab " hidden >
@ -2674,6 +2706,380 @@ HTML = """<!doctype html>
selectConfigBlock ( selectedConfigBlock ) ;
selectConfigBlock ( selectedConfigBlock ) ;
$ ( ' configContentScale ' ) ? . addEventListener ( ' input ' , event = > applyConfigContentScale ( event . target . value ) ) ;
$ ( ' configContentScale ' ) ? . addEventListener ( ' input ' , event = > applyConfigContentScale ( event . target . value ) ) ;
}
}
let networkCanvas = null ;
let networkBlocks = [ ] ;
let networkLayout = { } ;
let networkZIndex = 1 ;
let networkSelectedBlock = null ;
let networkStructureOpen = false ;
let networkContentScale = 1 ;
function readNetworkLayout ( ) {
try {
const saved = JSON . parse ( localStorage . getItem ( ' fpv-network-layout-v1 ' ) | | ' {} ' ) ;
return saved & & typeof saved == = ' object ' ? saved : { } ;
} catch ( _error ) { return { } ; }
}
function saveNetworkLayout ( ) {
try {
localStorage . setItem ( ' fpv-network-layout-v1 ' , JSON . stringify ( networkLayout ) ) ;
if ( networkCanvas ) localStorage . setItem ( ' fpv-network-canvas-width ' , String ( Math . round ( networkCanvasWidth ( ) ) ) ) ;
} catch ( _error ) { }
}
function networkMinWidth ( ) { return window . innerWidth < = 760 ? 220 : 240 ; }
function networkCanvasWidth ( ) { return Math . max ( networkMinWidth ( ) , ( networkCanvas ? . clientWidth | | 0 ) - 28 ) ; }
function networkState ( block ) { return block ? . dataset . blockState | | ' ' ; }
function networkTitle ( block ) { return block ? . querySelector ( ' :scope > .network-summary ' ) ? . textContent ? . trim ( ) | | block ? . dataset . section | | ' Блок ' ; }
function updateNetworkBlockScale ( block ) {
if ( ! block ) return ;
const width = Math . max ( networkMinWidth ( ) , block . clientWidth | | block . offsetWidth | | networkMinWidth ( ) ) ;
const height = Math . max ( 110 , block . clientHeight | | block . offsetHeight | | 110 ) ;
const area = Math . max ( .56 , Math . min ( 1.85 , ( width / 720 ) * ( height / 320 ) ) ) ;
const entry = networkLayout [ block . dataset . section ] | | { } ;
const userScale = Number . isFinite ( Number ( block . _networkUserScale ) ) ? block . _networkUserScale : ( Number ( entry . scale ) | | 100 ) / 100 ;
block . _networkUserScale = Math . max ( .5 , Math . min ( 2 , userScale ) ) ;
block . style . setProperty ( ' --block-scale ' , Math . max ( .5 , Math . min ( 2 , Math . sqrt ( area ) * block . _networkUserScale ) ) . toFixed ( 3 ) ) ;
}
function selectNetworkBlock ( block ) {
if ( ! block | | networkState ( block ) ) {
networkSelectedBlock = null ;
networkBlocks . forEach ( item = > item . classList . remove ( ' is-selected ' ) ) ;
const control = $ ( ' networkContentScale ' ) ;
if ( control ) control . disabled = true ;
return ;
}
networkSelectedBlock = block ;
networkBlocks . forEach ( item = > item . classList . toggle ( ' is-selected ' , item == = block ) ) ;
const value = Math . max ( 50 , Math . min ( 200 , Math . round ( Number ( networkLayout [ block . dataset . section ] ? . scale ) | | 100 ) ) ) ;
networkContentScale = value / 100 ;
setValue ( ' networkContentScale ' , value ) ;
setText ( ' networkContentScaleValue ' , ` $ { value } % ` ) ;
const control = $ ( ' networkContentScale ' ) ;
if ( control ) control . disabled = false ;
updateNetworkBlockScale ( block ) ;
}
function applyNetworkContentScale ( value ) {
networkContentScale = Math . max ( .5 , Math . min ( 2 , ( Number ( value ) | | 100 ) / 100 ) ) ;
const block = networkSelectedBlock ;
if ( ! block | | networkState ( block ) ) return ;
block . _networkUserScale = networkContentScale ;
const entry = networkLayout [ block . dataset . section ] | | { } ;
entry . scale = Math . round ( networkContentScale * 100 ) ;
networkLayout [ block . dataset . section ] = entry ;
setText ( ' networkContentScaleValue ' , ` $ { Math . round ( networkContentScale * 100 ) } % ` ) ;
updateNetworkBlockScale ( block ) ;
saveNetworkLayout ( ) ;
}
function captureNetworkLayout ( block ) {
const key = block ? . dataset . section ;
if ( ! key ) return ;
const entry = networkLayout [ key ] | | { } ;
entry . left = Math . round ( parseFloat ( block . style . left ) | | block . offsetLeft | | 0 ) ;
entry . top = Math . round ( parseFloat ( block . style . top ) | | block . offsetTop | | 0 ) ;
entry . width = Math . round ( block . offsetWidth ) ;
if ( block . style . height & & block . style . height != = ' auto ' ) entry . height = Math . round ( block . offsetHeight ) ;
else delete entry . height ;
if ( Number . isFinite ( Number ( block . _networkUserScale ) ) & & Math . abs ( block . _networkUserScale - 1 ) > .001 ) entry . scale = Math . round ( block . _networkUserScale * 100 ) ;
else delete entry . scale ;
if ( block . style . zIndex ) entry . z = Number ( block . style . zIndex ) ;
networkLayout [ key ] = entry ;
}
function placeNetworkBlock ( block , left , top , width ) {
if ( ! block ) return ;
const canvasWidth = networkCanvasWidth ( ) ;
const minWidth = networkMinWidth ( ) ;
const safeLeft = Math . max ( 0 , Math . round ( Math . min ( left , Math . max ( 0 , canvasWidth - minWidth ) ) ) ) ;
const safeWidth = Math . max ( minWidth , Math . min ( canvasWidth - safeLeft , Math . round ( width ) ) ) ;
block . style . left = ` $ { safeLeft } px ` ;
block . style . top = ` $ { Math . max ( 0 , Math . round ( top ) ) } px ` ;
block . style . width = ` $ { safeWidth } px ` ;
}
function updateNetworkCanvasHeight ( ) {
if ( ! networkCanvas ) return ;
const bottom = networkBlocks . reduce ( ( value , block ) = > networkState ( block ) ? value : Math . max ( value , ( block . offsetTop | | 0 ) + block . offsetHeight ) , 0 ) ;
networkCanvas . style . minHeight = ` $ { Math . max ( window . innerWidth < = 760 ? 900 : 720 , bottom + 28 ) } px ` ;
}
function applyNetworkLayout ( ) {
if ( ! networkCanvas ) return ;
const width = networkCanvasWidth ( ) ;
const gap = 14 ;
const visible = block = > block & & ! networkState ( block ) ;
const model = networkBlocks . find ( block = > block . dataset . section == = ' model ' & & visible ( block ) ) ;
const params = networkBlocks . find ( block = > block . dataset . section == = ' model-params ' & & visible ( block ) ) ;
const netron = networkBlocks . find ( block = > block . dataset . section == = ' netron ' & & visible ( block ) ) ;
const placeDefault = ( block , left , top , blockWidth ) = > {
if ( ! block ) return ;
const entry = networkLayout [ block . dataset . section ] | | { } ;
if ( ! Number . isFinite ( Number ( entry . left ) ) | | ! Number . isFinite ( Number ( entry . top ) ) ) placeNetworkBlock ( block , left , top , blockWidth ) ;
} ;
const half = Math . max ( networkMinWidth ( ) , ( width - gap ) / 2 ) ;
placeDefault ( model , 0 , 14 , half ) ;
placeDefault ( params , half + gap , 14 , half ) ;
const rowHeight = Math . max ( model ? . offsetHeight | | 110 , params ? . offsetHeight | | 110 ) ;
placeDefault ( netron , 0 , rowHeight + 28 , width ) ;
networkBlocks . forEach ( block = > {
const entry = networkLayout [ block . dataset . section ] ;
if ( ! entry | | networkState ( block ) ) return ;
const left = Number . isFinite ( Number ( entry . left ) ) ? Math . max ( 0 , Math . min ( width - networkMinWidth ( ) , Number ( entry . left ) ) ) : block . offsetLeft ;
block . style . left = ` $ { Math . round ( left ) } px ` ;
if ( Number . isFinite ( Number ( entry . top ) ) ) block . style . top = ` $ { Math . max ( 0 , Math . round ( Number ( entry . top ) ) ) } px ` ;
if ( Number . isFinite ( Number ( entry . width ) ) ) block . style . width = ` $ { Math . max ( networkMinWidth ( ) , Math . min ( width - left , Number ( entry . width ) ) ) } px ` ;
if ( Number . isFinite ( Number ( entry . height ) ) ) block . style . height = ` $ { Math . max ( 110 , Number ( entry . height ) ) } px ` ;
if ( Number . isFinite ( Number ( entry . z ) ) ) { block . style . zIndex = String ( Math . max ( 1 , Number ( entry . z ) ) ) ; networkZIndex = Math . max ( networkZIndex , Number ( entry . z ) ) ; }
updateNetworkBlockScale ( block ) ;
} ) ;
updateNetworkCanvasHeight ( ) ;
}
function bringNetworkBlockFront ( block ) {
if ( ! block | | networkState ( block ) ) return ;
block . style . zIndex = String ( + + networkZIndex ) ;
captureNetworkLayout ( block ) ;
saveNetworkLayout ( ) ;
}
function renderNetworkStructure ( ) {
const panel = $ ( ' networkRestorePanel ' ) ;
const list = $ ( ' networkRestoreList ' ) ;
const toggle = $ ( ' networkStructureToggle ' ) ;
const workspace = $ ( ' networkWorkspace ' ) ;
if ( ! panel | | ! list | | ! toggle ) return ;
list . replaceChildren ( ) ;
networkBlocks . forEach ( block = > {
const state = networkState ( block ) ;
const item = document . createElement ( ' div ' ) ;
item . className = ` config - restore - item $ { state ? ' is-hidden ' : ' ' } ` ;
item . title = ' Нажмите, чтобы выбрать блок ' ;
const label = document . createElement ( ' span ' ) ;
label . className = ' config-item-title ' ;
label . textContent = networkTitle ( block ) ;
const stateNode = document . createElement ( ' span ' ) ;
stateNode . className = ' config-item-state ' ;
stateNode . textContent = state == = ' deleted ' ? ' в корзине ' : state == = ' hidden ' ? ' скрыт ' : ' активен ' ;
const actions = document . createElement ( ' span ' ) ;
actions . className = ' config-item-actions ' ;
const visibility = document . createElement ( ' button ' ) ;
visibility . type = ' button ' ;
visibility . textContent = state ? ' вернуть ' : ' скрыть ' ;
visibility . addEventListener ( ' click ' , event = > {
event . stopPropagation ( ) ;
setNetworkBlockState ( block , state ? ' ' : ' hidden ' ) ;
} ) ;
actions . appendChild ( visibility ) ;
item . append ( label , stateNode , actions ) ;
item . addEventListener ( ' click ' , ( ) = > {
if ( ! state ) selectNetworkBlock ( block ) ;
item . classList . add ( ' is-selected ' ) ;
bringNetworkBlockFront ( block ) ;
} ) ;
list . appendChild ( item ) ;
} ) ;
const hidden = networkBlocks . filter ( block = > networkState ( block ) == = ' hidden ' ) . length ;
const deleted = networkBlocks . filter ( block = > networkState ( block ) == = ' deleted ' ) . length ;
toggle . textContent = hidden | | deleted ? ` структура · $ { hidden + deleted } ` : ' структура ' ;
toggle . setAttribute ( ' aria-expanded ' , String ( networkStructureOpen ) ) ;
panel . hidden = ! networkStructureOpen ;
workspace ? . classList . toggle ( ' has-structure ' , networkStructureOpen ) ;
setText ( ' networkLayoutState ' , ` $ { networkBlocks . length - hidden - deleted } блоков · $ { hidden } скрыто · $ { deleted } в корзине ` ) ;
}
function setNetworkBlockState ( block , state ) {
if ( ! block ) return ;
const key = block . dataset . section ;
if ( key & & state ) networkLayout [ key ] = { . . . ( networkLayout [ key ] | | { } ) , state } ;
else if ( key & & networkLayout [ key ] ) {
delete networkLayout [ key ] . state ;
if ( ! Object . keys ( networkLayout [ key ] ) . length ) delete networkLayout [ key ] ;
}
block . dataset . blockState = state | | ' ' ;
block . classList . toggle ( ' is-hidden ' , state == = ' hidden ' ) ;
block . classList . toggle ( ' is-deleted ' , state == = ' deleted ' ) ;
if ( state ) block . setAttribute ( ' aria-hidden ' , ' true ' ) ;
else block . removeAttribute ( ' aria-hidden ' ) ;
saveNetworkLayout ( ) ;
applyNetworkLayout ( ) ;
renderNetworkStructure ( ) ;
selectNetworkBlock ( networkBlocks . find ( candidate = > ! networkState ( candidate ) ) | | null ) ;
}
function networkResizeEdge ( event , block ) {
const rect = block . getBoundingClientRect ( ) ;
const edge = 9 ;
const left = event . clientX - rect . left < = edge ;
const right = rect . right - event . clientX < = edge ;
const top = event . clientY - rect . top < = edge ;
const bottom = rect . bottom - event . clientY < = edge ;
return ` $ { top ? ' n ' : bottom ? ' s ' : ' ' } $ { left ? ' w ' : right ? ' e ' : ' ' } ` ;
}
function networkDragBlocked ( block , event ) {
const target = event . target ;
if ( ! ( target instanceof Element ) ) return true ;
return ! ! target . closest ( ' input, select, textarea, button, a, iframe, [contenteditable= " true " ], .netron-panel ' ) ;
}
function networkTrashDrop ( event ) {
const trash = $ ( ' networkTrash ' ) ;
if ( ! trash | | trash . hidden | | ! event ) return false ;
const rect = trash . getBoundingClientRect ( ) ;
return event . clientX > = rect . left & & event . clientX < = rect . right & & event . clientY > = rect . top & & event . clientY < = rect . bottom ;
}
function showNetworkTrash ( ) {
const trash = $ ( ' networkTrash ' ) ;
if ( trash ) { trash . hidden = false ; trash . classList . remove ( ' is-over ' ) ; }
}
function finishNetworkDrag ( block , event , dragged ) {
const trash = $ ( ' networkTrash ' ) ;
const droppedOnTrash = networkTrashDrop ( event ) ;
if ( trash ) { trash . classList . remove ( ' is-over ' ) ; trash . hidden = true ; }
if ( ! dragged ) return ;
captureNetworkLayout ( block ) ;
if ( droppedOnTrash ) setNetworkBlockState ( block , ' deleted ' ) ;
else { saveNetworkLayout ( ) ; renderNetworkStructure ( ) ; }
block . _networkSuppressClick = true ;
window . setTimeout ( ( ) = > { block . _networkSuppressClick = false ; } , 0 ) ;
}
function startNetworkDrag ( block , event ) {
if ( event . button != = 0 | | ! networkCanvas | | networkDragBlocked ( block , event ) ) return ;
const startX = event . clientX ;
const startY = event . clientY ;
const startLeft = parseFloat ( block . style . left ) | | block . offsetLeft | | 0 ;
const startTop = parseFloat ( block . style . top ) | | block . offsetTop | | 0 ;
let dragged = false ;
showNetworkTrash ( ) ;
block . setPointerCapture ? . ( event . pointerId ) ;
const move = moveEvent = > {
if ( ! dragged & & Math . hypot ( moveEvent . clientX - startX , moveEvent . clientY - startY ) < 4 ) return ;
if ( ! dragged ) { dragged = true ; moveEvent . preventDefault ( ) ; block . classList . add ( ' is-dragging ' ) ; }
moveEvent . preventDefault ( ) ;
const maxLeft = Math . max ( 0 , networkCanvasWidth ( ) - block . offsetWidth ) ;
block . style . left = ` $ { Math . max ( 0 , Math . min ( maxLeft , Math . round ( startLeft + moveEvent . clientX - startX ) ) ) } px ` ;
block . style . top = ` $ { Math . max ( 0 , Math . round ( startTop + moveEvent . clientY - startY ) ) } px ` ;
const trash = $ ( ' networkTrash ' ) ;
if ( trash ) trash . classList . toggle ( ' is-over ' , networkTrashDrop ( moveEvent ) ) ;
updateNetworkCanvasHeight ( ) ;
} ;
const end = endEvent = > {
block . releasePointerCapture ? . ( event . pointerId ) ;
block . removeEventListener ( ' pointermove ' , move ) ;
block . removeEventListener ( ' pointerup ' , end ) ;
block . removeEventListener ( ' pointercancel ' , end ) ;
block . classList . remove ( ' is-dragging ' ) ;
finishNetworkDrag ( block , endEvent , dragged ) ;
} ;
block . addEventListener ( ' pointermove ' , move ) ;
block . addEventListener ( ' pointerup ' , end , { once : true } ) ;
block . addEventListener ( ' pointercancel ' , end , { once : true } ) ;
}
function makeNetworkBlockInteractive ( block ) {
if ( ! block | | block . _networkInteractive ) return ;
block . _networkInteractive = true ;
block . addEventListener ( ' pointerdown ' , event = > {
selectNetworkBlock ( block ) ;
bringNetworkBlockFront ( block ) ;
const edge = networkResizeEdge ( event , block ) ;
if ( ! edge | | networkDragBlocked ( block , event ) ) return ;
event . preventDefault ( ) ;
event . stopPropagation ( ) ;
const startX = event . clientX ;
const startY = event . clientY ;
const startLeft = parseFloat ( block . style . left ) | | block . offsetLeft | | 0 ;
const startTop = parseFloat ( block . style . top ) | | block . offsetTop | | 0 ;
const startWidth = block . offsetWidth ;
const startHeight = block . offsetHeight ;
block . classList . add ( ' is-resizing ' ) ;
block . setPointerCapture ? . ( event . pointerId ) ;
const move = moveEvent = > {
const dx = moveEvent . clientX - startX ;
const dy = moveEvent . clientY - startY ;
const minWidth = networkMinWidth ( ) ;
const canvasWidth = networkCanvasWidth ( ) ;
let left = startLeft ;
let top = startTop ;
let width = startWidth ;
let height = startHeight ;
if ( edge . includes ( ' w ' ) ) { left = Math . max ( 0 , Math . min ( startLeft + dx , startLeft + startWidth - minWidth ) ) ; width = startWidth - ( left - startLeft ) ; }
if ( edge . includes ( ' e ' ) ) width = Math . max ( minWidth , Math . min ( canvasWidth - left , startWidth + dx ) ) ;
if ( edge . includes ( ' n ' ) ) { top = Math . max ( 0 , Math . min ( startTop + dy , startTop + startHeight - 110 ) ) ; height = startHeight - ( top - startTop ) ; }
if ( edge . includes ( ' s ' ) ) height = Math . max ( 110 , startHeight + dy ) ;
block . style . left = ` $ { Math . round ( left ) } px ` ;
block . style . top = ` $ { Math . round ( top ) } px ` ;
block . style . width = ` $ { Math . round ( width ) } px ` ;
block . style . height = ` $ { Math . round ( height ) } px ` ;
updateNetworkBlockScale ( block ) ;
updateNetworkCanvasHeight ( ) ;
} ;
const end = ( ) = > {
block . releasePointerCapture ? . ( event . pointerId ) ;
block . removeEventListener ( ' pointermove ' , move ) ;
block . removeEventListener ( ' pointerup ' , end ) ;
block . removeEventListener ( ' pointercancel ' , end ) ;
block . classList . remove ( ' is-resizing ' ) ;
captureNetworkLayout ( block ) ;
saveNetworkLayout ( ) ;
block . _networkSuppressClick = true ;
window . setTimeout ( ( ) = > { block . _networkSuppressClick = false ; } , 0 ) ;
} ;
block . addEventListener ( ' pointermove ' , move ) ;
block . addEventListener ( ' pointerup ' , end , { once : true } ) ;
block . addEventListener ( ' pointercancel ' , end , { once : true } ) ;
} ) ;
block . addEventListener ( ' pointermove ' , event = > {
if ( ! block . classList . contains ( ' is-resizing ' ) ) {
const edge = networkResizeEdge ( event , block ) ;
block . style . cursor = edge ? ` $ { edge } - resize ` : ' ' ;
}
} ) ;
block . addEventListener ( ' pointerdown ' , event = > {
if ( event . defaultPrevented | | block . classList . contains ( ' is-resizing ' ) ) return ;
startNetworkDrag ( block , event ) ;
} ) ;
}
function resetNetworkLayout ( ) {
networkLayout = { } ;
networkZIndex = 1 ;
networkBlocks . forEach ( block = > {
block . style . left = ' ' ;
block . style . top = ' ' ;
block . style . width = ' ' ;
block . style . height = ' ' ;
block . style . zIndex = ' ' ;
block . _networkUserScale = 1 ;
block . dataset . blockState = ' ' ;
block . classList . remove ( ' is-hidden ' , ' is-deleted ' ) ;
block . removeAttribute ( ' aria-hidden ' ) ;
} ) ;
saveNetworkLayout ( ) ;
applyNetworkLayout ( ) ;
renderNetworkStructure ( ) ;
selectNetworkBlock ( networkBlocks [ 0 ] | | null ) ;
setText ( ' networkLayoutState ' , ' раскладка сети сброшена ' ) ;
}
function initNetworkCanvas ( ) {
networkCanvas = $ ( ' networkCanvas ' ) ;
if ( ! networkCanvas ) return ;
networkLayout = readNetworkLayout ( ) ;
networkBlocks = Array . from ( networkCanvas . children ) . filter ( node = > node . classList . contains ( ' network-block ' ) ) ;
networkBlocks . forEach ( block = > {
makeNetworkBlockInteractive ( block ) ;
const state = networkLayout [ block . dataset . section ] ? . state ;
if ( state ) {
block . dataset . blockState = state ;
block . classList . add ( state == = ' deleted ' ? ' is-deleted ' : ' is-hidden ' ) ;
block . setAttribute ( ' aria-hidden ' , ' true ' ) ;
}
} ) ;
applyNetworkLayout ( ) ;
renderNetworkStructure ( ) ;
selectNetworkBlock ( networkBlocks . find ( block = > ! networkState ( block ) ) | | null ) ;
$ ( ' networkResetLayout ' ) ? . addEventListener ( ' click ' , resetNetworkLayout ) ;
$ ( ' networkStructureToggle ' ) ? . addEventListener ( ' click ' , ( ) = > {
networkStructureOpen = ! networkStructureOpen ;
renderNetworkStructure ( ) ;
} ) ;
$ ( ' networkContentScale ' ) ? . addEventListener ( ' input ' , event = > applyNetworkContentScale ( event . target . value ) ) ;
window . addEventListener ( ' resize ' , applyNetworkLayout ) ;
if ( window . ResizeObserver ) {
const observer = new ResizeObserver ( entries = > {
entries . forEach ( entry = > updateNetworkBlockScale ( entry . target ) ) ;
updateNetworkCanvasHeight ( ) ;
} ) ;
networkBlocks . forEach ( block = > observer . observe ( block ) ) ;
}
}
function parseFrameSize ( value ) {
function parseFrameSize ( value ) {
const match = / ^ ( \\d { 1 , 5 } ) x ( \\d { 1 , 5 } ) $ / i . exec ( String ( value | | ' ' ) . trim ( ) ) ;
const match = / ^ ( \\d { 1 , 5 } ) x ( \\d { 1 , 5 } ) $ / i . exec ( String ( value | | ' ' ) . trim ( ) ) ;
return match ? [ Number ( match [ 1 ] ) , Number ( match [ 2 ] ) ] : [ 1280 , 720 ] ;
return match ? [ Number ( match [ 1 ] ) , Number ( match [ 2 ] ) ] : [ 1280 , 720 ] ;
@ -3568,6 +3974,7 @@ HTML = """<!doctype html>
$ ( ' settingsTab ' ) . hidden = name != = ' settings ' ;
$ ( ' settingsTab ' ) . hidden = name != = ' settings ' ;
$ ( ' modelTab ' ) . hidden = name != = ' model ' ;
$ ( ' modelTab ' ) . hidden = name != = ' model ' ;
if ( name == = ' settings ' & & typeof applyStoredConfigLayout == = ' function ' ) requestAnimationFrame ( applyStoredConfigLayout ) ;
if ( name == = ' settings ' & & typeof applyStoredConfigLayout == = ' function ' ) requestAnimationFrame ( applyStoredConfigLayout ) ;
if ( name == = ' model ' & & typeof applyNetworkLayout == = ' function ' ) requestAnimationFrame ( applyNetworkLayout ) ;
if ( name == = ' archive ' ) loadArchive ( ) ;
if ( name == = ' archive ' ) loadArchive ( ) ;
if ( updateHash & & location . hash != = ` #${name}`) history.replaceState(null, '', `#${name}`);
if ( updateHash & & location . hash != = ` #${name}`) history.replaceState(null, '', `#${name}`);
}
}
@ -4030,6 +4437,7 @@ HTML = """<!doctype html>
restoreAppearanceSettings ( ) ;
restoreAppearanceSettings ( ) ;
initCollapsibleSections ( ) ;
initCollapsibleSections ( ) ;
initConfigCanvas ( ) ;
initConfigCanvas ( ) ;
initNetworkCanvas ( ) ;
loadArchive ( ) ;
loadArchive ( ) ;
switchTab ( location . hash . slice ( 1 ) | | ' stream ' , false ) ;
switchTab ( location . hash . slice ( 1 ) | | ' stream ' , false ) ;
< / script >
< / script >