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.
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.
A wedged custom instrument blocks dispose RPC, which made every
write_custom_instrument_file call hang waiting on the live reload.
The store write already succeeded; only the side-effect bounce was
stuck, and that's exactly when you most need to edit the source.
Detach the reload into a Task so writes return as soon as the
on-disk state is updated.
Add Engine.anchor(sessionID:, address:) that uses the live node
when present and falls back to ProcessSession.lastKnownModules.
Route getOrCreateInsight and the tracer backtrace popovers
through it so detach no longer hides the button or fails the
module lookup. Look up the node lazily in click handlers;
symbolication still requires a live session and explains so
inline when unavailable.
Adopt Finder-style indentation: child icon starts where its
parent icon ends; spacings are 3/5/6 (leading, chevron-to-icon,
icon-to-label). Same rule cascades to grandchildren.
GTK: drop the 1px border on .luma-session-icon for an inset
shadow so the icon's allocation stays at 24.
Replace the simple warning icon with a button that opens a
popover containing the headline, summary and diagnostic
stack (when present). Tracer hook rows reuse the same
indicator, driven by the per-component status map on the
node's InstrumentRef.
Sidebar: manual expansion for sessions and custom instruments;
child rows align under parent icon column. Surface tracer hooks
inline under the Tracer item with a Browse-all popover for the
overflow.
Editor: drop static header rows; SaveBar slides down from the
editor top with Save button and unsaved indicator. Shared
between tracer and custom instrument editors.
GTK: pin detached banner to system-orange across focused and
backdrop states. Trim chevron button padding so it honors the
24px column, and tighten row padding so the icon sits 4px from
the row edge.
EngineRegistry caches one Engine per working-project URL.
MainWindowView.init does a pure lookup, so SwiftUI's
view-tree re-evaluations cannot open extra ProjectStores
that race the live one's BEGIN IMMEDIATE and crash via
try!.
UI-state persistence (collapse flag, sidebar selection,
session detail section, etc.) lives on Engine for GTK to
adopt. SQLite has a 5s busy timeout. Engine open failures
surface as an error view.
TargetPicker is host @State injected via environment.
Global instrument-UI defaults register at app startup;
per-engine instrument and local-notifier hookups live in
Engine extensions.
Engine owns the widget state cache, applies inbound updates from
the agent, persists session-scoped widgets, and broadcasts to
collaborating peers (action invocations route to the host).
Renderers in both UIs read the cache and the unified update
stream, with a Clear button that propagates to peers.
Agent SDK adds ctx.widget(id) with push/upsertItem/removeItem/
clear, plus a typed onAction handler. create() now takes a
restored snapshot seeded from persisted state and the join
snapshot.
Per-def .d.ts narrows the widget map, restored shape, and action
union to the exact ids declared in the editor.
Hookpack manifest mirrors CustomInstrumentDef: rich features,
TypeScript entry, optional file-based icon. Folder name is the
pack id.
HookPackConfig holds FeatureState per feature, producing the
same agent-JSON shape as custom instruments. Hookpack sources
compile through the shared TypeScript pipeline.
Import sits next to "New Custom Instrument..."; Export on the
custom-instrument context menu. Both UIs.
Gate min-width frames behind isCompactWidth so the event stream,
target picker, codeshare browser, and collaboration panel collapse
cleanly on iPhone instead of forcing a scroll at their regular
widths. Give the event stream a dedicated compact header that
promotes the source / process filters to icon menus and keeps the
search, pause, and clear controls inline.
Drop the auto-select-first behavior in the target picker and
codeshare browser on compact, so tapping a sidebar row navigates
to the detail pane on NavigationSplitView's built-in stack instead
of stranding the user looking at a preselected item.
Push the instrument event-row monospaced font up into each
InstrumentUI factory and out of JSInspectValueView, so tracer,
hook-pack, and codeshare rows match without recomputing it for
every sub-value. Migrate the JS inspector disclosure label to
AttributedString to kill the deprecated Text + Text concatenation
warning. Tighten REPL result formatting and relabel the detach
banner hint.
Workspace is now a thin host adapter. Engine owns sessions and
notebook entries (via store observation), GitHub auth + OAuth
device flow, hook-pack discovery, address annotations and address
actions, the disassembler cache, and all instrument lifecycle.
EventLog is @Observable so SwiftUI tracks it directly; the host
no longer mirrors event arrays or version counters.
R2 (live disassembly + trace inspection) lives entirely in
LumaCore as concrete Disassembler / TraceDisassembler classes
returning portable StyledText spans. ProcessNodeViewModel and
InstrumentRuntime are gone; views read engine.processNodes /
node.instruments / engine.descriptor(for:) directly.
Reorganize Luma/Models/ — instrument UI dispatch lives under
Luma/Instruments/, Monaco editor glue under Luma/Editor/, and the
remaining UI types sit at the top of Luma/.
Engine and ProcessNode are @Observable. Workspace
observes engine.processNodes via withObservationTracking
to sync VMs reactively. Views call workspace.engine.*
directly for spawn, attach, resume, instrument lifecycle,
insights, and tracer hooks. Engine instrument methods
take InstrumentInstance directly instead of split params.
InstrumentDescriptor (LumaCore) holds the data: kind,
identifier, display name, icon, initial config, and
event summary. Engine maintains a descriptor registry.
InstrumentUI protocol (Luma) defines the SwiftUI view
factories. TracerUI, HookPackUI, and CodeShareUI
implement it. InstrumentUIRegistry maps descriptor
IDs to UI instances.
Workspace no longer constructs templates. Views look
up descriptors via engine and UI via the registry.