Skip to content

PMM Workflow Designer: bug fixes, zoom, XSS hardening, and branching starter flow - #153

Open
thanatornpodgemini-collab wants to merge 2 commits into
alyssaxuu:masterfrom
thanatornpodgemini-collab:pmm-workflow-designer-improvements
Open

thanatornpodgemini-collab wants to merge 2 commits into
alyssaxuu:masterfrom
thanatornpodgemini-collab:pmm-workflow-designer-improvements

Conversation

@thanatornpodgemini-collab

Copy link
Copy Markdown

Summary

This PR ships a substantial upgrade to the Flowy demo — turning it into a fully featured PMM Agentic Workflow Designer with bug fixes, security hardening, a canvas zoom control, and a realistic branching starter flow.

What changed

Bug fixes (engine/flowy.js)

  • rearrangeMe position bugr_array.y was read/written on a filter array [] instead of r_array[0], silently producing NaN positions after any rearrange
  • Bottom-edge auto-scroll axis — wrote scrollLeft instead of scrollTop when cursor hit the bottom edge
  • snap() type mismatch — parent assignment compared int to string without parseInt, could silently fail to find the block

Security (demo/main.js)

  • XSS via innerHTMLhydrateBlock and buildStarterFlowImport replaced innerHTML string interpolation with createElement + textContent
  • drawArrow innerHTML+= — replaced with insertAdjacentHTML to avoid full DOM re-serialization on every arrow draw

Canvas zoom feature (engine/flowy.js, demo/main.js, demo/index.html, demo/styles.css)

  • Floating − % + ⊡ pill at bottom-right of canvas
  • Ctrl/Cmd + scroll wheel support; range 25%–200%, step 25%
  • flowy.setZoom(level) + scaled(rect) helper divides all mouse coordinates and drag-element getBoundingClientRect calls by the zoom factor — drag-and-drop stays correct at any zoom level
  • canvas_div.getBoundingClientRect() intentionally left unscaled (it's in screen space)

flowy.import() improvements

  • Added skipRearrange parameter — callers supplying pre-positioned HTML can opt out of the post-import rearrangeMe() pass that would otherwise overwrite custom positions

21-node branching starter flow (demo/main.js)

  • Replaced linear 7-step placeholder with a trunk (8-step launch lifecycle) + 4 ongoing operational branches: competitive intel loop, content calendar loop, win/loss review loop, sales enablement ops loop
  • buildStarterFlowImport rewritten with a recursive subtree-width centering layout engine (root at x=600 y=160, +220px per depth level, 320px sibling gap)

Reliability

  • persist() now calls localStorage.removeItem() on empty canvas instead of bailing silently
  • restore() and JSON import catch blocks now log errors instead of swallowing them
  • Properties form input debounced at 150ms
  • flowy.destroy() added to remove all document-level event listeners
  • Dead code removed: addEventListenerMulti, removeEventListenerMulti, unused maxheight vars

Test plan

  • Click Build Starter Flow — 21-node branching tree renders with trunk + 4 branches
  • Zoom out to 50% with button — full tree visible in viewport, label reads 50%
  • Ctrl+scroll on canvas zooms in/out
  • Drag a block at 75% zoom — snaps correctly to parent
  • Click Clear Canvas then refresh — canvas stays empty (localStorage cleared)
  • Edit a step property — card updates, analytics refresh after ~150ms

🤖 Generated with Claude Code

Cash Claw and others added 2 commits May 3, 2026 15:44
…r flow

engine/flowy.js
- Fix rearrangeMe using r_array.y instead of r_array[0].y (blocks
  misplaced after any rearrange operation)
- Fix bottom-edge auto-scroll writing scrollLeft instead of scrollTop
- Fix int/string type mismatch in snap() rearrange parent assignment
- Replace innerHTML += in drawArrow with insertAdjacentHTML to avoid
  full DOM re-serialization and re-parse on every arrow draw
- Add skipRearrange parameter to flowy.import() so callers supplying
  pre-positioned HTML can opt out of the post-import rearrangeMe() pass
- Add flowy.destroy() to remove all document-level event listeners
- Cache repeated blocks.filter() lookups in snap/checkOffset/rearrangeMe
- Remove unused addEventListenerMulti, removeEventListenerMulti, and
  maxheight dead variables

demo/main.js
- Replace innerHTML string interpolation in hydrateBlock and
  buildStarterFlowImport with createElement + textContent (XSS fix)
- Fix analytics auto-correct matching blocks by label instead of ID
- Fix persist() not clearing localStorage when canvas is empty
- Add 150ms debounce on properties form input to avoid per-keystroke
  full-DOM analytics recalculation
- Catch blocks in restore() and import handler now log errors instead
  of swallowing them silently
- Replace linear 14-step starter flow with a 21-node branching tree:
  trunk (launch lifecycle) + 4 ongoing operational branches
  (competitive intel, content calendar, win/loss review, sales
  enablement ops)
- buildStarterFlowImport rewritten with recursive subtree-width
  centering layout engine (root x=600 y=160, +220px depth, 320px gap)
- Pass skipRearrange=true from build-starter and restore() so flowy's
  rearrangeMe() does not override custom tree positions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…flow

Canvas zoom feature:
- Add zoom controls pill (−, %, +, reset) floating bottom-right of canvas
- Support Ctrl/Cmd+scroll wheel to zoom; range 25%–200%, step 25%
- Add flowy.setZoom(level) and scaled(rect) helper to engine/flowy.js
  so all drag mouse coordinates and block getBoundingClientRect calls
  are divided by zoom level, keeping snap/attach math correct at any zoom
- applyZoom() in main.js updates transform, disables buttons at limits,
  and calls flowy.setZoom() to keep coordinate systems in sync

Fix demo/flowy.min.js not tracking engine changes:
- demo/flowy.min.js was the original unmodified library; all engine/flowy.js
  fixes (rearrangeMe, scrollTop, drawArrow, skipRearrange, destroy, zoom)
  were invisible to the demo — replaced with the fixed engine build

Branching workflow tree (21 nodes):
- Replace linear 14-step starter with trunk + 4 ongoing operational branches:
  competitive intel loop (off research), content calendar (off launch assets),
  win/loss review (off post-launch), sales enablement ops (off GTM strategy)
- buildStarterFlowImport rewritten with recursive subtree-width centering layout
- flowy.import() gains skipRearrange param to preserve caller-supplied positions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.

1 participant