Process Mapper - edmozley/freeitsm GitHub Wiki

Process Mapper

Folder: process-mapper/ Colour: Indigo #6366f1 Languages: 🌐 20 (en, af, fr, de, es, pt-BR, nl, it, pl, ru, id + hi, bn, ta, te, mr, kn, ml, gu, pa)

Visual flowchart builder for documenting processes and workflows. Drag-and-drop, snap-to-grid canvas with a slide-in detail panel, structured swimlanes, free-form groups, gradient fills, autosave, user-definable step types managed from a Settings page, a rich right-click menu (create / change / connect / copy formatting), PNG / PDF / Mermaid export, bookmarkable per-process URLs, and full native multi-language support across 20 locales.

Multi-lingual: Every visible string in this module β€” toolbar, sidebar, detail panel labels, dropdowns, placeholders, autosave status, export modal, the lot β€” resolves through the i18n layer. Switch locale in System β†’ Preferences and the whole module re-renders in the chosen language. See Architecture β†’ i18n for how the layer works.

Canvas

  • Dot-grid background β€” 20px grid
  • Snap-to-grid on every movement so layouts stay tidy without manual alignment

Step Types (user-definable)

The toolbar, the detail-panel Type dropdown and the right-click Create new… / Change to… submenus are all rendered from the process_step_types lookup table β€” managed from Process Mapper β†’ Settings. Each type is a name + a shape + a default colour.

Four built-in types ship out of the box and are protected from deletion (but can be renamed and recoloured):

Type Default shape Use
Process Rounded rectangle A step in the process
Decision Diamond A branch point
Terminal / Start-End Pill The start or end of the flow
Document Wavy-bottomed rectangle A document / output

Twelve shape primitives are available when defining a custom type β€” rectangle, rounded, pill, circle, diamond, parallelogram, trapezoid, hexagon, document, cylinder, cloud, subroutine. Geometry lives in assets/css/process-mapper.css as size-independent [data-shape="..."] rules; the cloud shape uses an objectBoundingBox SVG clipPath, the subroutine uses inset pseudo-element bars. Canvas steps carry data-shape="<key>" and data-type="<slug>" so the renderer is fully data-driven.

Settings page at process-mapper/settings/ matches the tickets-settings layout exactly (shared .tab-content, .section-header, .add-btn, .table-action-btn, .status-badge, .modal / .btn primitives from inbox.css). Add / edit / reorder / deactivate any user-defined type. The add/edit modal has a 12-shape visual picker plus a colour input, and the four API endpoints (get / save / delete / reorder_step_types.php) mirror the Tasks-module lookup pattern. Deactivating a type hides it from the toolbar and the right-click menu but keeps it in the detail-panel Type dropdown (marked with a *) so existing steps that reference it stay editable.

process_steps.type stores the slug as free-text β€” no FK β€” so the four built-in slugs (process, decision, start, document) line up with legacy data and no migration was needed when the palette became configurable.

Connectors

  • Drag from a step's edge handles to draw an arrow to another step
  • Optional text labels on connectors β€” double-click to edit (e.g. "Yes" / "No" on decision branches)
  • Connectors are stored as {from_step_id, to_step_id} only β€” endpoints are recomputed from step coordinates at every render, so they re-route automatically when steps move (including across lane reorders)

Multi-Select

  • Ctrl+click to toggle individual steps in/out of the selection
  • Rubber-band drag across the dot-grid background β€” and over lane backgrounds, so it still works on processes that fill the canvas with swimlanes
  • Ctrl+A to select all
  • Once selected, dragging any one step moves the whole group together; arrow keys nudge them all by one grid unit

Right-click menu

Right-clicking any step opens a dense context menu grouped into four sections by separator. showContextMenu() enables/disables and shows/hides items based on current state.

Per-step shortcuts

