You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CLAUDE.md
+26-10Lines changed: 26 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -438,16 +438,17 @@ The following UI improvements were made on 2026-02-02 and need testing on GitHub
438
438
439
439
## Experiment Designer
440
440
441
-
### Architecture (v0.8 — 2026-04-08)
441
+
### Architecture (v0.9 — 2026-04-10)
442
442
- 3-zone layout: settings panel (280px left), editor with tab bar (flex right), filmstrip with lane view (bottom)
443
443
- Single `<script type="module">` importing from `js/arena-configs.js`, `js/protocol-yaml.js`, `js/plugin-registry.js`
444
444
- Data model: `experiment` object with `experiment_info`, `arena_info`, `rig_path`, `plugins[]`, `experiment_structure`, phases with `commands[]`, and `conditions[]` with `commands[]`
445
+
- Undo/redo: snapshot-based history stack (JSON.stringify/parse of `experiment`, max 50 entries)
445
446
446
447
### Shared Modules
447
448
-**`js/protocol-yaml.js`** — YAML parser (`simpleYAMLParse` with inline comment stripping), v1/v2 generators, string helpers. Dual-export (window.ProtocolYAML + ES6 module). Used by HTML, both test files.
448
449
-`yamlStr(str)` — double-quotes strings for YAML
449
450
-`yamlPath(str)` — single-quotes paths (no escape sequences, safe for Windows backslashes)
**Duration formula**: `max(trialParams.duration, sum_of_waits)` — shows the actual wall-clock time. If waits exceed trialParams duration, the condition runs longer than the pattern display (visible to the user as a mismatch).
479
+
477
480
**Shared command helpers** (used by Commands tab, Table view, and phase editor):
478
481
-`buildAddCommandOptions()` — returns HTML `<option>`/`<optgroup>` string for add-command dropdowns
479
482
-`createCommandFromSelectValue(value)` — parses `"controller:trialParams"` / `"wait:wait"` / `"plugin:backlight:setRedLEDPower"` into a command object
- Generates protocol v2 via `generateV2Protocol()` from `js/protocol-yaml.js`
484
487
-`rig:` field replaces inline `arena_info`
485
-
-**Paths use single quotes** via `yamlPath()` (pattern_library, rig, script_path) — prevents Windows backslash escape issues
488
+
-**All string values use single quotes** via `yamlPath()` — pattern filenames, paths, plugin string params — prevents Windows backslash escape issues
489
+
-**Empty optional plugin params are omitted** — e.g., an empty `pattern` field is not exported (would cause MATLAB errors)
490
+
-**Plugin param types respected**: input handlers consult `getCommandParams()` schema — string-typed params like `pattern: "1010"` are kept as strings, not coerced to numbers
486
491
-`plugins:` section lists enabled plugins with class/config — only user-set config values are exported (empty = omit)
487
492
- Conditions export full command arrays including plugin commands with params
488
493
- Phases export command arrays directly
@@ -501,6 +506,17 @@ The bottom timeline area is a unified scroll container:
501
506
- Block widths use `Math.max(48, duration * pxPerSecond)` — lane SVG must match this + account for 2px CSS gap between blocks
502
507
- Clicking any filmstrip block switches to the Commands tab
503
508
509
+
### Undo/Redo System (v0.9)
510
+
-**Snapshot stack**: `undoStack[]` and `redoStack[]` store `JSON.stringify(experiment)` snapshots (max 50)
511
+
-**`saveSnapshot()`**: Called before every mutation. Pushes current state to undoStack, clears redoStack.
512
+
-**`_restoring` guard**: Boolean flag set during `restoreSnapshot()` (wrapped in try/finally). Prevents focus events on re-rendered inputs from calling `saveSnapshot()` and clearing the redo stack.
513
+
-**Selection clamping**: `restoreSnapshot()` clamps `selection.index` to valid bounds after restoring, since the restored state may have fewer conditions.
514
+
-**Text inputs**: Snapshot on `focus` (not `input`) — one undo step per field visit, not per keystroke. Browser native Ctrl+Z still works inside inputs.
515
+
-**Reset button**: Clears conditions/phases/plugins to defaults, keeps settings (experiment_info, arena, rig_path). Clears both undo/redo stacks. Shows confirm dialog.
516
+
-**Keyboard**: Ctrl+Z/Cmd+Z (undo), Ctrl+Y/Cmd+Y/Ctrl+Shift+Z (redo) — only fires when focus is not in INPUT/TEXTAREA/SELECT.
517
+
518
+
**When adding new mutation sites**: Always call `saveSnapshot()` before the mutation. For new text inputs, add a `focus` event listener that calls `saveSnapshot`.
519
+
504
520
### Key Implementation Notes
505
521
-**Must use `<script type="module">`** to import shared modules
**Automated:**`node tests/test-protocol-roundtrip.js` — 130 checks across 9 suites (v1+v2 parse/generate roundtrips). Run after any change to `protocol-yaml.js` or the data model.
543
+
**Automated:**`node tests/test-protocol-roundtrip.js` — 137 checks across 10 suites (v1+v2 parse/generate roundtrips + bug #55-58 regressions). Run after any change to `protocol-yaml.js` or the data model.
528
544
529
545
**Manual testing checklist (import a v2 YAML like `full_experiment_test.yaml` to populate):**
@@ -343,7 +343,7 @@ <h2>Walkthrough: create an optomotor protocol</h2>
343
343
344
344
<h2>Importing an existing protocol</h2>
345
345
346
-
<p>Click <strong>Import YAML</strong> in the top bar and select any v2 protocol <code>.yaml</code> file. The editor populates with all settings, conditions, plugins, and phases. You can modify and re-export.</p>
346
+
<p>Click <strong>Import YAML</strong> in the top bar and select any v2 protocol <code>.yaml</code> file. The editor populates with all settings, conditions, plugins, and phases. You can modify and re-export.<strong>Import is undoable</strong> — press <code>Ctrl+Z</code> to restore your previous experiment.</p>
347
347
348
348
<divclass="tip-box">
349
349
<strong>Try it:</strong> Import one of the example protocols from the maDisplayTools repository, such as <code>full_experiment_test.yaml</code> (9 conditions with camera + backlight plugins) to see a fully populated experiment.
@@ -374,12 +374,23 @@ <h2>Timeline controls</h2>
374
374
<li><code>Fit</code> scales the timeline to fit the window</li>
375
375
</ul>
376
376
377
+
<h2>Undo, redo, and reset</h2>
378
+
379
+
<p>Every edit is tracked in an undo history (up to 50 steps). Use <strong>Undo</strong> / <strong>Redo</strong> in the top bar, or the keyboard shortcuts below.</p>
380
+
<ulclass="key-list">
381
+
<li><strong>Undo / Redo buttons</strong> — in the top bar, disabled when the stack is empty</li>
382
+
<li><strong>Reset button</strong> — red button in the tab bar (right of Commands / Table). Clears all conditions, phases, and plugins back to defaults. <em>Settings</em> (name, author, arena, rig path) are kept. A confirmation dialog appears first. Reset also clears the undo/redo history.</li>
383
+
</ul>
384
+
377
385
<h2>Keyboard shortcuts</h2>
378
386
379
387
<ulclass="key-list">
388
+
<li><code>Ctrl+Z</code> / <code>Cmd+Z</code> Undo last edit (when not typing in a field)</li>
0 commit comments