Skip to content

Add node/edge editing UI: rename, delete, shape picker, add node#19

Merged
bobwhitelock merged 20 commits into
mainfrom
claude/awesome-hypatia-StPaw
Jun 2, 2026
Merged

Add node/edge editing UI: rename, delete, shape picker, add node#19
bobwhitelock merged 20 commits into
mainfrom
claude/awesome-hypatia-StPaw

Conversation

@bobwhitelock

Copy link
Copy Markdown
Owner

Summary

Adds comprehensive node and edge editing capabilities to the D2 editor with new UI popups and action menu options. Users can now rename nodes, delete nodes/edges, change node shapes, edit edge labels, and add new nodes directly from the editor interface.

Key Changes

  • Action menu expansion: Added new buttons for rename, shape picker, delete, and edge label editing with appropriate styling (danger button for delete)
  • Rename popup: New modal for renaming nodes with keyboard support (Enter to confirm, Escape to cancel)
  • Edge label editing: Dedicated popup to add/edit/remove labels on connections
  • Shape picker popup: Interactive grid showing all 22 D2 shapes with live previews, allowing users to change node shapes
  • Add node popup: New toolbar button and modal to quickly add nodes with optional shape selection
  • Source mutation functions: Implemented deleteNode(), deleteEdge(), renameNode(), editEdgeLabel(), addNode(), and changeNodeShape() to safely modify the D2 source code
  • AST utilities: Extracted reusable helper functions (parseD2Range(), segVal(), findMapKeyAt(), buildEdgeSegLookup(), lineRangeInSrc(), findNodeTargetObj()) for AST traversal and source manipulation
  • Menu context awareness: Action menu now shows different buttons based on whether a shape or connection is selected
  • UI polish: Proper positioning of popups relative to viewport, keyboard navigation, and toast notifications for user feedback

Implementation Details

  • Shape previews are lazy-loaded on first use to avoid blocking the editor
  • All source edits are tracked with undo support via pushUndo()
  • Edge identification uses fully-qualified names with occurrence indices to handle multiple edges between the same nodes
  • Node references are matched against the compiled graph's object references for accurate source location
  • Popups intelligently reposition to stay within viewport bounds

https://claude.ai/code/session_0188HqZ2PyajbSEGNhNmg9Tb

github-actions Bot added a commit that referenced this pull request Jun 2, 2026
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Deploy Preview

Preview URL: https://tools.bobwhitelock.co.uk/pr-19/

Deployed commit: 4915b98

@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Deploy Preview

Preview URL: https://tools.bobwhitelock.co.uk/pr-19/

Deployed commit: 37e126f

github-actions Bot added a commit that referenced this pull request Jun 2, 2026
claude added 3 commits June 2, 2026 09:16
- Delete Node: removes node declaration and all connected edges
- Delete Edge: click edge to open menu, then delete
- Rename Node: renames leaf segment in all references (works for
  both dot-notation and block-scope nodes)
- Edit Edge Label: set or clear the label on any edge
- Add Node: + button opens popup with name input and full shape picker
- Change Shape: shape picker with lazy-loaded SVG previews of all 22
  D2 shapes (callout through text, sorted alphabetically)

Also:
- Extracts parseD2Range/segVal/findMapKeyAt/buildEdgeSegLookup/
  lineRangeInSrc from moveNode to module scope for reuse
- Edges now open the action menu (with Edit Label + Delete) instead
  of just showing a key readout
- CLAUDE.md: note CI + deploy wait before checking preview URL

https://claude.ai/code/session_0188HqZ2PyajbSEGNhNmg9Tb
Edges now open the action menu directly, so the old key-readout
shortcut is no longer called.

https://claude.ai/code/session_0188HqZ2PyajbSEGNhNmg9Tb
- ensureShapePreviews: render shapes sequentially (was Promise.all which
  caused all 22 concurrent D2 WASM renders to fail)
- changeNodeShape: fix regex /\S+/ → /[^\s}]+/ so it stops matching the
  closing brace when changing shape on a block-scope node
- renameNode: when declaration has a label (e.g. `api: API Server`),
  update the label text instead of the key identifier; fall back to key
  rename only when no label exists
- showRenamePopup: pre-fill input with current label text (not key)

https://claude.ai/code/session_0188HqZ2PyajbSEGNhNmg9Tb
@bobwhitelock
bobwhitelock force-pushed the claude/awesome-hypatia-StPaw branch from 37e126f to c50178a Compare June 2, 2026 09:16
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Deploy Preview

Preview URL: https://tools.bobwhitelock.co.uk/pr-19/

Deployed commit: c50178a

