Files
luma/Luma/SessionDetailView.swift
Ole André Vadla Ravnås 38e5198209 Move modules and threads into the sidebar
Drop the modules/threads tabs; show both as collapsible sidebar
groups mirroring the hooks UX: a handful of top picks plus a
browse popover for the rest. Default threads to collapsed.

Revert the eager re-analysis of persisted modules on attach; keep
disassembly analysis lazy and add a per-module Analyze action and
status indicator instead.

Share one sidebar-row and one browser-popover component across
hooks, modules, and threads in both the SwiftUI and GTK frontends.
2026-06-27 14:53:01 +02:00

105 lines
3.2 KiB
Swift

import LumaCore
import SwiftUI
struct SessionDetailView: View {
let sessionID: UUID
let engine: Engine
@Binding var selection: SidebarItemID?
@Environment(\.errorPresenter) private var errorPresenter
var body: some View {
VStack(alignment: .leading, spacing: 12) {
header
Divider()
summaryContent
}
.padding(.horizontal, 20)
.padding(.vertical, 16)
}
private var session: LumaCore.ProcessSession? {
engine.session(id: sessionID)
}
private var node: LumaCore.ProcessNode? {
engine.node(forSessionID: sessionID)
}
private var header: some View {
Text(node?.processName ?? session?.processName ?? "Session")
.font(.title2).bold()
}
private var summaryContent: some View {
ScrollView {
summaryGrid
.padding(.top, 4)
.frame(maxWidth: .infinity, alignment: .leading)
}
}
private var summaryGrid: some View {
let session = session
let node = node
return Grid(alignment: .leadingFirstTextBaseline, horizontalSpacing: 16, verticalSpacing: 4) {
row("Status", statusText(session: session, node: node))
row("Device", node?.deviceName ?? session?.deviceName ?? "—")
row("PID", String(node?.pid ?? session?.lastKnownPID ?? 0))
if let info = session?.processInfo {
row("Platform", info.platform)
row("Architecture", info.arch)
row("Pointer size", "\(info.pointerSize) bytes")
}
if let main = session?.lastKnownMainModule {
row("Main module", main.name)
row("Path", main.path)
baseRow(address: main.base)
row("Size", "\(main.size) bytes")
}
}
.font(.system(.body, design: .monospaced))
}
private func statusText(session: LumaCore.ProcessSession?, node: LumaCore.ProcessNode?) -> String {
if let node {
switch node.phase {
case .attaching: return "Attaching…"
case .attached: return "Attached"
case .detached: return "Detached"
}
}
switch session?.phase {
case .attaching: return "Attaching…"
case .awaitingInitialResume: return "Awaiting initial resume"
case .attached: return "Attached"
case .idle, .none: return "Idle"
}
}
private func row(_ label: String, _ value: String) -> some View {
GridRow {
Text(label)
.foregroundStyle(.secondary)
.gridColumnAlignment(.leading)
Text(value)
.textSelection(.enabled)
}
}
private func baseRow(address: UInt64) -> some View {
GridRow {
Text("Base")
.foregroundStyle(.secondary)
.gridColumnAlignment(.leading)
PointerValueText(
engine: engine,
sessionID: sessionID,
value: String(format: "0x%llx", address),
address: address,
selection: $selection
)
}
}
}