synergycodes/ng-diagram/ng-diagram-config

Claude Code and agent skill by synergycodes/ng-diagram

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Use when configuring ng-diagram - [config] vs updateConfig(), a config change that has no effect, NgDiagramConfig/FlowConfig keys and defaults, custom node and edge IDs (computeNodeId), edge panning, or debugMode logging.

Install

npx vskill@latest install synergycodes/ng-diagram/ng-diagram-config

SKILL.md

ng-diagram configuration

How [config], NgDiagramService.updateConfig() and the config() signal work, which settings apply at runtime, and common setups: custom IDs, edge panning, viewer mode, debug logging. Read references/config-reference.md for every key with its default and its owning skill.

Rules

  • Pass only overrides. NgDiagramConfig is a deep partial of FlowConfig; every key you leave out keeps its default.
  • `[config]` is read when the diagram creates its engine: on first render and whenever you bind a new model. Binding a new config object later does not reach the engine. Exception: hideWatermark and watermarkPosition follow the binding at any time.
  • Change settings at runtime with `updateConfig(partial)`. It deep-merges into the running config and updates the config() signal. A new model creates a new engine from [config], so updateConfig() changes are lost then: keep [config] in sync if you swap models.
  • Merging: objects merge key by key, but arrays (shortcuts) and functions replace the old value, and undefined values are skipped. To reset a key, pass its default value.
  • Let function options read your own state. Callbacks such as validateConnection, shouldSnapDragForNode or canGroup run on every use, so a callback that reads your signal needs no updateConfig() and survives model swaps.
  • Type callback parameters yourself. In 1.4.0 the deep-partial NgDiagramConfig type drops the signature of callbacks that take parameters (linking.validateConnection, both edge data builders, resize.getMinNodeSize, grouping.canGroup, the snapping and rotation callbacks): inline parameters become implicit any (TS7006 under strict), and a wrong signature or return type still compiles. Build the section with its own type (const linking: Partial<LinkingConfig> = { ... }, likewise Partial<SnappingConfig>, Partial<ResizeConfig>, Partial<GroupingConfig>, Partial<NodeRotationConfig>): it types the parameters and checks the signature. Annotating the parameters ((node: Node) => ...) only types the body. Optional callbacks (danglingEdges.*) and computeNodeId/computeEdgeId keep their types.
  • `config()` is `{}` until just after the first `diagramInit` (still {} inside that handler), then the full merged config with all defaults. After a model swap it shows the previous engine's config until the new initialization. Its type is a deep partial: use optional chaining.

Config binding and runtime changes

typescript
@Component({
selector: 'app-editor',
imports: [NgDiagramComponent],
providers: [provideNgDiagram()],
template: `
<ng-diagram [model]="model" [config]="config" />
<label><input type="checkbox" [checked]="snapToGrid()" (change)="snapToGrid.set(!snapToGrid())" /> Snap</label>
<button (click)="setMaxZoom(2)">Max zoom 200%</button> {{ maxZoom() }}
`,
})
export class EditorComponent {
private readonly diagram = inject(NgDiagramService);
readonly snapToGrid = signal(false);
// Read when the diagram creates its engine (first render and every new model)
readonly config: NgDiagramConfig = {
zoom: { max: 3 },
snapping: {
// Callbacks run on every use: reading your own signal needs no updateConfig()
shouldSnapDragForNode: () => this.snapToGrid(),
shouldSnapResizeForNode: () => this.snapToGrid(),
defaultDragSnap: { width: 20, height: 20 },
},
};
// {} until diagramInit, then the full merged config
readonly maxZoom = computed(() => this.diagram.config().zoom?.max);
model = initializeModel();
setMaxZoom(max: number): void {
this.diagram.updateConfig({ zoom: { max } }); // deep merge: other zoom keys stay
}
}

updateConfig() throws [ngDiagram] Library engine not initialized yet. before <ng-diagram> has created its engine (see ng-diagram-model-init).

Keys that are not re-read at runtime

KeyReadChange it later by
virtualization.enabledRendering and the pan handler: when the engine is created (zoomToFit, the minimap, port cleanup and [ngDiagramHidden] read it live)New [config] value plus a new model (below); not updateConfig().
edgeRouting.defaultRoutingWhen the engine is createdNgDiagramService.setDefaultRouting(name), see ng-diagram-edge-routing.
zoom.zoomToFit.onInitWhen diagramInit fires[config] before the next model.
hideWatermark, watermarkPositionFrom the [config] binding onlyChanging the bound object; updateConfig() ignores them.
viewportPanningEnabledBehavior: on every gesture. Grab cursor: when the engine is createdupdateConfig() changes the behavior; the grab cursor stays as it was.

Settings used to compute stored results (routing options, zIndex, snapping sizes) apply to what is computed after the change: existing edges keep their path until they or their nodes change.

Change an init-only key (virtualization)

typescript
@Component({
selector: 'app-big-diagram',
imports: [NgDiagramComponent],
providers: [provideNgDiagram()],
template: `<ng-diagram [model]="model()" [config]="config()" />`,
})
export class BigDiagramComponent {
private readonly injector = inject(Injector);
private readonly modelService = inject(NgDiagramModelService);
readonly config = signal<NgDiagramConfig>({ virtualization: { enabled: false } });
readonly model = signal(initializeModel());
setVirtualization(enabled: boolean): void {
const data = JSON.parse(this.modelService.toJSON()) as Partial<Model>;
this.config.update((config) => ({ ...config, virtualization: { ...config.virtualization, enabled } })); // keeps padding/idleDelay
this.model.set(initializeModel(data, this.injector)); // new engine reads the new [config]
}
}