github-actions Bot added a commit that referenced this pull request Jun 2, 2026
Instead of rendering shapes lazily at runtime (which required D2 WASM
to run 22 extra compile+render cycles in the browser), pre-render them
at build time and load from a companion file.

- tools/d2-shape-previews.js: pre-rendered SVGs for all 22 D2 shapes
  as an ES module (imported by d2-editor.html at module load time)
- tools/generate-shape-previews.mjs: generator script to regenerate
  d2-shape-previews.js if shapes change
- d2-editor.html: import SHAPE_PREVIEW_SVGS from the companion file;
  remove ensureShapePreviews(), shapePreviewSvgs state, and all
  lazy-loading code — shape picker now opens instantly

https://claude.ai/code/session_0188HqZ2PyajbSEGNhNmg9Tb
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Deploy Preview

Preview URL: https://tools.bobwhitelock.co.uk/pr-19/

Deployed commit: 2c20acc

github-actions Bot added a commit that referenced this pull request Jun 2, 2026
node_modules/, package.json, and package-lock.json were created locally
when generating d2-shape-previews.js and should not be tracked.

https://claude.ai/code/session_0188HqZ2PyajbSEGNhNmg9Tb
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Deploy Preview

Preview URL: https://tools.bobwhitelock.co.uk/pr-19/

Deployed commit: a37d049

github-actions Bot added a commit that referenced this pull request Jun 2, 2026
Shape buttons now show only the SVG preview (name available via tooltip).
The image shape gets a picture-icon placeholder SVG since it requires an
icon URL and cannot be rendered as a standalone D2 diagram.

https://claude.ai/code/session_0188HqZ2PyajbSEGNhNmg9Tb
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Deploy Preview

Preview URL: https://tools.bobwhitelock.co.uk/pr-19/

Deployed commit: 6883047

github-actions Bot added a commit that referenced this pull request Jun 2, 2026
… + image shape

Layout:
- Shape picker and add-node popup are now much wider (up to 520px) with
  larger shape previews (82×62px vs 54×38px) and no button borders
  (outline used for active state instead)
- Mobile (≤600px): both popups take over the full screen
- Added header with title and ✕ close button to both popups
- positionPopupNearEl skips JS positioning on mobile (CSS handles it)

Undo fix:
- pushUndo() was skipping the save when undoStack was empty because it
  compared editor.value against snapValue (always equal after commitSnap)
  — now only deduplicates against the actual last stack entry

image shape:
- Selecting image in the shape picker now shows a toast explaining it
  requires an "icon" field, rather than applying a change that causes
  a D2 render error

https://claude.ai/code/session_0188HqZ2PyajbSEGNhNmg9Tb
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Deploy Preview

Preview URL: https://tools.bobwhitelock.co.uk/pr-19/

Deployed commit: ad6f784

github-actions Bot added a commit that referenced this pull request Jun 2, 2026
- Crop D2's built-in 101px padding from SVG viewBox in buildShapeGrid
  so each shape fills its button (85px crop each side, xMidYMid meet)
- Replace image shape toast with a proper icon URL popup: shows an input
  pre-linked to icons.terrastruct.com, applies `shape: image` +
  `icon: <url>` to the node declaration on confirm
- Added .d2-image-icon-popup styles (reuses existing popup patterns)
- hideImageIconPopup() wired into resetInteraction()

https://claude.ai/code/session_0188HqZ2PyajbSEGNhNmg9Tb
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Deploy Preview

Preview URL: https://tools.bobwhitelock.co.uk/pr-19/

Deployed commit: ec0549a

github-actions Bot added a commit that referenced this pull request Jun 2, 2026
- Shape buttons now use white backgrounds so D2 shapes render naturally
  (removes fill:none/stroke overrides that made shapes invisible on dark)
- Active state uses light green tint + accent outline instead of dark bg
- viewBox crop now only applies to D2's large SVGs (>200 units) to
  avoid over-cropping the small image placeholder (80×60 viewBox)

https://claude.ai/code/session_0188HqZ2PyajbSEGNhNmg9Tb
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Deploy Preview

Preview URL: https://tools.bobwhitelock.co.uk/pr-19/

Deployed commit: ceaab01

github-actions Bot added a commit that referenced this pull request Jun 2, 2026
D2 SVGs have a nested structure: outer <svg viewBox="0 0 W H"> wraps an
inner <svg viewBox="-101 -101 W H">. The inner SVG fills 100%×100% of
its parent so the outer viewBox has no visual effect — the crop must
target the inner SVG.

For D2 SVGs (detected by negative x0/y0), crop the inner viewBox from
"-101 -101 W H" to "-20 -20 (content_w+40) (content_h+40)" — strips
D2's 101px padding, leaving 20px breathing room around the shape.