Action What it does
Edit label Alias for double-click inline rename. Reuses the same onStepDblClick code path.
Add note Drops a free-floating sticky-note annotation next to the step. Annotations live in their own table (process_annotations) and are drag/resize-able, with their own detail-panel body for text and colour. See Sticky-note annotations below.
Link to URL… Prompts for a URL (http(s):// only, validated client-side). Stored on the step's nullable url column. Steps with a URL get a small chain-link badge in their top-right corner; click the badge to open in a new tab (target="_blank" rel="noopener noreferrer", with stopPropagation so it doesn't kick off a step drag).

Type & connectors

Action What it does
Create new… Submenu of every active step type. Picks one, drops a new step of that type just to the right of the clicked step, already connected from it, with the detail panel open and the cursor in the label box. Placement nudges downward in grid steps to avoid overlapping; lane / group membership is inherited from where it lands.
Change to… Same submenu of types. Turns the step into the chosen type, picking up the new type's default colour and shape (gradient cleared) so a Process box becomes a Decision diamond in one click.
Connect to… Arms a one-shot click-to-connect mode: the cursor turns to a crosshair, a blue toast prompts "Click a step to connect to β€” or press Esc to cancel", and the next click on any step pairs them up. Click on empty canvas / a lane background / a group, right-click, or press Esc to cancel. Self-connect is refused.
Reverse connection… Submenu listing every connector incident to the step, each row showing an arrow indicating direction + the other end's label. Picking one flips just that connector's direction. Greyed when the step has no connectors. The submenu is rebuilt from current state at menu-open time.
Delete all connections Confirms then removes every connector incident to the step. Greyed when the step has no connectors.

Clipboard & formatting

Action What it does
Cut Visually fades the source step via .pm-step.is-cut (opacity 0.4) and stashes its data into stepClipboard with mode: 'cut'. The source isn't removed until a Paste happens β€” so cut+paste behaves like a move. Esc un-cuts and clears the clipboard.
Copy Same as Cut but without fading the source; mode: 'copy' so you can paste repeatedly.
Paste Hidden when the clipboard is empty. Drops the new step at the original right-click cursor position (ctxRightClickPos stashed by onStepContextMenu). For cut-paste, also removes the source and clears the clipboard.
Duplicate Same as Copy+Paste in one step β€” drops a copy offset 40Γ—40 from the original.
Copy formatting Stashes the right-clicked step's color + color2 into a separate paint-job clipboard (copiedFormat), independent of the step clipboard so they don't trample each other.
Apply formatting Hidden until something is copied. Paints the stashed colour + gradient onto the right-clicked step; the detail panel live-updates if it's open.

Destructive

Action What it does
Delete… Red-tinted via .pm-ctx-danger. Asks for confirmation, then removes the step and every connector attached to it. (Multi-select bulk delete still uses the Delete key as before.)

placeClonedStep(data, x, y) is the shared helper behind Paste / Duplicate β€” it picks a non-overlapping spot starting at the requested coords and seeds lane_id / group_id from where the new step lands. cloneStepData(step) captures the cloneable shape (type / label / description / url / size / colour / gradient, but not position / connectors / temp IDs).

The menu clamps to the viewport, the submenus flip leftward when there isn't room on the right, and the menu dismisses on outside click / Escape / canvas scroll / window blur. Right-clicking a step no longer starts a drag, so opening the menu never nudges the step.

Keyboard

  • Arrow keys nudge selected steps by one grid unit (20px)
  • Delete / Backspace deletes the current selection (step, connector, group, or lane)

URL links per step

Steps carry an optional URL (process_steps.url, VARCHAR(500) NULL) that turns into a small chain-link badge in the step's top-right corner. The badge is an <a target="_blank" rel="noopener noreferrer"> so clicks open in a new tab; mousedown and click are stopPropagation-ed on the badge so they don't trip the step's own drag handlers. Set via the right-click Link to URL… prompt or the detail panel's Link (URL) input. The .is-exporting chrome-hider strips the badge from PNG / PDF exports.

Sticky-note annotations

Free-form sticky notes that float above the canvas (z-index 4), independent of any step. They have their own table:

process_annotations (
  id, process_id, text, x, y, width, height,
  color    DEFAULT '#fff59d',   -- sticky yellow
  color2   NULL                 -- optional gradient second stop
)
  • Add via the step's right-click Add note item β€” the new note lands just above and to the right of the clicked step and the detail panel slides in with the text area focused
  • Move by dragging the note body; resize via the bottom-right corner handle; double-click the body to re-focus the text area
  • Detail panel: dedicated Note body with text / colour / position / size (the four bodies in the panel container are now Step / Group / Lane / Annotation, swapped by selection)
  • Delete by selecting + pressing Delete / Backspace
  • Persistence: saved in the same transaction as steps / connectors / groups / lanes. Defensive try/catch in save.php and get.php so older installs that haven't run db_verify still round-trip the rest of the diagram
  • Cross-save selection: after autosave reloads with fresh real IDs, the selected annotation is re-found by stable identity (x, y, width, height) so the panel stays on it without flicker

Stickies don't participate in connectors or lane / group ownership β€” they're pure visual annotation.

Slide-in Detail Panel

Click any item to open a context-aware panel β€” but only on click-release, not mousedown. Picking a step up to drag it deliberately doesn't flash the panel in; the panel only opens at mouseup if dragging.moved is false. Ctrl+click selection-toggle is unaffected (that path doesn't start a drag).

