- Curved: position at arc midpoint with rotation along curve
- Spur: center in trapezoid shape, not bounding box
- All: use dy offset instead of dominant-baseline for reliable centering
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Curved: position text at arc band midpoint, rotated along the curve
- Spur: center text in the trapezoid shape, not the bounding box
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
isTypeCompatible now matches by conveyance family, not just SVG file.
Conveyor family includes: conveyor, spur, curved_conv, induction, extendo.
Chute family includes: chute, tipper, curved_chute.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Parse labels like UL17_22_VFD into stacked text: "UL" / "17-22".
Bold black Arial, targets 14px but auto-scales down to fit with
consistent padding. Strips _VFD suffix, splits prefix from numbers.
If full text doesn't fit, strips the letter prefix.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix shift-drag: require 8px movement before locking axis, use strict
greater-than to avoid horizontal bias at small deltas
- Add PE shape-following outline for selection/hover/collision highlights
- Add PE shape-aware hit-testing (arrow + beam + receiver zones with margin)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The axis constraint was recalculated every mouse move, causing the symbol
to jerk between horizontal and vertical near the 45-degree diagonal.
Now the axis locks on first movement and stays locked until shift is released.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Also add previous 30x14 default to migration table so existing
PEs at either old size get updated on load.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Split SVG export into two passes: base conveyance symbols first, then
overlay devices rendered last so they appear on top in the exported SVG.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaces embedded SVG export (which used non-uniform scale transforms
that stretched strokes) with programmatic path/rect elements matching
the canvas renderer. Consistent 0.5px stroke at any size.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When loading saved state, symbols with known outdated default sizes
(e.g., photoeye 56x20) are migrated to current defaults (30x14).
Only exact matches of old defaults are migrated — user-resized
symbols keep their custom dimensions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Change all curved conveyor/chute SVGs from black to white fill
- Add CSS filter:invert(1) to palette thumbnails so white-fill symbols
remain visible on the dark palette background
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Cast EPC defaultWaypoints readonly tuple to EpcWaypoint[] in collision.ts
- Fix pdfjs render() call with canvas property
- Remove unused @ts-expect-error directive
- Add @types/node and type annotations for vite.config.ts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SVG-based rendering caused non-uniform stroke scaling when symbols were
stretched. Now conveyor, chute, tipper are drawn as canvas rects, and
extendo is drawn as a canvas path with fixed left bracket proportions.
All use consistent 0.5px stroke regardless of size.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Change all conveyance SVGs from black fill to white fill with black stroke
- Update programmatic rendering (curves, induction, spur) to white fill
- Replace PE 3-slice rendering with programmatic canvas paths for
consistent stroke width at any size
- Reduce PE default size from 56x20 to 30x14 to fit around conveyor devices
- Update SVG export to match new white fills
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
End box was 10×20 (tall/narrow perpendicular to line), now 20×10
(wide/squat along line), matching the start icon's landscape proportions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rotate the direction vector -90° for the right box in collision
detection and bounds calculation to match the perpendicular
rendering orientation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Right box stroke now uses EPC_CONFIG.lineWidth (1.5) instead of 0.3
- Right box positioned at -rb.w (backward) matching canvas renderer
- End box rotated 90° (perpendicular to line) in canvas, export, and hit-testing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace bounding-box hit test with proper shape-aware check that tests
proximity to line segments and oriented end boxes with 8px hit margin,
making the thin EPC much easier to select and drag.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Just expand w/h to cover the full waypoint extent without moving
sym.x/y. The origin-shifting math was causing the symbol to jump
in the direction of waypoint movement on mouse release.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
recalcEpcBounds now properly normalizes the bounding box to start at
(0,0) in local space, adjusting sym.x/y to compensate so the world
position and rotation center stay constant. Handles rotated symbols
by rotating the center offset into world space.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Updating sym.w/h mid-drag shifted the rotation center used by
toSymbolLocal, causing waypoints to jump. Now recalcEpcBounds
only runs on mouseup, keeping the coordinate transform stable.
Also removed the endpoint slide constraint that was causing
instability due to using the moving position as direction source.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- First/last waypoints now only extend/shorten along their segment direction,
preventing unwanted rotation of the end boxes
- Middle waypoints remain freely draggable for direction changes
- Add default middle waypoint so new EPCs have 3 control points
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Increase EPC line width from 0.4 to 1.5 for visibility
- Increase right box stroke from 0.3 to 1.0 to match left icon stroke
- Fix right box to extend behind last waypoint (like left icon), not forward
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All three now use identical 20x20 viewBox, 19x19 rect, font-size 8,
centered text with dominant-baseline="central". Updated symbol defs
to match the new 20x20 dimensions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reduced font sizes and used dominant-baseline="central" with y at
the vertical center so text is centered both horizontally and
vertically inside the box without overlapping the border.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a symbol is mirrored, negate the horizontal drag delta and swap
the left/right handle identity so resizing matches the visual direction.
Also fixes spur handle resize with mirrored spurs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove tabs, single unified device list grouped by type
- Drop on empty space → places new symbol with device ID
- Drop on existing symbol → assigns the device ID as label
- Drop-target highlight shown when hovering over existing symbols
- Always sorted ascending by device type
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Collision detection now mirrors spur trapezoid and induction arrow
vertices when the symbol is mirrored, matching the visual appearance.
Previously the collision polygon stayed unmirrored causing false
positives/misaligned collision zones.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Holding Shift while dragging locks movement to the dominant axis
(whichever has the larger delta from drag start). Works for both
single and multi-selection moves.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Each ID entry now shows a small SVG icon of its symbol type
- Device type name shown as subtle suffix
- Search results sorted ascending by device type, then by ID
- Search also matches device type name
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Blue dashed rectangle drawn while dragging
- Selects all visible symbols whose bounding box intersects the marquee
- Respects hidden symbols and hidden groups
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Right-click context menu: "Hide" option to hide individual symbols
- "Show All Hidden" appears in context menu when anything is hidden
- Top visibility bar with toggle chips for each device group
- Hidden symbols are excluded from rendering, hit testing, and SVG export
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Single-element SVGs: id/label/transform directly on the element (no <g>)
- SVGs with <g> group (dpm, diverter): keep the group, put id/label on it
- Multi-child SVGs without group (beacon): wrap in <g> with id/label
- Programmatic shapes (induction, curved, spur): single <path> with id
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add search filter to Devices tab in right dock
- Add IDs tab: flat list of unassigned device IDs, drag onto placed symbol to assign label
- Highlight drop target symbol with cyan glow during label drag
- Add labelDropTarget state and dropTarget theme entry
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Split collision.ts (707→549): extract distance.ts (pure math) and grid-snap.ts
- Fix curved conveyor/chute outline to match SVG viewBox geometry
- Draw curves programmatically with fixed 30px band width (no SVG stretching)
- Single resize handle for curves (was 2)
- Add .gitattributes for consistent line endings
- Make Vec2 type module-private
- Add mirror transform support in renderer
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>