Symbols share name+address (aliases, mapping symbols, empty names),
so the models' data-derived ids collide and SwiftUI's Table resolved
a click to the first row with that id, snapping to the previous
selection. Identify rows by position instead. Also guard the async
query against writing results from a superseded request.
Serve symbol pages by offset and step through them with prev/next
controls showing the current range. Reset to the first page when
the filter or category changes. Clamp the page index at the
mutation site and floor the offset in the agent so hammering
prev/next can't run past the ends into a negative range.
Bind Cmd+F (Ctrl+F on GTK) to focus the filter input.
Modules like YouTube Kids export 100k+ symbols. Enumerating and
marshalling the whole set per open stalled the UI, and both
frontends re-filtered the full arrays on the main thread on every
keystroke.
Build a cached, pre-sorted, pre-lowercased index in the agent and
serve filtered, capped pages. Add queryModuleSymbols returning a
window plus match and category counts. Wire both frontends to it
with a debounced filter; drop the client-side filtering.
A per-cell context menu intercepts the primary click in a macOS
Table, so rows rarely got selected. Make the cells passive and
move the address menu to a table-level contextMenu(forSelectionType:),
reusing a shared AddressMenuItems.
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.
The export/import/symbol tables attached the context menu to the
cell text, so a one-character symbol left almost nothing to hit.
Fill each cell and make it hit-testable.
Right-clicking anywhere on an export/import/symbol row now opens
the address menu, and its copy item reads "Copy Address". On
macOS each cell carries the menu and hover-warms facts; GTK
already attached the menu to the row, so it just labels the copy
item.
One shared component per frontend renders the value with a
context menu gated by the resolved address facts: Copy, Open
Disassembly when executable, Open Memory when mapped, then the
hook actions. Replace the per-site menus.
Modules-view actions built names via anchor.displayString,
yielding module!symbol for hooks and module+offset for
disassembly/memory insights. Route both through the symbol the
tracer search already uses.
Add AddressAnchor.symbolName; default tracer hook names to it.
Thread the context anchor hint into getOrCreateInsight so
insights opened on exports and imports inherit the symbol name.
Drop the unused title parameter from ModuleDetailView.
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.
The export, import, and symbol contexts now carry an anchorHint:
exports use moduleExport(name, export); imports do the same when
the import's source module is known; symbols stay anchorless.
"Add Function Hook" therefore lands on the actual symbol name.
Drop the leading top padding and render the picker plus an
empty Table while the symbol bundle loads, so switching between
modules updates row counts in place instead of flashing through
a centered ProgressView.
Drop the redundant header that repeated the master row's name
and base address along with the runtime-pointless reload
button. Cache symbol bundles by module ID and stop forcing a
view recreation on each module switch, so re-visiting a module
is instant and switching no longer flickers through a progress
spinner.
Make the per-tab Tables selectable and expose row actions via
contextMenu(forSelectionType:) and primaryAction. The address
menu now offers Open Disassembly + Open Memory in a stable
order without the duplicated entries the previous "default
above the divider, both below" layout produced.
Sessions now have a top-level detail pane (Summary / Modules /
Threads) with master-detail panes for module symbols and thread
register snapshots. Sidebar gains a session row, and the ITrace
sidebar entries adopt the unified naming.
ITrace detail decodes the streaming trace data with first-paint
prefix, onProgress, and a Stop control restricted to thread
origins; the timeline paginates past 200 instructions.
The document is now a .luma directory wrapper containing
db.sqlite alongside a traces/ folder. Workspace owns the
ProjectUIState (selection, event-stream layout, collaboration
visibility) and a per-session SessionUIState (detail section,
last selected module/thread). MainWindowView reads/writes these
through Bindings instead of local @State.