@@ -438,14 +438,18 @@ The following UI improvements were made on 2026-02-02 and need testing on GitHub
438438
439439## Experiment Designer
440440
441- ### Architecture (v0.6 — 2026-04-01 )
442- - 3-zone layout: settings panel (280px left), editor with tab bar (flex right), timeline (bottom strip )
441+ ### Architecture (v0.8 — 2026-04-08 )
442+ - 3-zone layout: settings panel (280px left), editor with tab bar (flex right), filmstrip with lane view (bottom)
443443- Single ` <script type="module"> ` importing from ` js/arena-configs.js ` , ` js/protocol-yaml.js ` , ` js/plugin-registry.js `
444444- Data model: ` experiment ` object with ` experiment_info ` , ` arena_info ` , ` rig_path ` , ` plugins[] ` , ` experiment_structure ` , phases with ` commands[] ` , and ` conditions[] ` with ` commands[] `
445445
446446### Shared Modules
447447- ** ` 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+ - ` yamlStr(str) ` — double-quotes strings for YAML
449+ - ` yamlPath(str) ` — single-quotes paths (no escape sequences, safe for Windows backslashes)
448450- ** ` js/plugin-registry.js ` ** — Built-in plugin definitions (LEDControllerPlugin: 7 commands, BiasPlugin: 6 commands), controller command definitions (6 commands), lookup functions for dropdown population. Dual-export.
451+ - Plugin config fields use ` rigDefined: true ` for fields already in rig YAML (ip, port) — these are NOT auto-included in exports
452+ - ` createPluginEntry() ` skips fields with empty string defaults
449453
450454### Data Model (v2 commands)
451455Conditions and phases use ** command arrays** as the primary data model:
@@ -470,37 +474,53 @@ experiment.rig_path = "./configs/rigs/test_rig_1.yaml"
470474```
471475Helper functions: ` cmdFindTrialParams(commands) ` , ` condGetDuration(cond) ` , ` condGetPattern(cond) ` , ` phaseGetDuration(phase) ` .
472476
477+ ** Shared command helpers** (used by Commands tab, Table view, and phase editor):
478+ - ` buildAddCommandOptions() ` — returns HTML ` <option> ` /` <optgroup> ` string for add-command dropdowns
479+ - ` createCommandFromSelectValue(value) ` — parses ` "controller:trialParams" ` / ` "wait:wait" ` / ` "plugin:backlight:setRedLEDPower" ` into a command object
480+ - ` createPluginCommand(pluginName, commandName) ` — builds plugin command with default params from registry schema
481+
473482### YAML Export (v2)
474483- Generates protocol v2 via ` generateV2Protocol() ` from ` js/protocol-yaml.js `
475484- ` rig: ` field replaces inline ` arena_info `
476- - ` plugins: ` section lists enabled plugins with class/config
485+ - ** Paths use single quotes** via ` yamlPath() ` (pattern_library, rig, script_path) — prevents Windows backslash escape issues
486+ - ` plugins: ` section lists enabled plugins with class/config — only user-set config values are exported (empty = omit)
477487- Conditions export full command arrays including plugin commands with params
478488- Phases export command arrays directly
479489
480490### Editor Tabs
481- Three tabs in the right panel, all views of the same data model:
482- 1 . ** Visual** — Command card editor with color-coded cards (green=controller, gray=wait, blue=plugin), inline field editing, "Add Command" dropdown from plugin registry
483- 2 . ** Table** — Spreadsheet view with collapsible sections (pretrial, conditions with ITI, posttrial), type badges, param display
484- 3 . ** Timeline** — SVG multi-lane visualization per condition: controller spans, plugin event markers, wait bars, time axis. Read-only with hover tooltips.
491+ Two tabs in the right panel, both views of the same data model:
492+ 1 . ** Commands** (was "Visual" in v0.6) — Command card editor with color-coded cards (green=controller, gray=wait, blue=plugin), inline field editing, "Add Command" dropdown, up/down reorder arrows, delete buttons
493+ 2 . ** Table** — Fully editable spreadsheet view with collapsible sections, inline field editing, add/move/delete commands, condition reorder/remove, Expand All/Collapse All buttons
494+
495+ ### Bottom Filmstrip + Lane View
496+ The bottom timeline area is a unified scroll container:
497+ - ** Block strip** : Colored blocks (green=condition, gray=phase, dark=ITI) with drag-to-reorder conditions
498+ - ** Lane view** : Always-visible SVG showing controller spans (green bars), plugin events (blue dots), and wait bars (gray) across all blocks
499+ - ** Fixed lane labels** : Left column (70px) with lane names stays visible during scroll
500+ - Block strip and lane SVG share the same scroll parent for perfect alignment
501+ - Block widths use ` Math.max(48, duration * pxPerSecond) ` — lane SVG must match this + account for 2px CSS gap between blocks
502+ - Clicking any filmstrip block switches to the Commands tab
485503
486504### Key Implementation Notes
487505- ** Must use ` <script type="module"> ` ** to import shared modules
488506- Mode 2 (Constant Rate): ` gain ` fixed at 0, ` frame_rate ` editable
489507- Mode 4 (Closed-Loop): ` frame_rate ` fixed at 0, ` gain ` editable
490- - ` handleTrackClick ` must explicitly call ` renderTimelineView() ` and ` renderTableView() ` after ` renderEditor() ` to keep all tabs in sync
491- - Timeline ` computeLaneData() ` : trialParams fires controller autonomously (doesn't advance clock), wait advances clock, plugin commands are instantaneous
508+ - ` handleTrackClick ` calls ` switchEditorTab('commands') ` then ` renderEditor() ` — always shows Commands tab on block click
509+ - ` computeLaneData() ` : trialParams fires controller autonomously (doesn't advance clock), wait advances clock, plugin commands are instantaneous
510+ - ** Phase initialization must deep-clone** : ` { include: false, commands: JSON.parse(JSON.stringify(DEFAULT_PHASE.commands)) } ` — shallow spread shares the commands array reference
511+ - Plugin config uses ` setPluginConfig() ` helper that deletes empty values and removes config object when empty
492512
493513### Related Files
494- - ` experiment_designer.html ` — Main tool (v0.6 )
495- - ` experiment_designer_quickstart.html ` — Step-by-step guide
496- - ` js/protocol-yaml.js ` — Shared YAML parser/generator
497- - ` js/plugin-registry.js ` — Plugin definitions + command schemas
514+ - ` experiment_designer.html ` — Main tool (v0.8 )
515+ - ` experiment_designer_quickstart.html ` — Step-by-step guide (v0.8)
516+ - ` js/protocol-yaml.js ` — Shared YAML parser/generator (added ` yamlPath ` )
517+ - ` js/plugin-registry.js ` — Plugin definitions + command schemas (updated defaults)
498518- ` tests/test-protocol-roundtrip.js ` — 130 CI checks (9 suites, v1+v2)
499519- ` tests/generate-roundtrip-protocol.js ` — YAML + manifest generator for MATLAB
500520- ` tests/fixtures/v2_*.yaml ` — V2 YAML test fixtures from maDisplayTools
501521- ` docs/experiment-designer-v06-testing.md ` — Manual testing checklist
502522- ` docs/protocol-roundtrip-testing.md ` — Roundtrip testing architecture
503- - GitHub Issue : [ #33 ] ( https://github.com/reiserlab/webDisplayTools/issues/33 )
523+ - GitHub Issues : [ #33 ] ( https://github.com/reiserlab/webDisplayTools/issues/33 ) , [ # 53 ] ( https://github.com/reiserlab/webDisplayTools/issues/53 ) (hover tooltips), [ # 54 ] ( https://github.com/reiserlab/webDisplayTools/issues/54 ) (undo/redo )
504524
505525## Planning Best Practices
506526
0 commit comments