Plan: Fixed Quick-Add Slot With Dynamic Suggestion (Backtick Cycle, Tab Preserved)
Summary
Implement a single fixed quick-add button in the toolbar that always stays in the same location and inserts the currently suggested device. Suggestion cycling uses the backtick key (`) in canvas context, preserving existing Tab behavior for edit-panel access. The suggestion engine will be performant and deterministic by recomputing only on target changes (selection/last-inserted), not on every node/edge mutation.
Final Product Decisions
- Keep Tab behavior unchanged (open/focus edit flow as today).
- Use backtick (`) to cycle quick-add suggestions (canvas context only).
- Use one dynamic quick-add button (not two).
- Always show a best-guess suggestion (no hiding/disable mode).
- Show lightweight cycle feedback with a persistent 3-dot indicator.
Public/Internal Interface Changes
ComponentToolbarProps
Add a single grouped prop (avoid prop explosion):
interface QuickAddToolbarConfig {
type: string | null; // current suggested UI type
onAdd: (shiftHeld?: boolean) => void; // add current suggestion
onCycle: (direction: 1 | -1) => void; // keyboard or click cycling
cycleIndex: number; // 0..2 for dot indicator
cycleSize: number; // up to 3 shown dots
label: string; // display label for tooltip/aria
}
interface ComponentToolbarProps {
onComponentClick?: (type: string, shiftHeld: boolean) => void;
disabledComponents?: Record<string, string>;
quickAdd?: QuickAddToolbarConfig;
}useCanvasKeyboard additions
quickAdd?: { cycle: (direction: 1 | -1) => void };- Preserve existing Tab behavior.
- Add backtick handler in canvas context for cycling.
- Keep existing input/contenteditable guards.
Architecture and Data Flow
1. New hook: use-quick-add-suggestion.ts
- Inputs:
- selectedNodeId, lastInsertedId
- stable refs to localNodes, localEdges
- disabledComponents
- validateConnection (injected dependency from useConnectionValidation)
- Outputs:
- selectedType, selectedLabel
- cycleIndex, cycleSize
- cycle(direction), addCurrent()
- Internal state:
- candidatesRef + selectedIndex
- state updates only when selected type or visible cycle metadata actually changes
2. Reuse existing pure policy utilities directly
- findSmartTarget, isSensibleAutoConnection from
src/lib/sld/auto-connect-policy.ts - Do not extract logic from use-auto-build.ts.
- Connection feasibility for ranking will call injected validateConnection via a pure helper in the new hook.
3. Candidate model
Candidate list matches current toolbar-addable UI types:
- utility, generator, bus, transformer, cable
- breaker-hv, breaker-lv, switch, fuse
- motor_control_device, load, motor
Motor-control subtypes are a single candidate (motor_control_device) to match existing toolbar semantics.
Ranking and Performance Strategy
1. Recompute triggers (high-impact only)
- selectedNodeId, lastInsertedId, disabledComponents.
- Not directly on every localNodes/localEdges change.
2. Fresh graph data without rerender churn
- Maintain nodesRef/edgesRef synced each render.
- Ranking uses refs at recompute time.
3. Ranking steps
- Resolve smart target from selected/last-inserted via findSmartTarget.
- Score each enabled candidate:
- connectable (via handle-aware validation check)
- sensible (policy utility)
- skipPenalty (if smart-target walk skipped nodes)
- Sort deterministically by score + fixed tie-break priority.
- Keep top 5 internal candidates; expose 3-dot indicator for current window.
4. No-target fallback (explicit)
- On empty graph or no valid target, default deterministic order begins with bus, then utility, then generator, etc.
- This guarantees quick-add is always usable and predictable.
5. Queue/race resilience
- After handleToolbarClick, schedule suggestion refresh from latest refs (microtask/frame) so rapid add + cycle uses current graph snapshot.
- Cycling itself never mutates graph; it only changes selected candidate index.
Keyboard Behavior Spec
Existing behavior retained
- Tab still triggers edit-panel path in canvas context (current implementation).
New
- Backtick (`) cycles quick-add forward in canvas context.
- Shift+` cycles backward.
Guards
- Ignore when typing in input/textarea/contenteditable.
- Ignore when focus is outside canvas/document body context.
UI/UX Details
- Single fixed quick-add button at start of toolbar.
- Button contents:
- Suggested device icon + short label.
- Tooltip: "Quick Add: \<Device\>" and "Press \` to cycle".
- Cycle feedback:
- 3 small dots under/near button; active dot marks relative position.
- Motion:
- 100ms icon/label crossfade on candidate change.
File-by-File Implementation Plan
1. use-quick-add-suggestion.ts (new)
Path: src/app/(application)/app/(workspace)/sld-editor/_components/canvas/hooks/use-quick-add-suggestion.ts
Implement ranking, candidate index management, ref-based graph reads, stable callbacks.
2. component-toolbar.tsx
Path: src/app/(application)/app/(workspace)/sld-editor/_components/component-toolbar.tsx
Add quickAdd prop handling and single dynamic button UI + dots + tooltip.
3. sld-canvas.tsx
Path: src/app/(application)/app/(workspace)/sld-editor/_components/sld-canvas.tsx
- Instantiate suggestion hook with validateConnection and graph refs.
- Wire quick-add onAdd to existing handleToolbarClick.
- Pass quickAdd config into toolbar.
4. use-canvas-keyboard.ts
Path: src/app/(application)/app/(workspace)/sld-editor/_components/canvas/hooks/use-canvas-keyboard.ts
Add backtick/shift+backtick cycling; preserve existing Tab logic.
5. Tests
component-toolbar.test.tsx: quick-add rendering, add action, dot indicator, disabled safety.use-canvas-keyboard.test.ts: backtick cycling in canvas, no cycling while typing, Tab behavior unchanged.use-quick-add-suggestion.test.ts(new): deterministic ranking, empty-graph fallback, disabled filtering, wraparound, race-safe refresh.
6. Docs
docs/sld-editor/canvas-operations/keyboard-shortcuts.md
Quick-Add Speed Path
- Repeated clicks on fixed quick-add adds expected chain.
- If current type becomes disabled, next enabled type auto-selected.
Assumptions and Defaults
- Backtick is acceptable across target user keyboards; fallback can be added later via shortcut settings.
- Quick-add remains additive to existing full toolbar.
- Confidence is not surfaced as a warning state; usefulness is driven by deterministic ranking and fast cycling.