Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Aston2710/mc-modeler/llms.txt

Use this file to discover all available pages before exploring further.

A diagram is the central entity in MC Modeler — it holds a complete BPMN 2.0 model serialised as XML, plus metadata such as a thumbnail preview, an element count, and versioning information. Diagrams live either at the top level (loose) or inside a project, and multiple diagrams can be open simultaneously in the tabbed editor.

The Diagram interface

interface Diagram {
  id: string                      // UUID, generated on create
  name: string                    // User-visible display name
  xml: string                     // Full BPMN 2.0 XML (lazy-loaded in list views)
  thumbnail: string | null        // SVG data URL; stored separately from XML
  folderId: string | null         // Optional folder assignment
  projectId: string | null        // Owning project, or null for loose diagrams
  elementCount: number            // Count of BPMN flow elements
  schemaVersion: number           // Migration version, starts at 1
  createdAt: string               // ISO 8601 creation timestamp
  updatedAt: string               // ISO 8601 last-modified timestamp (server-authoritative)
  parentDiagramId: string | null  // Links a sub-process diagram to its parent
  subProcessElementId: string | null // The bpmn:SubProcess element ID in the parent
  deletedAt?: string | null       // Non-null means the diagram is in the trash
}
The schemaVersion field is reserved for future data migrations. All diagrams created today start at version 1. When the XML schema changes in a future release, a migration routine will increment this value so older diagrams can be automatically upgraded on first open.

Creating a diagram

Click the + New diagram button in the toolbar or the Create diagram card at the top-left of the diagram grid. Either path opens the New diagram modal where you enter a name, then the store calls createDiagram(name, projectId?):
// diagramStore.ts — createDiagram
const id = generateDiagramId()  // UUID v4
const diagram: Diagram = {
  id,
  name,
  xml: EMPTY_BPMN,           // Canonical starter XML with one participant + start event
  thumbnail: null,
  folderId: null,
  projectId,                 // Optional — set when creating inside a project
  elementCount: 0,
  schemaVersion: 1,
  createdAt: now,
  updatedAt: now,
  parentDiagramId: null,
  subProcessElementId: null,
}
await diagramRepository.save(diagram)
The new diagram is immediately pushed into the store and opened in a new tab. When you import a .bpmn or .bpm file, the XML is first normalised through a canonical serialiser and any base64-embedded images are extracted to Storage before the Diagram is saved — ensuring the persisted XML never contains large inline blobs.

The DiagramList view

The home screen renders diagrams as a responsive grid of cards or a compact list of rows — toggle between them with the grid/list buttons in the top-right of the content area. Each DiagramCard shows:
  • A thumbnail preview (SVG data URL) or a file-text placeholder icon when no thumbnail has been generated yet.
  • The diagram name.
  • A relative timestamp of the last modification and the element count.
  • Live presence avatars showing collaborators currently editing the diagram (cloud mode only).
  • A ⋯ kebab menu with Open and Delete actions.
The DiagramRow (list view) shows the same information in a single horizontal line. Cards are generated by the DiagramCard and DiagramRow components in DiagramList.tsx. A global search field (⌘K / Ctrl+K) filters by name across all diagrams and folders simultaneously.

Thumbnail auto-generation

Thumbnails are generated as SVG data URLs directly from the bpmn-js canvas after each save. The utility buildThumbnail in src/utils/thumbnailUtils.ts renders the diagram in light theme regardless of the user’s current theme preference, ensuring consistent preview images. For diagrams with two or more pools, only the top pool (lowest y coordinate) is used as the thumbnail crop, keeping the preview readable when the full diagram would be too small to identify. Thumbnails are stored separately from the diagram XML via dedicated repository methods, keeping the main list query lean:
// IDiagramRepository.ts
getThumbnail(id: string): Promise<string | null>
saveThumbnail(id: string, dataUrl: string): Promise<string | null>
saveThumbnail returns the new updatedAt value if the database trigger bumped it (first thumbnail for a diagram). The caller must adopt this value as the current CAS token to avoid phantom conflicts on the next save. On initial load, thumbnails are hydrated in the background after the diagram cards are already visible — the list never blocks on thumbnail fetches.

Renaming a diagram

