scripts/DEBUGGER_REQUIREMENTS_2.md
The debugger needs a snapshot system that lets users save the current app
state under a user-chosen ID (e.g. "initial-state", "after-login") and
restore any saved snapshot later.
app.state.appStateJson is deep-cloned and stored under that key in
app.state.snapshots (a { [alias]: jsonValue } map).set_app_state){ op: 'set_app_state', state: <snapshot> }, then calls
handlers.loadAppState() and handlers.refreshDOM() to reflect the change.handlers.save() (localStorage)
and in the Export / Import JSON (see §2).E2E test steps should be able to reference a snapshot alias instead of providing a full inline JSON blob:
{ "op": "restore_snapshot", "alias": "initial-state" }.
The client-side runner resolves the alias from app.state.snapshots and
sends set_app_state with the stored JSON. The server-side runner
should accept a snapshots map alongside the test definition so it can
resolve aliases without a round-trip.E2eSetup.app_state field (Rust) should also accept a string alias
(try to look it up from a provided snapshots map, fall back to treating
it as inline JSON).restore_snapshot with a dropdown of all
saved aliases.RunE2eTests request payload to accept an optional
snapshots: HashMap<String, serde_json::Value>.restore_snapshot or when E2eSetup.app_state is a
string, look it up in the provided map.exportProject() currently serialises app.state.tests verbatim, which
includes internal runtime fields (_result, lastResponse, status,
error, screenshot, duration_ms) that are useless in a saved project
file.
Fix: Strip internal fields before serialisation:
tests: app.state.tests.map(function(t) {
return {
name: t.name,
steps: t.steps.map(function(s) {
return { op: s.op, params: s.params || {} };
}),
};
}),
The project export should additionally contain:
| Key | Content |
|---|---|
snapshots | app.state.snapshots — all saved app-state aliases |
htmlTree | Result of get_node_hierarchy at export time (optional) |
resolvedSymbols | A map of { address → resolvedInfo } collected during the |
| session (see §6) | |
componentRegistry | Last loaded component list (if available) |
importProject() should restore snapshots and (optionally) warn if the
HTML tree in the project differs from the current live tree.
The "Response" section in the Step Details panel (showStepDetails())
dumps step.lastResponse as a JSON.stringify(…, null, 2) string inside
a <pre> block.
Reuse the existing app.json tree widget (the same one used by the App
State panel) to render the response:
// In showStepDetails(), replace the <pre> block with:
var responseContainer = document.createElement('div');
responseContainer.id = 'step-response-tree';
app.json.render('step-response-tree', step.lastResponse);
The tree should be read-only (no inline editing) — either add a
readOnly option to app.json.render() or skip attaching edit handlers
when the container is not app-state-tree.
handlers.loadComponents() is never called. The "components" sidebar view
starts with the placeholder text "Loading component registry…" and never
progresses.
Call handlers.loadComponents() when the user switches to the components
view. In switchView('components') (or the equivalent sidebar tab click
handler), add:
if (view === 'components') {
handlers.loadComponents();
}
Also consider calling it once during init() so the data is ready when the
user first opens the panel.
The toolbar has icon-only buttons with these tooltips:
| Icon | Tooltip | Action |
|---|---|---|
| ▶ (green) | "Run (client)" | app.runner.run() |
| ☁↑ | "Run on server" | app.runner.runServerSide() |
| ☁✓ | "Run all on server" | app.runner.runAllServerSide() |
Rename tooltips:
Headless execution in a separate window: The "Run headless" mode should ideally not interfere with the main UI window. Since the server-side runner already executes inside the application process, it operates without user-visible rendering. The tooltip / UI should clarify that "headless" means the test runs server-side without visual feedback (no window interaction), while "Run" drives the visible window step-by-step.
Optional UX: Show a small "headless" badge or indicator next to the cloud icon so the distinction is clear without hovering.
Currently, function pointers are only resolved when the user manually clicks on a callback address in the node detail panel.
Required: Resolve automatically whenever:
showNodeDetail())Batch all callback addresses for the selected node into a single
resolve_function_pointers request and display results inline.
backtrace (Rust Side)The current dladdr-based resolver only returns the shared library path and
the symbol name — not the source file or line number.
Enhance the Rust resolver (resolve_function_pointer):
Try backtrace::resolve first (requires the user's binary compiled
with -g). If it returns filename() and lineno(), include them in
ResolvedSymbolInfo as source_file: Option<String> and
source_line: Option<u32>.
Warn about -g: If backtrace returns only the symbol name but no
file/line, set a hint field in the response:
"Compile with -g for source locations".
Minimal heuristic fallback (worst case): If backtrace gives a
symbol name but no file, do a minimal search of the current working
directory for a definition of that symbol. Constraints:
.c, .cpp, .h, .cc, .m, .mm files..gitignore (use the ignore crate or equivalent).^.*\b<symbol_name>\s*\( (find definition, not call
sites).Add source_file and source_line to ResolvedSymbolInfo:
pub struct ResolvedSymbolInfo {
pub symbol_name: String,
pub file_name: String, // shared library path (existing)
pub source_file: Option<String>, // source code file path (new)
pub source_line: Option<u32>, // line number (new)
pub hint: Option<String>, // e.g. "Compile with -g"
pub approximate: bool, // true if heuristic was used
}
When source_file and source_line are present, show a clickable
"Open" link next to the resolved symbol.
open (macOS) / xdg-open (Linux) / start
(Windows) — not code. This respects the user's default editor
association. On macOS, open <file> opens the file in the default
app.vscode://file/<path>:<line> URL and
open it via window.open(). This works if VS Code is installed but
doesn't require it.main.c:15 → clicking sends a request to the debug server with
{ op: 'open_file', file: '/full/path/main.c', line: 15 }, which
calls open on the server side.Maintain a session-level cache app.state.resolvedSymbols mapping
{ address → resolvedInfo } so repeated node selections don't re-resolve
the same pointers. Include this cache in the project export (see §2.2).
Test names in the sidebar can be renamed via double-click, but there is no visual affordance (no pencil icon).
Use a <span class="edit-icon" onclick="...">✏️</span> element styled to
appear on hover (opacity 0 → 1 on .test-item:hover .edit-icon).
(Kept for reference — already implemented.)
success: false, found: false, passed: false in API responses.