Four bodies share the same panel container, swapped by the type of the selection:

  • Step: label, type (any from the user-configurable palette), colour + optional gradient, description, link URL, x/y position, and a list of incoming/outgoing connectors with editable labels and a delete button per connector
  • Group: label, colour + optional gradient, x/y, width/height
  • Annotation (sticky note): text, colour + optional gradient, x/y, width/height β€” see Sticky-note annotations above
  • Lane: label, colour + optional gradient, height, display order

The panel survives autosave β€” when the canvas is reloaded after a save, the previously-selected item is re-found by stable identity (step x/y, group x/y/w/h, lane display_order, annotation x/y/w/h) and the panel re-populates without flicker. If the item was deleted in the meantime, the panel closes gracefully.

Groups (visual underlays + container)

Optional labelled coloured rectangles that sit behind steps. Useful for marking a phase ("Resolution", "Triage"), a team's area of responsibility, or any free-form cluster.

  • Click Group in the toolbar to drop one at the canvas centre (240Γ—160, pale blue dashed border by default)
  • Drag to move, drag the bottom-right corner handle to resize, double-click to rename
  • Groups own their contents via a nullable group_id on process_steps:
    • Drop a step inside a group β†’ step gets the group's id
    • Drag the group β†’ contained steps move with it
    • Resize the group so a step falls outside β†’ that step's group_id clears on mouseup
    • Delete the group β†’ contained steps survive untouched but lose their group_id
  • When groups overlap, the smallest by area wins for ownership (so an inner group inside a wider one claims the steps that visually fall inside it)
  • Groups can sit inside a lane and will move with the lane on reorder/resize/delete, derived from each group's vertical centre at the start of the lane drag

Swimlanes

Horizontal bands stacked top-to-bottom across the canvas, with a left-edge vertical label header showing each lane's name.

  • Click Lane in the toolbar to add a 180px-tall band at the bottom of the stack
  • Reorder by dragging the lane's left-edge header up/down β€” lanes snap between slots as the cursor crosses each neighbour's midline. Every step (and group) belonging to a reordering lane comes along, anchored to its lane's band via a yWithinLane snapshot taken before the swap
  • Resize by dragging the bottom edge (the divider). Snaps to the 20px grid so the divider and its contents move in lockstep. Lanes below shift to make room, and steps/groups in those lanes follow so they stay anchored to their own band
  • Delete by selecting (click the header) and pressing Delete β€” contained steps lose lane_id but keep their position, lanes below shift up to close the gap, their contents follow
  • Step ownership: every step has a nullable lane_id. After any step drag, lane_id auto-assigns based on the step's vertical centre. At the start of any lane drag, an autoAssignAllStepLanes() pass tags every step with its current lane so the upcoming reorder/resize math operates on a fully-tagged set (catches steps that pre-existed the lanes)

Gradient fills

Steps, groups, and lanes can render as a two-stop linear-gradient(135deg, color, color2) instead of a flat fill. Each shape's detail panel has a Gradient checkbox that reveals a second-colour picker (pre-seeded with a 40-unit darker shade of the primary colour, but you can pick anything). Null color2 = solid, matching pre-feature behaviour.

Autosave

Toolbar toggle persists per-analyst via user_preferences (key process_mapper_autosave).

  • When on: debounces a save ~2 seconds after the last edit (no wasted writes on an untouched diagram, no half-finished-drag captures)
  • Status indicator beside the toggle cycles through βœ“ Saved / Unsaved / Saving… / ⚠ Save failed β€” retry. Saving… is held visible for at least 400ms so it doesn't flash on fast saves
  • The save fire defers itself while any drag is in progress so the user's in-flight drag never gets clobbered by a mid-drag reload
  • Manual Save button and Ctrl+S still work in both modes

Export β€” PNG / PDF / Mermaid

Toolbar Export button opens a chooser with three format tiles:

PNG image

Captures the canvas at 2Γ— resolution via vendored html2canvas (assets/js/vendor/html2canvas.min.js, 1.4.1), ideal for slides, screenshots and embedding in tickets/changes. File names slugify the process title (Incident triage β†’ incident-triage.png).

PDF document

Same capture wrapped onto an A4 page via vendored jsPDF (assets/js/vendor/jspdf.umd.min.js, 2.5.2). Portrait or landscape is auto-picked from the diagram's aspect ratio; proportional fit-to-page with a 24pt margin so big diagrams aren't stretched. Print-ready.

Mermaid markup