Rename a diagram through its context menu or the properties panel. The store calls renameDiagram(id, name), which delegates to the targeted repository method setDiagramName:
// IDiagramRepository.ts
setDiagramName(id: string, name: string): Promise<string | null>
setDiagramName issues a narrowly scoped UPDATE that only touches the name column. The full save(diagram) path is intentionally not used for renaming because it would write the in-memory XML — which may be stale in a collaborative session — risking a silent overwrite of another user’s work.

Duplicating a diagram

Select Duplicate from the ⋯ menu. The store fetches the source diagram’s XML if it is not already in memory, copies all Storage images to a new path under the duplicate’s ID (via rehomeImages), then saves the copy as a new Diagram with a fresh UUID, createdAt/updatedAt, and a (copia) name suffix. The duplicate starts with no thumbnail.

Opening a diagram and tabs

Clicking a diagram card calls openDiagram(id), which adds a DiagramTab entry if one does not already exist and sets it as the active tab. The TabsBar at the top of the editor shows all open tabs simultaneously.
interface DiagramTab {
  id: string
  name: string
  dirty: boolean  // true when unsaved changes exist
}
Tabs are independent — you can switch between open diagrams without losing changes in any of them. Closing a tab calls closeTab(id), which focuses the nearest remaining tab. The XML for a diagram is not loaded when the list populates — it is fetched on demand the first time a diagram is opened via ensureXml(id) and cached in memory. If the XML is already cached from a previous open, it is returned immediately without a network round trip.

Auto-save and the unsavedChanges flag

The editor runs an auto-save timer (default 20 seconds) after any change that sets the unsavedChanges flag. A random jitter of 0–5 seconds is added to decorrelate save attempts from different collaborators editing the same diagram simultaneously, reducing CAS conflicts. While unsaved changes exist, the unsavedChanges flag in UIState is true and the status bar shows an Unsaved changes indicator. Saves are serialised through a saveChain promise so that concurrent auto-saves from the same client never race against each other.

Optimistic CAS: conflict detection

Every save calls IDiagramRepository.save(diagram, expectedUpdatedAt). The expectedUpdatedAt argument is the updatedAt value the client loaded. If another writer has saved between the client’s load and this save, the server updated_at will differ and the repository throws a DiagramConflictError.
export class DiagramConflictError extends Error {
  constructor(public readonly diagramId: string) {
    super(`Conflicto de guardado: el diagrama ${diagramId} fue modificado por otro usuario`)
    this.name = 'DiagramConflictError'
  }
}
The store resolves conflicts with an idempotency check: if the server already holds exactly the same XML the client is trying to write, it adopts the server’s updatedAt and continues. If the content genuinely differs, the store retries once with the fresh updatedAt. On a second conflict, it emits a flujo:save-conflict custom DOM event so the UI can offer the user a choice between loading the server version or saving their copy as a duplicate.

Sub-process diagrams

A sub-process element on a BPMN diagram can link to a dedicated child diagram. The child Diagram records:
  • parentDiagramId — the UUID of the parent diagram.
  • subProcessElementId — the ID of the bpmn:SubProcess element within the parent’s XML.
// diagramStore.ts — createSubDiagram
createSubDiagram: async (name, parentDiagramId, subProcessElementId) => {
  const parent = get().diagrams.find((d) => d.id === parentDiagramId)
  const diagram: Diagram = {
    // ...
    parentDiagramId,
    subProcessElementId,
    projectId: parent?.projectId ?? null,  // inherits parent's project
  }
}
Helper methods getChildren(parentId) and getChildByElement(parentId, subProcessElementId) let the editor navigate the sub-process hierarchy. deleteWithChildren(id) recursively soft-deletes a parent and all its descendant sub-process diagrams.

Filters and sort reference

Filter valueBehaviour
allAll diagrams in the current scope
recentModified within the last 7 days
ownDiagrams you own (not shared with you)
sharedDiagrams shared with you by another user
Sort keyDefault direction
updatedNewest first
createdNewest first
nameA–Z (locale-aware, case-insensitive)
elementsMost elements first
The active sort and direction are persisted in UserPreferences.diagramSort and survive page reloads. Clicking the active sort option toggles between ascending and descending.

Build docs developers (and LLMs) love