The new model is a full re-initialization (ng-diagram-model-init). Virtualization itself: ng-diagram-performance.

Custom node and edge IDs

typescript
// crypto.randomUUID exists only on HTTPS and localhost
const uid = (): string =>
typeof crypto.randomUUID === 'function' ? crypto.randomUUID() : `${Date.now()}-${Math.random().toString(36).slice(2)}`;
export const idConfig: NgDiagramConfig = {
computeNodeId: () => `node_${uid()}`,
computeEdgeId: () => `edge_${uid()}`,
};
  • computeNodeId names nodes dropped from the palette and every pasted node.
  • computeEdgeId names every pasted edge and every edge the user draws. A drawn edge gets its id before linking.finalEdgeDataBuilder runs, so the builder can still replace it.
  • Neither is used by addNodes, addEdges or initializeModel: you pass those ids yourself.
  • Default: crypto.randomUUID(), or Date.now() plus a random string where crypto.randomUUID is missing (plain http:// other than localhost). IDs must stay unique: a plain counter restarts after a reload and collides with saved ids.

Edge panning

Dragging near the border of the diagram pans the viewport, faster the closer the pointer is to the border.

typescript
export const panningConfig: NgDiagramConfig = {
selectionMoving: { edgePanningThreshold: 60, edgePanningForce: 15 }, // dragging nodes
linking: { edgePanningEnabled: false }, // drawing and relinking edges
};
  • selectionMoving.edgePanningEnabled / edgePanningThreshold / edgePanningForce (defaults true, 30 px, 10): while dragging nodes. The threshold is also the margin the viewport keeps around nodes moved with the arrow keys.
  • linking.edgePanningEnabled / edgePanningThreshold / edgePanningForce (same defaults): while drawing or relinking an edge.
  • Both need viewportPanningEnabled: true.

Panning and dragging switches

  • viewportPanningEnabled: false (since 0.9.0): no panning with mouse, touch, wheel or trackpad, no arrow-key panning, no edge panning. Zooming and programmatic moves (moveViewport, setViewport, zoomToFit) still work. See ng-diagram-viewport.
  • nodeDraggingEnabled: false (since 1.0.0): nodes cannot be dragged or moved with the arrow keys; since 1.4.0 the arrow keys pan the viewport instead. For single nodes use draggable: false (ng-diagram-custom-nodes). Programmatic moves still work.

Viewer (read-only) settings

typescript
export const viewerConfig: NgDiagramConfig = {
nodeDraggingEnabled: false, // no drag, no arrow-key move (arrows pan instead)
resize: { defaultResizable: false }, // nodes with resizable: true still show handles
nodeRotation: { defaultRotatable: false }, // same for rotatable: true
linking: { validateConnection: () => false }, // a draw can start but never connects to a port ('noTarget')
shortcuts: configureShortcuts([
{ actionName: 'deleteSelection', bindings: [] },
{ actionName: 'cut', bindings: [] },
{ actionName: 'paste', bindings: [] },
]),
};

These flags remove most editing gestures, but they are not a lock: nodes with their own resizable: true or rotatable: true keep their handles, users can still select, and your code can still change the model. With danglingEdges.enabled, also set danglingEdges: { shouldKeepOnDrop: () => false }: the connection validator is not called for a drop on empty canvas, so such a drop still keeps a dangling edge. To block changes for real, cancel them in a middleware. For a runtime read-only toggle that blocks changes (and how to hide ports in it), see ng-diagram-middleware (Read-only mode). Shortcut names: ng-diagram-shortcuts.

Debug logging

debugMode: true logs every committed model update (an update cancelled by a middleware is not logged) as [ngDiagram] <action types> with { initialState, finalState, history, initialUpdate }, and logs render timings ([ngDiagram] Render: +<added> -<removed> nodes (total: <n>): <ms>ms) when the set of rendered nodes changes. It can be switched at runtime with updateConfig({ debugMode: true }). It does not put the engine on window. It logs full state objects on every update, so keep it off in production.

Edge cases

  • Settings read through the config() signal apply only from diagramInit on: background, defaultNode.removePorts, resize.defaultResizable, nodeRotation.defaultRotatable, linking.defaultRelinkable. For example, with defaultNode: { removePorts: true } the default ports exist during the first initialization and disappear right after it.
  • shortcuts is replaced as a whole: build the list with configureShortcuts(...), which merges with the defaults. At runtime pass the live list as the second argument, configureShortcuts(changes, diagram.config().shortcuts as ShortcutDefinition[]), or earlier changes reset to the defaults (ng-diagram-shortcuts).
  • Every new engine starts from [config]; middlewares and custom routings registered at runtime are dropped too (ng-diagram-model-init).

Symptom → fix

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 90 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

7-day trend score
0

Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/synergycodes/ng-diagram/ng-diagram-config/badge)](https://verified-skill.com/skills/synergycodes/ng-diagram/ng-diagram-config)

Something wrong with this skill or its page? Tell us.