GTK now opens each document into a fresh working copy and exports
a VACUUM INTO snapshot to the user-facing path on save and on
close. Hooks stay attached across Save As since the engine never
moves. The window close handler holds the GApplication while the
snapshot and shutdown drain.
Engine.shutdown flushes EventLog and awaits the chained
event-store writes so events.log is durable.
LumaProject and ProjectAutosaver share the snapshot helper.
Length-prefixed JSON log at events.log next to db.sqlite,
configurable byte cap with rolling compaction. Replays on
Engine.start.
toLogJSON/fromLogJSON carry the .raw payload and sessionID
that wire JSON drops. EventLog flushes a pendingPersist
queue so the visible-window trim can't drop events.
Replace snapshot-and-rebuild patterns with granular change
events throughout the app.
REPL cells: Engine emits onREPLCellAdded; REPLPane appends
individual cell widgets instead of clearing and rebuilding.
Notebook entries: Add NotebookChange enum with added/updated/
removed/reordered cases. Engine emits from addNotebookEntry,
updateNotebookEntry, deleteNotebookEntry, and collaboration
callbacks. NotebookPane tracks rows by ID and handles each
event incrementally.
Event stream: EventLog emits onEventsAppended with just the
new events from each flush. EventStreamPane appends matching
rows inline. Full rebuild only on filter/search change or
resume from pause.
Device list: Add DeviceChange enum to frida-swift with
appeared/disappeared cases and a changes() stream alongside
existing snapshots(). TargetPicker inserts and removes
individual device rows.
Collaboration: CollaborationSession emits onParticipantJoined,
onParticipantLeft, onChatMessageReceived. CollaborationPanel
appends participant rows and chat bubbles incrementally.
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/.
EventLog (LumaCore) provides the ring-buffered event
log with batched UI updates. Engine feeds the log from
its event stream. loadRemoteDevices moves to Engine.
Workspace observes engine.eventLog.changes and wraps
core events with VM references for the UI.