Generates Mermaid flowchart syntax ready to paste into any Markdown editor that supports it (GitHub READMEs, GitHub/GitLab wikis, Notion, Confluence, Obsidian, Mermaid Live Editor).

  • flowchart LR direction so lanes-as-subgraphs stack vertically into proper horizontal swimlanes
  • Each lane becomes a subgraph laneN["Label"] ... end block in display_order, with its steps nested inside
  • Unlaned steps emit at the top level
  • Each step's shape (not type name) decides the Mermaid syntax β€” so custom user-defined types map cleanly via their chosen shape: rectangle β†’ ["label"], rounded β†’ ("label"), pill β†’ (["label"]), circle β†’ (("label")), cylinder β†’ [("label")], subroutine β†’ [["label"]], diamond β†’ {"label"}, hexagon β†’ {{"label"}}, parallelogram β†’ [/"label"/], trapezoid β†’ [/"label"\]. Mermaid has no document or cloud primitive in classic syntax, so document β†’ parallelogram and cloud β†’ circle as the closest visual matches
  • Connectors become --> arrows, gaining -->|"label"| when labelled β€” Mermaid resolves IDs across subgraph boundaries so cross-lane connectors render correctly
  • Labels are HTML-entity-escaped (&, ", <, > β†’ entities; \n β†’ <br/>)
  • Copy button uses the Clipboard API with a Ctrl-A β†’ Ctrl-C fallback

Capture rect

For PNG/PDF the export rect is the bounding box of all steps + groups with 40px padding. When swimlanes are present the rect extends to x=0, y=0 so lane headers and the top of the first lane aren't cropped. During capture an .is-exporting class hides edit-time chrome (edge handles, selection rings, rubber-band, temp connector, empty-state placeholder, the dot-grid background) so the file shows what you'd want to share, not what you're editing.

.pm-canvas is a scroll container, so the capture pass temporarily expands its CSS width/height to fit the full content + sets overflow: visible (and pins the connectors SVG to the same dimensions) β€” without that, steps reachable only by scrolling sideways would be clipped by html2canvas's render bounds. All mutations are stashed and restored in finally.

Mermaid is lossy on visuals, faithful on structure: hand-placed positions, colours, gradients, and groups don't translate (Mermaid only has subgraphs, already used for lanes). PNG/PDF preserve everything visible.

Save / Load

Full persistence with a sidebar listing all saved processes. The save flow inserts lanes and groups first, builds laneIdMap and groupIdMap (oldTempId β†’ realId) tables, then inserts steps with lane_id and group_id translated through those maps. Connectors translate from_step_id/to_step_id through a similar idMap for steps. After save, the canvas reloads to pick up the fresh real IDs β€” preserveDetail keeps the detail panel open across that reload (see Slide-in Detail Panel above).

Deep-linkable URLs

Selecting a process from the sidebar (or creating a new one) updates the address bar to process-mapper/?id=<n> via history.pushState, so each open diagram has a copyable URL you can share or bookmark. Loading the page with ?id=N auto-opens that diagram once the sidebar list has loaded (silently no-ops if the id doesn't exist any more). Browser back / forward buttons step through previously-opened processes via a popstate handler that reloads whichever diagram the URL points at, or clears the canvas if ?id is gone. Deleting the currently-open process drops ?id from the URL. Same-URL pushState calls are no-ops, so the post-save reload (which re-opens the diagram to pick up real IDs in place of temp negatives) doesn't bloat the history stack.

Demo Data

The System β†’ Demo Data importer includes a Process Mapper module with auto-laid-out flowcharts. The demo JSON omits step coordinates β€” a server-side layered-DAG layout pass assigns x/y on import so the imported processes look tidy without per-step coordinate authoring.

Key Tables

Table Purpose
processes Process metadata (title, description, created/updated datetimes)
process_steps Step records β€” label, type slug, description, optional URL (nullable VARCHAR(500)), colour, optional color2 for gradient, position, nullable lane_id and group_id for ownership
process_annotations Free-form sticky-note records: id, process_id, text, x, y, width, height, color, color2. Independent of any step β€” saved alongside steps in the same transaction. Used for canvas-level annotations / callouts
process_connectors Edges with optional label; endpoints recomputed at render time
process_groups Free-form labelled rectangles that own steps via process_steps.group_id
process_lanes Horizontal swimlane bands (label, colour, color2, display_order, height) that own steps via process_steps.lane_id
process_step_types User-definable step-type palette: id, name, slug, shape, color, display_order, is_active, is_builtin. Slugs are immutable; the four built-ins (process, decision, start, document) are protected from deletion. Referenced by process_steps.type as free-text (no FK) so legacy data round-trips without migration
⚠️ **GitHub.com Fallback** ⚠️