Files
luma/Luma/Instruments/TracerUI.swift
Ole André Vadla Ravnås 5ed87e6018 Make sidebar hooks collapsible like module groups
Give instruments that have hooks a disclosure chevron and persist
the per-instrument collapsed state in the session UI state. Toggle
the module and thread groups on a full header-row click. Pad the
module view so its tabs clear the top-left edge.
2026-06-27 15:30:30 +02:00

126 lines
3.7 KiB
Swift

import SwiftUI
import LumaCore
struct TracerUI: InstrumentUI {
func makeConfigEditor(
configJSON: Binding<Data>,
engine: Engine,
selection: Binding<SidebarItemID?>
) -> AnyView {
let configBinding = Binding<TracerConfig>(
get: {
(try? TracerConfig.decode(from: configJSON.wrappedValue)) ?? TracerConfig()
},
set: { newValue in
configJSON.wrappedValue = newValue.encode()
}
)
return AnyView(
TracerConfigView(
config: configBinding,
engine: engine,
selection: selection
)
)
}
func renderEvent(
_ event: RuntimeEvent,
engine: Engine,
selection: Binding<SidebarItemID?>
) -> AnyView {
guard case .jsValue(let v) = event.payload,
let ev = Engine.parseTracerEvent(from: v)
else {
return AnyView(
Text(String(describing: event.payload))
.font(.system(.footnote, design: .monospaced))
.textSelection(.enabled)
)
}
let messageView: AnyView = {
if case .array(_, let elems) = ev.message,
elems.count == 1,
case .string(let messageText) = elems[0]
{
return AnyView(
Text(messageText)
.font(.system(.footnote, design: .monospaced))
.textSelection(.enabled)
)
} else {
return AnyView(
JSInspectValueView(
value: ev.message,
sessionID: event.sessionID ?? UUID(),
engine: engine,
selection: selection
)
.font(.system(.footnote, design: .monospaced))
)
}
}()
return AnyView(
TracerEventRowView(
messageView: messageView,
sessionID: event.sessionID,
backtrace: ev.backtrace,
engine: engine,
selection: selection
)
)
}
func makeEventContextMenuItems(
_ event: RuntimeEvent,
engine: Engine,
selection: Binding<SidebarItemID?>
) -> [InstrumentEventMenuItem] {
guard case .instrument(let instrumentID, _) = event.source,
case .jsValue(let v) = event.payload,
let ev = Engine.parseTracerEvent(from: v),
let sessionID = event.sessionID
else {
return []
}
return [
InstrumentEventMenuItem(
title: "Go to Hook",
systemImage: "arrow.turn.down.right",
role: .normal
) {
selection.wrappedValue = .instrumentComponent(
sessionID,
instrumentID,
ev.id
)
},
]
}
func sidebarChildren(
sessionID: UUID,
instance: LumaCore.InstrumentInstance,
engine: Engine,
selection: Binding<SidebarItemID?>
) -> AnyView {
AnyView(
TracerSidebarChildren(
sessionID: sessionID,
instance: instance,
engine: engine,
selection: selection
)
)
}
func hasSidebarChildren(instance: LumaCore.InstrumentInstance) -> Bool {
guard let config = try? TracerConfig.decode(from: instance.configJSON) else { return false }
return !config.hooksByMostRecentlyEdited().isEmpty
}
}