Skip to content

feat(apollo-react): alignment guide prototype for canvas drag hints#956

Draft
dbacomputer wants to merge 4 commits into
mainfrom
manual-alignment-hinting
Draft

feat(apollo-react): alignment guide prototype for canvas drag hints#956
dbacomputer wants to merge 4 commits into
mainfrom
manual-alignment-hinting

Conversation

@dbacomputer

Copy link
Copy Markdown
Collaborator

Summary

Prototype exploring drag-alignment placement hints on the canvas — Figma/Sketch-style smart guides that appear when dragging a node near other nodes, so users have a visual cue for where to align/place it. There is currently no equivalent behavior in apollo-react's canvas (only a fixed grid-snap utility existed).

This is a review branch, not a finished feature. It ships one baseline implementation plus 9 additional comparison sub-pages so the team can pick a direction together.

What's here

New AlignmentGuides component under packages/apollo-react/src/canvas/components/AlignmentGuides/:

  • useAlignmentGuides hook: compares the dragged node's edges/center against every other node's on onNodeDrag, zoom-aware match threshold (screen px, not flow units)
  • AlignmentGuidesOverlay: renders matches as dashed guide lines using the existing --canvas-selection-indicator token
  • 10 Storybook pages under Apollo React → Canvas → Components → Canvas → AlignmentGuides:
    1. Alignment Guides (Baseline)
    2. Static Guide Preview
    3. Variant: Center vs Edge Styling
    4. Variant: Spacing Labels
    5. Variant: Highlighted Match
    6. Variant: Magnetic Snap
    7. Variant: Threshold Playground
    8. Variant: Multi-select Drag
    9. Variant: Grid-snap Interplay
    10. Variant: Equal-spacing Detection

