Layout Guides Plugin

Overview

@designful/layout-guides-plugin is a standalone runtime plugin for frame layout guides.

It is not a ui-plugin-host modal plugin. You enable it through editor.pluginManager, then use editor.layoutGuides for API access.

What It Adds

  • multiple guides per frame
  • square, columns/stretch, columns/fixed, rows/stretch, rows/fixed
  • page-scoped presets
  • global show/hide for all guides on the current page
  • copy/paste between frames
  • history-safe replay for frame metadata and plugin document state

Why It Stays Decoupled

  • @designful/editor-core does not statically import the package
  • runtime dependencies stay limited to editor contracts, plugin-base, types, and konva
  • API actions and context-menu actions share the same validation and command path
  • package-boundary tests prevent imports from unrelated Designful modules

Persistence

Frame guide collections are stored on each frame:

frame.metadata.plugins.layoutGuides

Page-scoped plugin state is stored on current page metadata:

page.metadata.layoutGuidesPlugin

That page state contains:

  • globalVisible
  • presets

Docs Routes

Why Aren't My Layout Guides Showing?

The plugin has one shared visibility rule source, so the runtime behavior and docs stay aligned. Guides stay hidden when:

  • all guides on the frame are toggled off
  • global guide visibility is off for the page
  • the current target is not a frame
  • the frame has any non-zero rotation

The package exports resolveLayoutGuideVisibilityReason(...), and the plugin emits the same result on layoutGuides:removed as a reason payload.

Demo Checklist

  1. Open the live demo and select the root frame.
  2. Add a guide through the API panel, then add another guide through the context-menu path.
  3. Save a preset, switch to the nested frame, and apply that preset there.
  4. Toggle global visibility, then undo and redo to verify state replay.

Selection Behavior In The Live Demo

  • Clicking the nested frame's blank area selects the nested frame, even when the click does not land on the inner rect.
  • Keeping the root frame selected does not disable next-level hover feedback; direct children still render hover outlines.
  • Hovering the nested frame blank area still resolves to the nested frame hover outline, even when Konva does not return a child hit target.
  • The live demo listens to editor selection:changed and scope:changed events, so the sidebar and runtime snapshot always reflect the editor's real selection state.
  • Direct nested-frame drag uses the same alignment snap path as the left-side rect in the demo.
  • Selected-frame threshold drags keep alignment guides visible during nested-frame snapping instead of clearing them from the stage sync path.
  • Descendant-hit fallback frame drags still suppress snapping until the frame itself owns the drag lifecycle, which prevents descendant streams from producing stale snap guides.