Improved all axpect of tracing / forcing arrays
Improved ui and tree export
This commit is contained in:
Binary file not shown.
@@ -347,9 +347,11 @@ func (m *MarteClient) handleJSONResponse(tag, data string) {
|
||||
return
|
||||
|
||||
case "TREE":
|
||||
// Parse server-side and stream pre-digested flat-node batches to the
|
||||
// browser so it never has to JSON.parse a multi-MB string.
|
||||
go m.sendTreeBatches(data)
|
||||
// Per-node response — forward to browser as-is.
|
||||
broadcast(m.hub, map[string]any{
|
||||
"type": "tree_node",
|
||||
"data": data,
|
||||
})
|
||||
return
|
||||
}
|
||||
broadcast(m.hub, map[string]any{
|
||||
@@ -359,90 +361,6 @@ func (m *MarteClient) handleJSONResponse(tag, data string) {
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// TREE streaming
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// TreeNode mirrors the DebugService TREE JSON structure.
|
||||
type TreeNode struct {
|
||||
Name string `json:"Name"`
|
||||
Class string `json:"Class"`
|
||||
IsTraceable bool `json:"IsTraceable"`
|
||||
IsForcable bool `json:"IsForcable"`
|
||||
Elements uint32 `json:"Elements"`
|
||||
Children []*TreeNode `json:"Children"`
|
||||
}
|
||||
|
||||
// FlatNode is a pre-digested, compact representation sent to the browser.
|
||||
type FlatNode struct {
|
||||
Path string `json:"p"`
|
||||
Name string `json:"n"`
|
||||
Class string `json:"c"`
|
||||
Parent string `json:"par"`
|
||||
Tr bool `json:"tr,omitempty"`
|
||||
Fo bool `json:"fo,omitempty"`
|
||||
El uint32 `json:"el,omitempty"`
|
||||
HasCh bool `json:"ch,omitempty"`
|
||||
}
|
||||
|
||||
func flattenTree(node *TreeNode, parentPath string, out *[]FlatNode) {
|
||||
path := node.Name
|
||||
if parentPath != "" {
|
||||
path = parentPath + "." + node.Name
|
||||
}
|
||||
hasCh := len(node.Children) > 0
|
||||
*out = append(*out, FlatNode{
|
||||
Path: path,
|
||||
Name: node.Name,
|
||||
Class: node.Class,
|
||||
Parent: parentPath,
|
||||
Tr: node.IsTraceable,
|
||||
Fo: node.IsForcable,
|
||||
El: node.Elements,
|
||||
HasCh: hasCh,
|
||||
})
|
||||
for _, c := range node.Children {
|
||||
flattenTree(c, path, out)
|
||||
}
|
||||
}
|
||||
|
||||
// sendTreeBatches parses the raw TREE JSON and streams compact FlatNode
|
||||
// batches to all browsers. Each browser processes one batch per animation
|
||||
// frame, keeping the UI responsive during large tree loads.
|
||||
func (m *MarteClient) sendTreeBatches(raw string) {
|
||||
var root TreeNode
|
||||
if err := json.Unmarshal([]byte(raw), &root); err != nil {
|
||||
log.Printf("[TREE] parse error: %v — falling back to raw JSON", err)
|
||||
broadcast(m.hub, map[string]any{"type": "response", "tag": "TREE", "data": raw})
|
||||
return
|
||||
}
|
||||
|
||||
var nodes []FlatNode
|
||||
if root.Name == "Root" && len(root.Children) > 0 {
|
||||
for _, c := range root.Children {
|
||||
flattenTree(c, "", &nodes)
|
||||
}
|
||||
} else {
|
||||
flattenTree(&root, "", &nodes)
|
||||
}
|
||||
|
||||
total := len(nodes)
|
||||
log.Printf("[TREE] streaming %d flat nodes to browser", total)
|
||||
broadcast(m.hub, map[string]any{"type": "tree_clear", "total": total})
|
||||
|
||||
const batchSize = 50
|
||||
for i := 0; i < total; i += batchSize {
|
||||
end := i + batchSize
|
||||
if end > total {
|
||||
end = total
|
||||
}
|
||||
broadcast(m.hub, map[string]any{
|
||||
"type": "tree_batch",
|
||||
"nodes": nodes[i:end],
|
||||
"done": end == total,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MarteClient) handleTextLine(line string) {
|
||||
if strings.HasPrefix(line, "OK SERVICE_INFO") {
|
||||
|
||||
+883
-193
File diff suppressed because it is too large
Load Diff
@@ -81,6 +81,7 @@ 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-loading{padding:2px 4px;color:#585b70;font-size:10px;font-style:italic}
|
||||
.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}
|
||||
@@ -156,6 +157,21 @@ details>.children{padding-left:14px}
|
||||
::-webkit-scrollbar-thumb{background:#45475a;border-radius:3px}
|
||||
::-webkit-scrollbar-thumb:hover{background:#585b70}
|
||||
|
||||
/* ── grouped array signal in trace list ── */
|
||||
.sig-group{display:flex;align-items:center;gap:4px;padding:3px 6px;border-bottom:1px solid #1e1e2e;user-select:none;cursor:grab}
|
||||
.sig-group:hover{background:#313244}
|
||||
.sig-group-exp{color:#585b70;font-size:10px;width:14px;text-align:center;flex-shrink:0;cursor:pointer}
|
||||
.sig-group-exp:hover{color:#89b4fa}
|
||||
.sig-item-sub{padding-left:24px;background:#11111b;border-left:2px solid #313244;margin-left:2px}
|
||||
.sig-item-sub:hover{background:#1e1e2e}
|
||||
|
||||
/* ── array selection toggle ── */
|
||||
.arr-seg{display:flex;gap:0;margin-bottom:12px;border-radius:4px;overflow:hidden;border:1px solid #45475a}
|
||||
.arr-seg button{flex:1;border:none;border-radius:0;border-right:1px solid #45475a;color:#a6adc8;background:#313244;padding:4px 0;font-size:11px}
|
||||
.arr-seg button:last-child{border-right:none}
|
||||
.arr-seg button.active{background:#89b4fa;color:#1e1e2e}
|
||||
.arr-seg button:hover:not(.active){background:#45475a;color:#cdd6f4}
|
||||
|
||||
/* ── 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}
|
||||
@@ -276,7 +292,7 @@ select{background:#313244;border:1px solid #45475a;color:#cdd6f4;padding:2px 4px
|
||||
<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-service" checked onchange="renderLogs()">
|
||||
<label><input type="checkbox" id="lf-service" onchange="renderLogs()">
|
||||
Service</label>
|
||||
<label><input type="checkbox" id="lf-debug" checked onchange="renderLogs()">
|
||||
Debug</label>
|
||||
@@ -369,6 +385,87 @@ select{background:#313244;border:1px solid #45475a;color:#cdd6f4;padding:2px 4px
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Array signal dialog (Trace / Force / Break with index/range selection) -->
|
||||
<div class="dialog-overlay" id="dlg-array" style="display:none" onclick="if(event.target===this)closeDlg('dlg-array')">
|
||||
<div class="dialog" style="min-width:360px">
|
||||
<h3 id="arr-dlg-title">Array Signal</h3>
|
||||
<p style="font-size:11px;color:#a6adc8;margin-bottom:14px">
|
||||
<b id="arr-dlg-sig" style="color:#cdd6f4"></b>
|
||||
· <span id="arr-dlg-n" style="color:#89b4fa"></span> elements
|
||||
</p>
|
||||
<!-- Segmented selection toggle -->
|
||||
<label style="margin-bottom:6px">Apply to</label>
|
||||
<div class="arr-seg">
|
||||
<button id="arr-sel-all" class="active" onclick="setArrSel('all')">All</button>
|
||||
<button id="arr-sel-idx" onclick="setArrSel('idx')">Index</button>
|
||||
<button id="arr-sel-rng" onclick="setArrSel('rng')">Range</button>
|
||||
</div>
|
||||
<!-- Index mode: single number input -->
|
||||
<div id="arr-idx-sect" style="display:none">
|
||||
<label>Element index <span style="color:#585b70;font-weight:normal">(0 – <span id="arr-idx-max"></span>)</span></label>
|
||||
<input id="arr-idx" type="number" min="0" value="0">
|
||||
</div>
|
||||
<!-- Range mode: start / end -->
|
||||
<div id="arr-rng-sect" style="display:none">
|
||||
<div class="form-row">
|
||||
<div>
|
||||
<label>From index</label>
|
||||
<input id="arr-r0" type="number" min="0" value="0">
|
||||
</div>
|
||||
<div>
|
||||
<label>To index <span style="color:#585b70;font-weight:normal">inclusive</span></label>
|
||||
<input id="arr-r1" type="number" min="0" value="0">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Force value (shown only for Force action) -->
|
||||
<div id="arr-force-sect" style="display:none">
|
||||
<label>Force value</label>
|
||||
<input id="arr-force-val" placeholder="e.g. 0">
|
||||
</div>
|
||||
<!-- Break condition (shown only for Break action) -->
|
||||
<div id="arr-break-sect" style="display:none">
|
||||
<div class="form-row">
|
||||
<div>
|
||||
<label>Condition</label>
|
||||
<select id="arr-break-op">
|
||||
<option>></option><option>>=</option>
|
||||
<option><</option><option><=</option>
|
||||
<option>==</option><option>!=</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>Threshold</label>
|
||||
<input id="arr-break-thr" placeholder="0">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btns">
|
||||
<button onclick="closeDlg('dlg-array')">Cancel</button>
|
||||
<button id="arr-ok-btn" class="active" onclick="doArrayOp()">Apply</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Array → plot mode dialog (Sequential / Waterfall) -->
|
||||
<div class="dialog-overlay" id="dlg-arr-plot" style="display:none" onclick="if(event.target===this)closeDlg('dlg-arr-plot')">
|
||||
<div class="dialog" style="min-width:360px">
|
||||
<h3>Plot Array Signal</h3>
|
||||
<p style="font-size:11px;color:#a6adc8;margin-bottom:14px">
|
||||
<b id="arrp-name" style="color:#cdd6f4"></b>
|
||||
· <span id="arrp-n" style="color:#89b4fa"></span>
|
||||
elements
|
||||
</p>
|
||||
<div class="arr-seg" style="margin-bottom:8px">
|
||||
<button id="arrp-sel-sequential" class="active" onclick="setArrpMode('sequential')">Sequential</button>
|
||||
<button id="arrp-sel-waterfall" onclick="setArrpMode('waterfall')">Waterfall</button>
|
||||
</div>
|
||||
<p id="arrp-desc" style="font-size:10px;color:#a6adc8;margin-bottom:16px;min-height:28px"></p>
|
||||
<div class="btns">
|
||||
<button onclick="closeDlg('dlg-arr-plot')">Cancel</button>
|
||||
<button class="active" onclick="doArrayPlotMode()">Add</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">
|
||||
|
||||
Reference in New Issue
Block a user