https://claude.ai/code/session_0188HqZ2PyajbSEGNhNmg9Tb
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Deploy Preview

Preview URL: https://tools.bobwhitelock.co.uk/pr-19/

Deployed commit: fce276d

github-actions Bot added a commit that referenced this pull request Jun 2, 2026
- class shape now renders with sample fields/method so it's visually
  distinct (was empty box); sql_table similarly gets sample rows
- showShapePickerPopup now calls getCurrentShape() to pre-select the
  node's current shape (defaults to rectangle if none set)

https://claude.ai/code/session_0188HqZ2PyajbSEGNhNmg9Tb
github-actions Bot added a commit that referenced this pull request Jun 2, 2026
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Deploy Preview

Preview URL: https://tools.bobwhitelock.co.uk/pr-19/

Deployed commit: fad09eb

'class' is a reserved keyword in D2 — using it as a node name produces
an empty SVG. Using 'MyClass' renders the full UML class box with
fields and methods.

https://claude.ai/code/session_0188HqZ2PyajbSEGNhNmg9Tb
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Deploy Preview

Preview URL: https://tools.bobwhitelock.co.uk/pr-19/

Deployed commit: 8ba9782

github-actions Bot added a commit that referenced this pull request Jun 2, 2026
github-actions Bot added a commit that referenced this pull request Jun 2, 2026
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Deploy Preview

Preview URL: https://tools.bobwhitelock.co.uk/pr-19/

Deployed commit: 4f37e14

- showEdgeLabelPopup now reads the current label from the AST and
  pre-fills the input so users can edit rather than retype
- tagShapeGroups adds invisible 20px-wide stroke overlay paths on
  connection <path> elements, making thin edges much easier to tap

https://claude.ai/code/session_0188HqZ2PyajbSEGNhNmg9Tb
github-actions Bot added a commit that referenced this pull request Jun 2, 2026
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Deploy Preview

Preview URL: https://tools.bobwhitelock.co.uk/pr-19/

Deployed commit: 8850be9

On narrow screens the horizontal button row was overflowing the viewport.
On ≤600px the menu now uses flex-column so all buttons are visible.

https://claude.ai/code/session_0188HqZ2PyajbSEGNhNmg9Tb
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Deploy Preview

Preview URL: https://tools.bobwhitelock.co.uk/pr-19/

Deployed commit: f4ec5f3

github-actions Bot added a commit that referenced this pull request Jun 2, 2026
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Deploy Preview

Preview URL: https://tools.bobwhitelock.co.uk/pr-19/

Deployed commit: cc60ec3

github-actions Bot added a commit that referenced this pull request Jun 2, 2026
- All three label popups now show a hint line ("Label edge", "Label node",
  "Label edge (blank to remove)") so it's clear what is being labelled
- Connect flow: "Label edge" popup now appears at the midpoint between
  the source and target nodes instead of below the target node
- Popup layout changed to column so the hint sits above the input row

https://claude.ai/code/session_0188HqZ2PyajbSEGNhNmg9Tb
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Deploy Preview

Preview URL: https://tools.bobwhitelock.co.uk/pr-19/

Deployed commit: f2991e8

github-actions Bot added a commit that referenced this pull request Jun 2, 2026
Was using segs[0] (the outermost path segment) to find the declaration,
so for network.subnet it found network's map_key and deleted the whole
parent block. Now uses segs[kpi] — the segment that corresponds to the
actual node being deleted — matching the logic in findNodeTargetObj.

https://claude.ai/code/session_0188HqZ2PyajbSEGNhNmg9Tb
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Deploy Preview

Preview URL: https://tools.bobwhitelock.co.uk/pr-19/

Deployed commit: 88eea48

github-actions Bot added a commit that referenced this pull request Jun 2, 2026
- Move edit-label button before delete so Delete is last (matching node menu)
- Set edge label button text to "Rename" if edge has a label, "Name" if not
- Fix invisible hit overlay: use pointer-events:all and rgba(0,0,0,0) stroke
  (transparent wasn't reliably hittable), increase stroke-width to 30px

https://claude.ai/code/session_0188HqZ2PyajbSEGNhNmg9Tb
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Deploy Preview

Preview URL: https://tools.bobwhitelock.co.uk/pr-19/

Deployed commit: 1cffa18

github-actions Bot added a commit that referenced this pull request Jun 2, 2026
@bobwhitelock
bobwhitelock merged commit fe8560b into main Jun 2, 2026
2 checks passed
github-actions Bot added a commit that referenced this pull request Jun 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants