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

  1. Keep Tab behavior unchanged (open/focus edit flow as today).
  2. Use backtick (`) to cycle quick-add suggestions (canvas context only).
  3. Use one dynamic quick-add button (not two).
  4. Always show a best-guess suggestion (no hiding/disable mode).
  5. 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

  1. Single fixed quick-add button at start of toolbar.
  2. Button contents:
  • Suggested device icon + short label.
  • Tooltip: "Quick Add: \<Device\>" and "Press \` to cycle".
  1. Cycle feedback:
  • 3 small dots under/near button; active dot marks relative position.
  1. 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

  1. Backtick is acceptable across target user keyboards; fallback can be added later via shortcut settings.
  2. Quick-add remains additive to existing full toolbar.
  3. Confidence is not surfaced as a warning state; usefulness is driven by deterministic ranking and fast cycling.