forked from martino/marte-debug
Added go webui for minimal c dependencies
This commit is contained in:
1 parent
242b1c4406
commit
d967098ead
10 files changed
+2379
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,13 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<rect width="32" height="32" rx="5" fill="#1e1e2e"/>
|
||||
<!-- oscilloscope grid lines -->
|
||||
<line x1="2" y1="16" x2="30" y2="16" stroke="#313244" stroke-width="0.8"/>
|
||||
<line x1="16" y1="3" x2="16" y2="29" stroke="#313244" stroke-width="0.8"/>
|
||||
<!-- waveform -->
|
||||
<polyline points="2,16 7,16 9,7 12,25 16,7 20,25 23,16 25,16 27,11 30,11"
|
||||
fill="none" stroke="#89b4fa" stroke-width="2"
|
||||
stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<!-- scope bezel -->
|
||||
<rect x="1" y="1" width="30" height="30" rx="4"
|
||||
fill="none" stroke="#45475a" stroke-width="1.5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 638 B |
@@ -0,0 +1,398 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>MARTe2 Debug Client</title>
|
||||
<link rel="icon" type="image/svg+xml" href="favicon.svg">
|
||||
<link rel="stylesheet" href="uplot.min.css">
|
||||
<style>
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
html,body{height:100%;overflow:hidden;font-family:'Segoe UI',system-ui,sans-serif;font-size:12px;background:#1e1e2e;color:#cdd6f4}
|
||||
|
||||
/* ── layout ── */
|
||||
#app{display:flex;flex-direction:column;height:100vh}
|
||||
#toolbar{display:flex;align-items:center;gap:6px;padding:4px 8px;background:#181825;border-bottom:1px solid #313244;flex-shrink:0;flex-wrap:wrap}
|
||||
#main{display:flex;flex:1;overflow:hidden}
|
||||
#left-panel{width:240px;flex-shrink:0;display:flex;flex-direction:column;border-right:1px solid #313244;overflow:hidden}
|
||||
#center-panel{flex:1;overflow:hidden;display:flex;flex-direction:column;gap:6px;padding:6px}
|
||||
#right-panel{width:260px;flex-shrink:0;display:flex;flex-direction:column;border-left:1px solid #313244;overflow:hidden}
|
||||
#log-panel{height:160px;flex-shrink:0;border-top:1px solid #313244;display:flex;flex-direction:column;overflow:hidden}
|
||||
|
||||
/* ── toolbar ── */
|
||||
.tb-group{display:flex;align-items:center;gap:4px;padding:0 6px;border-right:1px solid #313244}
|
||||
.tb-group:last-child{border-right:none}
|
||||
input[type=text],input[type=number]{background:#313244;border:1px solid #45475a;color:#cdd6f4;padding:2px 6px;border-radius:4px;width:100px}
|
||||
input[type=number]{width:60px}
|
||||
button{background:#313244;border:1px solid #45475a;color:#cdd6f4;padding:2px 8px;border-radius:4px;cursor:pointer;white-space:nowrap}
|
||||
button:hover{background:#45475a}
|
||||
button.active{background:#89b4fa;color:#1e1e2e;border-color:#89b4fa}
|
||||
button.danger{background:#f38ba8;color:#1e1e2e;border-color:#f38ba8}
|
||||
button.warn{background:#fab387;color:#1e1e2e;border-color:#fab387}
|
||||
button.ok{background:#a6e3a1;color:#1e1e2e;border-color:#a6e3a1}
|
||||
label{color:#a6adc8;user-select:none}
|
||||
#conn-status{width:8px;height:8px;border-radius:50%;background:#f38ba8;display:inline-block;flex-shrink:0;vertical-align:middle}
|
||||
#conn-status.ok{background:#a6e3a1}
|
||||
#udp-stats{color:#585b70;font-size:11px;margin-left:4px}
|
||||
|
||||
/* ── dropdown menus ── */
|
||||
.menu-wrap{position:relative}
|
||||
.dropdown{position:absolute;top:calc(100% + 4px);left:0;z-index:200;background:#1e1e2e;border:1px solid #45475a;border-radius:6px;padding:8px;min-width:260px;display:none;flex-direction:column;gap:6px;box-shadow:0 4px 16px rgba(0,0,0,.5)}
|
||||
.dropdown.open{display:flex}
|
||||
.menu-sep{border:none;border-top:1px solid #313244;margin:2px 0}
|
||||
.menu-row{display:flex;gap:6px;align-items:center}
|
||||
.menu-row input[type=text]{flex:1;min-width:0;width:auto}
|
||||
.menu-row input[type=number]{width:64px}
|
||||
.menu-btn-row{display:flex;gap:6px}
|
||||
.menu-btn-row button{flex:1}
|
||||
|
||||
/* ── panels ── */
|
||||
.panel-header{padding:4px 8px;background:#181825;border-bottom:1px solid #313244;font-weight:600;color:#89b4fa;display:flex;align-items:center;justify-content:space-between;flex-shrink:0}
|
||||
.panel-search{padding:4px;border-bottom:1px solid #313244;flex-shrink:0}
|
||||
.panel-search input{width:100%;background:#313244;border:1px solid #45475a;color:#cdd6f4;padding:3px 6px;border-radius:4px}
|
||||
.panel-body{flex:1;overflow-y:auto}
|
||||
.panel-toggle{background:transparent;border:none;color:#585b70;cursor:pointer;padding:0 2px;font-size:11px;line-height:1}
|
||||
.panel-toggle:hover{color:#cdd6f4;background:transparent}
|
||||
|
||||
/* ── collapsible panels ── */
|
||||
#left-panel{transition:width .15s;position:relative}
|
||||
#left-panel.collapsed{width:0!important;border:none;overflow:hidden}
|
||||
#right-panel{transition:width .15s;position:relative}
|
||||
#right-panel.collapsed{width:0!important;border:none;overflow:hidden}
|
||||
#log-panel{transition:height .15s}
|
||||
#log-panel.collapsed{height:28px;overflow:hidden}
|
||||
/* collapse toggle strips */
|
||||
.panel-strip{width:14px;flex-shrink:0;display:flex;align-items:center;justify-content:center;cursor:pointer;background:#181825;border:1px solid #313244;color:#585b70;font-size:10px;writing-mode:horizontal-tb;user-select:none}
|
||||
.panel-strip:hover{background:#313244;color:#cdd6f4}
|
||||
#left-strip{border-left:none;border-top:none;border-bottom:none}
|
||||
#right-strip{border-right:none;border-top:none;border-bottom:none}
|
||||
|
||||
/* ── tree ── */
|
||||
.tree-node{padding:1px 0}
|
||||
.tree-leaf{display:flex;align-items:center;gap:2px;padding:1px 4px;cursor:default;user-select:none}
|
||||
.tree-leaf:hover{background:#313244}
|
||||
.tree-leaf.selected{background:#45475a}
|
||||
.tree-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
.tree-class{color:#585b70;font-size:10px;flex-shrink:0}
|
||||
details>summary{list-style:none;cursor:pointer;padding:1px 4px;display:flex;align-items:center;gap:4px;user-select:none}
|
||||
details>summary:hover{background:#313244}
|
||||
details>summary::before{content:'▶';font-size:9px;color:#585b70;width:10px;flex-shrink:0}
|
||||
details[open]>summary::before{content:'▼'}
|
||||
details>.children{padding-left:14px}
|
||||
.tree-btn{background:transparent;border:1px solid #45475a;color:#a6adc8;padding:0 4px;border-radius:3px;cursor:pointer;font-size:10px;line-height:14px}
|
||||
.tree-btn:hover{background:#45475a;color:#cdd6f4}
|
||||
.tree-btn.t{border-color:#89b4fa;color:#89b4fa}
|
||||
.tree-btn.f{border-color:#a6e3a1;color:#a6e3a1}
|
||||
.tree-btn.b{border-color:#fab387;color:#fab387}
|
||||
|
||||
/* ── right panel tabs ── */
|
||||
.tabs{display:flex;border-bottom:1px solid #313244;flex-shrink:0}
|
||||
.tab{flex:1;padding:4px;text-align:center;cursor:pointer;color:#585b70;border-bottom:2px solid transparent}
|
||||
.tab.active{color:#89b4fa;border-bottom-color:#89b4fa}
|
||||
.tab-content{display:none;flex:1;overflow-y:auto;flex-direction:column}
|
||||
.tab-content.active{display:flex}
|
||||
|
||||
/* ── signal items (right panel) ── */
|
||||
.sig-item{display:flex;align-items:center;gap:4px;padding:3px 6px;border-bottom:1px solid #181825}
|
||||
.sig-item:hover{background:#313244}
|
||||
.sig-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px}
|
||||
.sig-val{color:#a6e3a1;font-size:11px;min-width:60px;text-align:right;font-family:monospace}
|
||||
.sig-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}
|
||||
|
||||
/* ── plots ── */
|
||||
.plot-card{background:#181825;border:1px solid #313244;border-radius:6px;overflow:hidden;flex:1;min-height:160px;display:flex;flex-direction:column}
|
||||
.plot-card.drop-active{border-color:#89b4fa}
|
||||
.plot-header{display:flex;align-items:center;gap:6px;padding:4px 8px;background:#11111b;border-bottom:1px solid #313244;flex-shrink:0}
|
||||
.plot-title{font-weight:600;color:#89b4fa;flex:1}
|
||||
.plot-series-bar{display:flex;flex-wrap:wrap;gap:4px;padding:3px 8px;background:#11111b;border-bottom:1px solid #313244;flex-shrink:0}
|
||||
.plot-series-bar:empty{display:none;padding:0;border:none}
|
||||
.series-chip{display:inline-flex;align-items:center;gap:3px;background:#1e1e2e;border:1px solid #45475a;border-radius:10px;padding:1px 3px 1px 6px;font-size:10px}
|
||||
.series-chip-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0}
|
||||
.series-chip-name{max-width:130px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#cdd6f4}
|
||||
.series-chip-rm{background:transparent;border:none;color:#585b70;cursor:pointer;padding:0 2px;font-size:12px;line-height:1}
|
||||
.series-chip-rm:hover{color:#f38ba8;background:transparent}
|
||||
.plot-drop{flex:1;display:flex;align-items:center;justify-content:center;color:#45475a;border:2px dashed #313244;margin:8px;border-radius:4px}
|
||||
.plot-drop.over{border-color:#89b4fa;color:#89b4fa}
|
||||
.uplot-wrap{flex:1;min-height:0;padding:2px 4px 4px;display:flex;flex-direction:column}
|
||||
|
||||
/* ── logs ── */
|
||||
#log-toolbar{display:flex;align-items:center;gap:6px;padding:3px 8px;background:#181825;border-bottom:1px solid #313244;flex-shrink:0}
|
||||
#log-body{flex:1;overflow-y:auto;font-family:monospace;font-size:11px;padding:2px 0}
|
||||
.log-line{padding:1px 8px;display:flex;gap:8px}
|
||||
.log-time{color:#585b70;flex-shrink:0}
|
||||
.log-lvl{flex-shrink:0;min-width:50px;font-weight:600}
|
||||
.log-msg{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}
|
||||
.log-line.DEBUG .log-lvl{color:#89b4fa}
|
||||
.log-line.INFO .log-lvl{color:#a6e3a1}
|
||||
.log-line.WARNING .log-lvl{color:#fab387}
|
||||
.log-line.ERROR,.log-line.FatalError,.log-line.FATAL{background:#2d1b1b}
|
||||
.log-line.ERROR .log-lvl,.log-line.FatalError .log-lvl,.log-line.FATAL .log-lvl{color:#f38ba8}
|
||||
.log-hidden{display:none}
|
||||
|
||||
/* ── dialogs ── */
|
||||
.dialog-overlay{position:fixed;inset:0;background:rgba(0,0,0,.6);display:flex;align-items:center;justify-content:center;z-index:100}
|
||||
.dialog{background:#1e1e2e;border:1px solid #45475a;border-radius:8px;padding:16px;min-width:320px;max-width:500px}
|
||||
.dialog h3{margin-bottom:12px;color:#89b4fa}
|
||||
.dialog label{display:block;margin-bottom:4px;color:#a6adc8}
|
||||
.dialog input,.dialog select,.dialog textarea{width:100%;background:#313244;border:1px solid #45475a;color:#cdd6f4;padding:4px 8px;border-radius:4px;margin-bottom:10px}
|
||||
.dialog textarea{height:80px;resize:vertical;font-family:monospace}
|
||||
.dialog .btns{display:flex;gap:8px;justify-content:flex-end;margin-top:4px}
|
||||
.dialog select option{background:#1e1e2e}
|
||||
.form-row{display:flex;gap:8px}
|
||||
.form-row>*{flex:1}
|
||||
.form-check{display:flex;align-items:center;gap:8px;margin-bottom:10px}
|
||||
.form-check input[type=checkbox]{width:auto;margin:0}
|
||||
.form-check label{margin:0;color:#a6adc8}
|
||||
|
||||
/* ── step status bar ── */
|
||||
#step-bar{background:#2d2b45;border-bottom:1px solid #313244;padding:4px 8px;display:none;align-items:center;gap:8px;flex-shrink:0}
|
||||
#step-bar.visible{display:flex}
|
||||
|
||||
/* ── scrollbars ── */
|
||||
::-webkit-scrollbar{width:6px;height:6px}
|
||||
::-webkit-scrollbar-track{background:#181825}
|
||||
::-webkit-scrollbar-thumb{background:#45475a;border-radius:3px}
|
||||
::-webkit-scrollbar-thumb:hover{background:#585b70}
|
||||
|
||||
/* ── misc ── */
|
||||
.empty-hint{padding:16px;color:#45475a;text-align:center}
|
||||
.break-item{padding:3px 6px;border-bottom:1px solid #181825;display:flex;align-items:center;gap:4px;font-size:11px}
|
||||
.break-sig{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#fab387}
|
||||
.msg-item{padding:3px 6px;border-bottom:1px solid #181825;font-size:11px}
|
||||
.msg-status{width:14px;text-align:center;flex-shrink:0}
|
||||
select{background:#313244;border:1px solid #45475a;color:#cdd6f4;padding:2px 4px;border-radius:4px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
|
||||
<!-- ═══ TOOLBAR ═══ -->
|
||||
<div id="toolbar">
|
||||
|
||||
<!-- Connection menu -->
|
||||
<div class="menu-wrap tb-group">
|
||||
<button id="conn-menu-btn" onclick="toggleMenu('conn-dropdown')">
|
||||
<span id="conn-status"></span> Connection ▾
|
||||
</button>
|
||||
<div class="dropdown" id="conn-dropdown">
|
||||
<div class="menu-row">
|
||||
<input type="text" id="host" value="127.0.0.1" placeholder="host">
|
||||
<input type="number" id="port" value="8080" placeholder="port">
|
||||
<button id="btn-connect" onclick="toggleConnect()">Connect</button>
|
||||
</div>
|
||||
<hr class="menu-sep">
|
||||
<div class="menu-btn-row">
|
||||
<button onclick="sendCmd('DISCOVER');closeAllMenus()">Discover</button>
|
||||
<button onclick="sendCmd('TREE');closeAllMenus()">Tree</button>
|
||||
<button onclick="sendCmd('SERVICE_INFO');closeAllMenus()">Info</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tb-group">
|
||||
<button id="btn-pause" onclick="togglePause()">⏸ Pause</button>
|
||||
<button onclick="openStepDialog()">⚙ Step</button>
|
||||
</div>
|
||||
<div class="tb-group">
|
||||
<button onclick="addPlot()">+ Plot</button>
|
||||
</div>
|
||||
<div class="tb-group">
|
||||
<button onclick="openForceDialog()">⚡ Force</button>
|
||||
<button onclick="openBreakDialog()">🔴 Break</button>
|
||||
<button onclick="openMsgDialog()">✉ Msg</button>
|
||||
</div>
|
||||
<div class="tb-group" style="border:none;margin-left:auto">
|
||||
<span id="udp-stats" style="color:#585b70;font-size:11px">0 pkts</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══ STEP STATUS BAR ═══ -->
|
||||
<div id="step-bar">
|
||||
<span>⏹ PAUSED at <b id="paused-gam">—</b></span>
|
||||
<span id="step-remaining"></span>
|
||||
<select id="step-thread" style="width:120px"></select>
|
||||
<button onclick="step(1)">Step 1</button>
|
||||
<button onclick="step(5)">Step 5</button>
|
||||
<button onclick="step(20)">Step 20</button>
|
||||
<button class="ok" onclick="togglePause()">▶ Resume</button>
|
||||
</div>
|
||||
|
||||
<!-- ═══ MAIN ═══ -->
|
||||
<div id="main">
|
||||
|
||||
<!-- LEFT: object tree -->
|
||||
<div id="left-panel">
|
||||
<div class="panel-header">
|
||||
<span>Object Tree</span>
|
||||
<button style="font-size:10px;padding:1px 6px" onclick="sendCmd('TREE')">↻</button>
|
||||
</div>
|
||||
<div class="panel-search"><input id="tree-search" oninput="filterTree(this.value)" placeholder="Search…"></div>
|
||||
<div class="panel-body" id="tree-body">
|
||||
<div class="empty-hint">Connect to MARTe2 then click Tree</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Left collapse strip -->
|
||||
<div id="left-strip" class="panel-strip" title="Toggle left panel" onclick="togglePanelStrip('left-panel','left-strip','◀','▶')">◀</div>
|
||||
|
||||
<!-- CENTER: plots -->
|
||||
<div id="center-panel">
|
||||
<div class="empty-hint" id="no-plots-hint">Add a plot panel to begin — drag signals from the tree or traced list</div>
|
||||
</div>
|
||||
|
||||
<!-- Right collapse strip -->
|
||||
<div id="right-strip" class="panel-strip" title="Toggle right panel" onclick="togglePanelStrip('right-panel','right-strip','▶','◀')">▶</div>
|
||||
|
||||
<!-- RIGHT: tabs -->
|
||||
<div id="right-panel">
|
||||
<div class="tabs">
|
||||
<div class="tab active" onclick="switchTab('traced')">Traced</div>
|
||||
<div class="tab" onclick="switchTab('forced')">Forced</div>
|
||||
<div class="tab" onclick="switchTab('breaks')">Breaks</div>
|
||||
<div class="tab" onclick="switchTab('msgs')">Msgs</div>
|
||||
</div>
|
||||
<div class="tab-content active" id="tab-traced">
|
||||
<div class="empty-hint" id="no-traced-hint">No signals traced</div>
|
||||
</div>
|
||||
<div class="tab-content" id="tab-forced">
|
||||
<div class="empty-hint" id="no-forced-hint">No signals forced</div>
|
||||
</div>
|
||||
<div class="tab-content" id="tab-breaks">
|
||||
<div class="empty-hint" id="no-breaks-hint">No breakpoints set</div>
|
||||
</div>
|
||||
<div class="tab-content" id="tab-msgs">
|
||||
<div class="empty-hint" id="no-msgs-hint">No messages sent</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- ═══ LOGS ═══ -->
|
||||
<div id="log-panel">
|
||||
<div id="log-toolbar">
|
||||
<button class="panel-toggle" title="Collapse logs" onclick="togglePanel('log-panel','▼','▲',this)" id="log-toggle-btn">▼</button>
|
||||
<span style="font-weight:600;color:#89b4fa">Logs</span>
|
||||
<label><input type="checkbox" id="lf-debug" checked onchange="renderLogs()"> Debug</label>
|
||||
<label><input type="checkbox" id="lf-info" checked onchange="renderLogs()"> Info</label>
|
||||
<label><input type="checkbox" id="lf-warn" checked onchange="renderLogs()"> Warn</label>
|
||||
<label><input type="checkbox" id="lf-error" checked onchange="renderLogs()"> Error</label>
|
||||
<input type="text" id="log-filter" placeholder="Filter…" oninput="renderLogs()" style="width:150px">
|
||||
<button onclick="logs=[];renderLogs()" style="margin-left:auto">Clear</button>
|
||||
<label><input type="checkbox" id="log-autoscroll" checked> Auto-scroll</label>
|
||||
</div>
|
||||
<div id="log-body"></div>
|
||||
</div>
|
||||
|
||||
</div><!-- #app -->
|
||||
|
||||
<!-- ═══ DIALOGS ═══ -->
|
||||
|
||||
<!-- Force dialog -->
|
||||
<div class="dialog-overlay" id="dlg-force" style="display:none" onclick="if(event.target===this)closeDlg('dlg-force')">
|
||||
<div class="dialog">
|
||||
<h3>⚡ Force Signal</h3>
|
||||
<label>Signal</label>
|
||||
<input id="force-sig" list="force-sig-list" placeholder="Signal path…">
|
||||
<datalist id="force-sig-list"></datalist>
|
||||
<label>Value</label>
|
||||
<input id="force-val" placeholder="e.g. 42">
|
||||
<div class="btns">
|
||||
<button onclick="closeDlg('dlg-force')">Cancel</button>
|
||||
<button class="active" onclick="doForce()">Force</button>
|
||||
</div>
|
||||
</div></div>
|
||||
|
||||
<!-- Unforce confirm -->
|
||||
<div class="dialog-overlay" id="dlg-unforce" style="display:none" onclick="if(event.target===this)closeDlg('dlg-unforce')">
|
||||
<div class="dialog">
|
||||
<h3>Remove Force</h3>
|
||||
<p id="unforce-msg" style="margin-bottom:12px;color:#cdd6f4"></p>
|
||||
<div class="btns">
|
||||
<button onclick="closeDlg('dlg-unforce')">Cancel</button>
|
||||
<button class="danger" onclick="doUnforce()">Unforce</button>
|
||||
</div>
|
||||
</div></div>
|
||||
|
||||
<!-- Break dialog -->
|
||||
<div class="dialog-overlay" id="dlg-break" style="display:none" onclick="if(event.target===this)closeDlg('dlg-break')">
|
||||
<div class="dialog">
|
||||
<h3>🔴 Set Breakpoint</h3>
|
||||
<label>Signal</label>
|
||||
<input id="break-sig" list="break-sig-list" placeholder="Signal path…">
|
||||
<datalist id="break-sig-list"></datalist>
|
||||
<div class="form-row">
|
||||
<div>
|
||||
<label>Operator</label>
|
||||
<select id="break-op">
|
||||
<option>></option><option>>=</option>
|
||||
<option><</option><option><=</option>
|
||||
<option>==</option><option>!=</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>Threshold</label>
|
||||
<input id="break-thresh" placeholder="0">
|
||||
</div>
|
||||
</div>
|
||||
<div class="btns">
|
||||
<button onclick="closeDlg('dlg-break')">Cancel</button>
|
||||
<button class="active" onclick="doBreak()">Set Break</button>
|
||||
</div>
|
||||
</div></div>
|
||||
|
||||
<!-- Message dialog -->
|
||||
<div class="dialog-overlay" id="dlg-msg" style="display:none" onclick="if(event.target===this)closeDlg('dlg-msg')">
|
||||
<div class="dialog">
|
||||
<h3>✉ Send Message</h3>
|
||||
<label>Destination</label>
|
||||
<input id="msg-dest" list="msg-dest-list" placeholder="e.g. App.Functions.GAM1">
|
||||
<datalist id="msg-dest-list"></datalist>
|
||||
<label>Function</label>
|
||||
<input id="msg-func" placeholder="FunctionName">
|
||||
<label>Payload (key=value lines)</label>
|
||||
<textarea id="msg-payload" placeholder="Key = Value Key2 = Value2"></textarea>
|
||||
<div class="form-check">
|
||||
<input type="checkbox" id="msg-wait">
|
||||
<label for="msg-wait">Wait for reply</label>
|
||||
</div>
|
||||
<div class="btns">
|
||||
<button onclick="closeDlg('dlg-msg')">Cancel</button>
|
||||
<button class="active" onclick="doSendMsg()">Send</button>
|
||||
</div>
|
||||
</div></div>
|
||||
|
||||
<!-- Info dialog -->
|
||||
<div class="dialog-overlay" id="dlg-info" style="display:none" onclick="if(event.target===this)closeDlg('dlg-info')">
|
||||
<div class="dialog" style="max-width:600px;width:90vw">
|
||||
<h3 id="info-title">Info</h3>
|
||||
<pre id="info-body" style="background:#11111b;padding:8px;border-radius:4px;overflow:auto;max-height:400px;font-size:11px;color:#cdd6f4;white-space:pre-wrap"></pre>
|
||||
<div class="btns" style="margin-top:12px">
|
||||
<button onclick="closeDlg('dlg-info')">Close</button>
|
||||
</div>
|
||||
</div></div>
|
||||
|
||||
<!-- Step dialog -->
|
||||
<div class="dialog-overlay" id="dlg-step" style="display:none" onclick="if(event.target===this)closeDlg('dlg-step')">
|
||||
<div class="dialog">
|
||||
<h3>⚙ Step Execution</h3>
|
||||
<div class="form-row">
|
||||
<div>
|
||||
<label>Count</label>
|
||||
<input type="number" id="step-count" value="1" min="1">
|
||||
</div>
|
||||
<div>
|
||||
<label>Thread (optional)</label>
|
||||
<input id="step-thread-inp" list="step-thread-list" placeholder="all">
|
||||
<datalist id="step-thread-list"></datalist>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btns">
|
||||
<button onclick="closeDlg('dlg-step')">Cancel</button>
|
||||
<button class="active" onclick="doStep()">Step</button>
|
||||
</div>
|
||||
</div></div>
|
||||
|
||||
<script src="uplot.min.js"></script>
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
+1
@@ -0,0 +1 @@
|
||||
.uplot, .uplot *, .uplot *::before, .uplot *::after {box-sizing: border-box;}.uplot {font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";line-height: 1.5;width: min-content;}.u-title {text-align: center;font-size: 18px;font-weight: bold;}.u-wrap {position: relative;user-select: none;}.u-over, .u-under {position: absolute;}.u-under {overflow: hidden;}.uplot canvas {display: block;position: relative;width: 100%;height: 100%;}.u-axis {position: absolute;}.u-legend {font-size: 14px;margin: auto;text-align: center;}.u-inline {display: block;}.u-inline * {display: inline-block;}.u-inline tr {margin-right: 16px;}.u-legend th {font-weight: 600;}.u-legend th > * {vertical-align: middle;display: inline-block;}.u-legend .u-marker {width: 1em;height: 1em;margin-right: 4px;background-clip: padding-box !important;}.u-inline.u-live th::after {content: ":";vertical-align: middle;}.u-inline:not(.u-live) .u-value {display: none;}.u-series > * {padding: 4px;}.u-series th {cursor: pointer;}.u-legend .u-off > * {opacity: 0.3;}.u-select {background: rgba(0,0,0,0.07);position: absolute;pointer-events: none;}.u-cursor-x, .u-cursor-y {position: absolute;left: 0;top: 0;pointer-events: none;will-change: transform;}.u-hz .u-cursor-x, .u-vt .u-cursor-y {height: 100%;border-right: 1px dashed #607D8B;}.u-hz .u-cursor-y, .u-vt .u-cursor-x {width: 100%;border-bottom: 1px dashed #607D8B;}.u-cursor-pt {position: absolute;top: 0;left: 0;border-radius: 50%;border: 0 solid;pointer-events: none;will-change: transform;/*this has to be !important since we set inline "background" shorthand */background-clip: padding-box !important;}.u-axis.u-off, .u-select.u-off, .u-cursor-x.u-off, .u-cursor-y.u-off, .u-cursor-pt.u-off {display: none;}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
/*! https://github.com/leeoniya/uPlot (v1.6.30) */
|
||||
var uPlot=function(){"use strict";const l="u-off",e="u-label",t="width",n="height",i="top",o="bottom",s="left",r="right",u="#000",a=u+"0",f="mousemove",c="mousedown",h="mouseup",d="mouseenter",p="mouseleave",m="dblclick",g="change",x="dppxchange",w="--",_="undefined"!=typeof window,b=_?document:null,v=_?window:null,k=_?navigator:null;let y,M;function S(l,e){if(null!=e){let t=l.classList;!t.contains(e)&&t.add(e)}}function E(l,e){let t=l.classList;t.contains(e)&&t.remove(e)}function T(l,e,t){l.style[e]=t+"px"}function z(l,e,t,n){let i=b.createElement(l);return null!=e&&S(i,e),null!=t&&t.insertBefore(i,n),i}function D(l,e){return z("div",l,e)}const P=new WeakMap;function A(e,t,n,i,o){let s="translate("+t+"px,"+n+"px)";s!=P.get(e)&&(e.style.transform=s,P.set(e,s),0>t||0>n||t>i||n>o?S(e,l):E(e,l))}const W=new WeakMap;function Y(l,e,t){let n=e+t;n!=W.get(l)&&(W.set(l,n),l.style.background=e,l.style.borderColor=t)}const C=new WeakMap;function F(l,e,t,n){let i=e+""+t;i!=C.get(l)&&(C.set(l,i),l.style.height=t+"px",l.style.width=e+"px",l.style.marginLeft=n?-e/2+"px":0,l.style.marginTop=n?-t/2+"px":0)}const H={passive:!0},R={...H,capture:!0};function G(l,e,t,n){e.addEventListener(l,t,n?R:H)}function I(l,e,t,n){e.removeEventListener(l,t,n?R:H)}function L(l,e,t,n){let i;t=t||0;let o=2147483647>=(n=n||e.length-1);for(;n-t>1;)i=o?t+n>>1:tl((t+n)/2),l>e[i]?t=i:n=i;return l-e[t]>e[n]-l?n:t}function O(l,e,t,n){for(let i=1==n?e:t;i>=e&&t>=i;i+=n)if(null!=l[i])return i;return-1}function N(l,e,t,n){let i=ul(l),o=ul(e);l==e&&(-1==i?(l*=t,e/=t):(l/=t,e*=t));let s=10==t?al:fl,r=1==o?il:tl,u=(1==i?tl:il)(s(el(l))),a=r(s(el(e))),f=rl(t,u),c=rl(t,a);return 10==t&&(0>u&&(f=Sl(f,-u)),0>a&&(c=Sl(c,-a))),n||2==t?(l=f*i,e=c*o):(l=Ml(l,f),e=yl(e,c)),[l,e]}function j(l,e,t,n){let i=N(l,e,t,n);return 0==l&&(i[0]=0),0==e&&(i[1]=0),i}_&&function l(){let e=devicePixelRatio;y!=e&&(y=e,M&&I(g,M,l),M=matchMedia(`(min-resolution: ${y-.001}dppx) and (max-resolution: ${y+.001}dppx)`),G(g,M,l),v.dispatchEvent(new CustomEvent(x)))}();const U=.1,B={mode:3,pad:U},V={pad:0,soft:null,mode:0},J={min:V,max:V};function q(l,e,t,n){return Fl(t)?X(l,e,t):(V.pad=t,V.soft=n?0:null,V.mode=n?3:0,X(l,e,J))}function K(l,e){return null==l?e:l}function X(l,e,t){let n=t.min,i=t.max,o=K(n.pad,0),s=K(i.pad,0),r=K(n.hard,-hl),u=K(i.hard,hl),a=K(n.soft,hl),f=K(i.soft,-hl),c=K(n.mode,0),h=K(i.mode,0),d=e-l,p=al(d),m=sl(el(l),el(e)),g=al(m),x=el(g-p);(1e-9>d||x>10)&&(d=0,0!=l&&0!=e||(d=1e-9,2==c&&a!=hl&&(o=0),2==h&&f!=-hl&&(s=0)));let w=d||m||1e3,_=al(w),b=rl(10,tl(_)),v=Sl(Ml(l-w*(0==d?0==l?.1:1:o),b/10),9),k=a>l||1!=c&&(3!=c||v>a)&&(2!=c||a>v)?hl:a,y=sl(r,k>v&&l>=k?k:ol(k,v)),M=Sl(yl(e+w*(0==d?0==e?.1:1:s),b/10),9),S=e>f||1!=h&&(3!=h||f>M)&&(2!=h||M>f)?-hl:f,E=ol(u,M>S&&S>=e?S:sl(S,M));return y==E&&0==y&&(E=100),[y,E]}const Z=new Intl.NumberFormat(_?k.language:"en-US"),$=l=>Z.format(l),Q=Math,ll=Q.PI,el=Q.abs,tl=Q.floor,nl=Q.round,il=Q.ceil,ol=Q.min,sl=Q.max,rl=Q.pow,ul=Q.sign,al=Q.log10,fl=Q.log2,cl=(l,e=1)=>Q.asinh(l/e),hl=1/0;function dl(l){return 1+(0|al((l^l>>31)-(l>>31)))}function pl(l,e,t){return ol(sl(l,e),t)}function ml(l){return"function"==typeof l?l:()=>l}const gl=l=>l,xl=(l,e)=>e,wl=()=>null,_l=()=>!0,bl=(l,e)=>l==e,vl=l=>Sl(l,14);function kl(l,e){return vl(Sl(vl(l/e))*e)}function yl(l,e){return vl(il(vl(l/e))*e)}function Ml(l,e){return vl(tl(vl(l/e))*e)}function Sl(l,e=0){if(Yl(l))return l;let t=10**e;return nl(l*t*(1+Number.EPSILON))/t}const El=new Map;function Tl(l){return((""+l).split(".")[1]||"").length}function zl(l,e,t,n){let i=[],o=n.map(Tl);for(let s=e;t>s;s++){let e=el(s),t=Sl(rl(l,s),e);for(let l=0;n.length>l;l++){let r=n[l]*t,u=(0>r||0>s?e:0)+(o[l]>s?o[l]:0),a=Sl(r,u);i.push(a),El.set(a,u)}}return i}const Dl={},Pl=[],Al=[null,null],Wl=Array.isArray,Yl=Number.isInteger;function Cl(l){return"string"==typeof l}function Fl(l){let e=!1;if(null!=l){let t=l.constructor;e=null==t||t==Object}return e}function Hl(l){return null!=l&&"object"==typeof l}const Rl=Object.getPrototypeOf(Uint8Array);function Gl(l,e=Fl){let t;if(Wl(l)){let n=l.find((l=>null!=l));if(Wl(n)||e(n)){t=Array(l.length);for(let n=0;l.length>n;n++)t[n]=Gl(l[n],e)}else t=l.slice()}else if(l instanceof Rl)t=l.slice();else if(e(l)){t={};for(let n in l)t[n]=Gl(l[n],e)}else t=l;return t}function Il(l){let e=arguments;for(let t=1;e.length>t;t++){let n=e[t];for(let e in n)Fl(l[e])?Il(l[e],Gl(n[e])):l[e]=Gl(n[e])}return l}function Ll(l,e,t){for(let n,i=0,o=-1;e.length>i;i++){let s=e[i];if(s>o){for(n=s-1;n>=0&&null==l[n];)l[n--]=null;for(n=s+1;t>n&&null==l[n];)l[o=n++]=null}}}const Ol="undefined"==typeof queueMicrotask?l=>Promise.resolve().then(l):queueMicrotask,Nl=["January","February","March","April","May","June","July","August","September","October","November","December"],jl=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];function Ul(l){return l.slice(0,3)}const Bl=jl.map(Ul),Vl=Nl.map(Ul),Jl={MMMM:Nl,MMM:Vl,WWWW:jl,WWW:Bl};function ql(l){return(10>l?"0":"")+l}const Kl={YYYYLine truncated
|
||||
Reference in new issue
Block a user