Test plan

  • tsc --noEmit clean on the new files
  • biome lint clean on the new files
  • All 10 Storybook pages verified to render with no console errors
  • Core guide-matching math verified via standalone script against the exact story layout (edge vs. center classification, matched-node grouping, equal-spacing tolerance)
  • Live drag interaction: verify by hand in Storybook (this environment's browser automation can't reliably trigger xyflow's pointer-capture drag, so this needs a real mouse)
  • Team review of variants to pick a direction before this becomes a real feature

🤖 Generated with Claude Code

Storybook prototype for placement hints while dragging a node on the
canvas: a useAlignmentGuides hook compares the dragged node's edges and
center against every other node's on onNodeDrag, and
AlignmentGuidesOverlay renders the matches as dashed guide lines.
Visual only, nothing snaps. Demoed on a small workflow of nodes at
Templates/Canvas Alignment Guides.
…e story

Move the story under Components/Canvas (next to BaseCanvas, where canvas
behavior is documented) instead of Templates. Add a Static Guide Preview
story with hardcoded guides for visual QA without needing to drag.

The match threshold was a fixed 6 flow-space units, which shrinks to a
few screen pixels once the canvas is zoomed below 1x, making some axes
far harder to hit by mouse than others depending on layout. Switch to a
thresholdPx option (default 8) converted to flow-space via zoom, so
guides are equally reachable at any zoom level.
…view

Extend AlignmentGuideLine with kind (edge/center) and matchedNodeIds so
variants can distinguish match types and know which nodes a line
corresponds to, without changing the baseline's visual behavior.

Add five comparison sub-pages under Components/Canvas/AlignmentGuides,
each a self-contained fork so the "decided" hook/overlay API stays
stable while these are exploratory:

- Center vs Edge Styling: distinct color/dash for center-only matches
- Spacing Labels: px gap label alongside the guide line
- Highlighted Match: ring highlight on the node(s) a line matches
- Magnetic Snap: position snaps onto the guide instead of just showing it
- Threshold Playground: live slider to compare match distance/feel
…riants

Three more comparison sub-pages under Components/Canvas/AlignmentGuides:

- Multi-select Drag: guides compare the dragged selection's combined
  bounding box (outlined) against the rest of the canvas, not just one
  node in isolation
- Grid-snap Interplay: toggleable native snapToGrid alongside the
  guides, demonstrating the two compose without any special
  integration code (grid-snap quantizes position, guides just read it)
- Equal-spacing Detection: highlights both gaps with a shared "Npx
  equal" label when the dragged node's left/right (or top/bottom)
  neighbor gaps match within tolerance; scoped to immediate neighbors,
  not full n-way spacing across the canvas
Copilot AI review requested due to automatic review settings July 22, 2026 22:14
@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (PT)
apollo-design 🟢 Ready Preview, Logs Jul 22, 2026, 03:20:09 PM
apollo-docs 🟢 Ready Preview, Logs Jul 22, 2026, 03:20:09 PM
apollo-landing 🟢 Ready Preview, Logs Jul 22, 2026, 03:20:09 PM
apollo-vertex 🟢 Ready Preview, Logs Jul 22, 2026, 03:20:09 PM

@github-actions

Copy link
Copy Markdown
Contributor

Dependency License Review

  • 1950 package(s) scanned
  • ✅ No license issues found
  • ⚠️ 2 package(s) excluded (see details below)
License distribution
License Packages
MIT 1720
ISC 89
Apache-2.0 55
BSD-3-Clause 27
BSD-2-Clause 23
BlueOak-1.0.0 8
MPL-2.0 4
MIT-0 3
CC0-1.0 3
MIT OR Apache-2.0 2
(MIT OR Apache-2.0) 2
Unlicense 2
LGPL-3.0-or-later 1
Python-2.0 1
CC-BY-4.0 1
(MPL-2.0 OR Apache-2.0) 1
Unknown 1
Artistic-2.0 1
(WTFPL OR MIT) 1
(BSD-2-Clause OR MIT OR Apache-2.0) 1
CC-BY-3.0 1
0BSD 1
(MIT OR CC0-1.0) 1
MIT AND ISC 1
Excluded packages
Package Version License Reason
@img/sharp-libvips-linux-x64 1.2.4 LGPL-3.0-or-later LGPL pre-built binary, not linked
khroma 2.1.0 Unknown MIT per GitHub repo, missing license field in package.json

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a prototype “smart guides” system to the apollo-react canvas to visualize alignment opportunities (edge/center) while dragging nodes, plus a set of Storybook variants to compare potential UX directions.

Changes:

  • Introduces useAlignmentGuides + computeAlignmentGuides for guide-line detection during drag.
  • Adds AlignmentGuidesOverlay to render guide lines as a viewport-aware overlay.
  • Adds a large Storybook sandbox with multiple variants (baseline + exploratory behaviors like snap, spacing labels, multi-select, equal spacing).

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/apollo-react/src/canvas/components/index.ts Exports the new AlignmentGuides module from the canvas components barrel.
packages/apollo-react/src/canvas/components/AlignmentGuides/useAlignmentGuides.ts Core guide matching logic + hook wiring for onNodeDrag/onNodeDragStop.
packages/apollo-react/src/canvas/components/AlignmentGuides/index.ts Public barrel exports for the AlignmentGuides module.
packages/apollo-react/src/canvas/components/AlignmentGuides/AlignmentGuidesOverlay.tsx Renders computed guides in screen space using viewport transform.
packages/apollo-react/src/canvas/components/AlignmentGuides/AlignmentGuides.types.ts Types for guide lines and computed node bounds.
packages/apollo-react/src/canvas/components/AlignmentGuides/AlignmentGuides.stories.tsx Storybook “comparison suite” for multiple alignment-guide variants.

Comment on lines +46 to +66
for (const other of others) {
for (const draggedValue of draggedValues) {
otherValues(other).forEach((otherValue, otherIndex) => {
if (Math.abs(draggedValue - otherValue) > threshold) return;
const key = Math.round(otherValue);
const [oStart, oEnd] = otherRange(other);
const start = Math.min(draggedRange[0], oStart);
const end = Math.max(draggedRange[1], oEnd);
const isCenterMatch = otherIndex === 1;
const existing = matchesByPosition.get(key);
const nodeIds = existing?.nodeIds ?? new Set<string>();
nodeIds.add(other.id);
matchesByPosition.set(key, {
start: existing ? Math.min(existing.start, start) : start,
end: existing ? Math.max(existing.end, end) : end,
nodeIds,
isCenterOnly: existing ? existing.isCenterOnly && isCenterMatch : isCenterMatch,
});
});
}
}
Comment on lines +7 to +15
export function toBounds(node: Node): NodeBounds {
const width = node.measured?.width ?? node.width ?? DEFAULT_NODE_SIZE;
const height = node.measured?.height ?? node.height ?? DEFAULT_NODE_SIZE;
const x1 = node.position.x;
const y1 = node.position.y;
const x2 = x1 + width;
const y2 = y1 + height;
return { id: node.id, x1, y1, x2, y2, cx: (x1 + x2) / 2, cy: (y1 + y2) / 2 };
}
Comment on lines +81 to +90
export function computeAlignmentGuides(
dragged: NodeBounds,
others: NodeBounds[],
threshold: number
): AlignmentGuideLine[] {
return [
...collectAxisGuides(dragged, others, threshold, 'vertical'),
...collectAxisGuides(dragged, others, threshold, 'horizontal'),
];
}
@github-actions

Copy link
Copy Markdown
Contributor

📊 Coverage + size by package

Per-package coverage and bundle size on this PR. New-line coverage = of the source lines this PR adds or changes, the % hit by tests.

Package Coverage New-line coverage Packed (gzip) Unpacked vs main
@uipath/apollo-core 9.0% 43.84 MB 57.45 MB ±0
@uipath/apollo-react 37.1% 0.0% (0/233) 7.40 MB 28.32 MB +1.9 KB
@uipath/apollo-wind 41.0% 397.6 KB 2.58 MB −3 B
@uipath/ap-chat 85.8% 43.43 MB 55.93 MB +1.0 KB

"Coverage" is each package's own coverage.include scope (e.g. apollo-core instruments only scripts/). "Packed"/"Unpacked" come from npm pack --dry-run and only cover built packages — "—" means not measured this run (package not affected / not built). "vs main" is the packed (gzipped) delta against the last successful main build (the package-sizes artifact from the Release workflow); "—" there means no main baseline was available this run. The baseline is main's latest build, not this PR's exact merge-base, so it includes any drift since the branch diverged. Packages with no vitest config are omitted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pkg:apollo-react size:XXL 1,000+ changed lines.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants