@bpmn-nova/studio 0.3.0-preview → 0.3.2-preview

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +245 -20
  2. package/dist/canvas.js +7 -4
  3. package/dist/context-menu.js +2 -2
  4. package/dist/controller.js +84 -6
  5. package/dist/index.d.ts +138 -38
  6. package/dist/index.js +12 -11
  7. package/dist/interactions.js +1 -1
  8. package/dist/modules/bpmn-model/index.d.ts +11 -0
  9. package/dist/modules/bpmn-model/index.js +703 -0
  10. package/dist/modules/core/containment.js +590 -0
  11. package/dist/modules/core/gateway.js +72 -0
  12. package/dist/modules/core/history.js +32 -0
  13. package/dist/modules/core/index.d.ts +268 -0
  14. package/dist/modules/core/index.js +7 -0
  15. package/dist/modules/core/layout.js +644 -0
  16. package/dist/modules/core/model.js +287 -0
  17. package/dist/modules/core/runtime-transition-route.js +360 -0
  18. package/dist/modules/core/scope.js +99 -0
  19. package/dist/modules/designer/index.d.ts +79 -0
  20. package/dist/modules/designer/index.js +607 -0
  21. package/dist/modules/engine-activiti/index.d.ts +19 -0
  22. package/dist/modules/engine-activiti/index.js +160 -0
  23. package/dist/modules/engine-flowable/index.d.ts +19 -0
  24. package/dist/modules/engine-flowable/index.js +160 -0
  25. package/dist/modules/export-svg/index.d.ts +112 -0
  26. package/dist/modules/export-svg/index.js +2 -0
  27. package/dist/modules/export-svg/preview.js +327 -0
  28. package/dist/modules/export-svg/render.js +718 -0
  29. package/dist/modules/icons/index.d.ts +24 -0
  30. package/dist/modules/icons/index.js +264 -0
  31. package/dist/modules/palette/index.d.ts +74 -0
  32. package/dist/modules/palette/index.js +99 -0
  33. package/dist/modules/palette/panel.js +99 -0
  34. package/dist/modules/properties/index.d.ts +20 -0
  35. package/dist/modules/properties/index.js +19 -0
  36. package/dist/modules/properties-activiti/index.d.ts +3 -0
  37. package/dist/modules/properties-activiti/index.js +97 -0
  38. package/dist/modules/properties-bpmn/index.d.ts +3 -0
  39. package/dist/modules/properties-bpmn/index.js +518 -0
  40. package/dist/modules/properties-core/index.d.ts +124 -0
  41. package/dist/modules/properties-core/index.js +312 -0
  42. package/dist/modules/properties-flowable/index.d.ts +3 -0
  43. package/dist/modules/properties-flowable/index.js +114 -0
  44. package/dist/modules/properties-renderer/index.d.ts +25 -0
  45. package/dist/modules/properties-renderer/index.js +491 -0
  46. package/dist/modules/renderer-svg/index.d.ts +118 -0
  47. package/dist/modules/renderer-svg/index.js +1460 -0
  48. package/dist/modules/runtime/index.d.ts +169 -0
  49. package/dist/modules/runtime/index.js +535 -0
  50. package/dist/modules/theme/index.d.ts +95 -0
  51. package/dist/modules/theme/index.js +368 -0
  52. package/dist/modules/viewer/index.d.ts +265 -0
  53. package/dist/modules/viewer/index.js +1011 -0
  54. package/dist/modules/viewer/runtime-content.js +123 -0
  55. package/dist/modules/viewer/runtime-details-motion.js +228 -0
  56. package/dist/modules/viewer/runtime-trace.js +574 -0
  57. package/dist/modules/viewer/timeline.js +276 -0
  58. package/dist/selection-layout.js +1 -1
  59. package/dist/shell.js +210 -26
  60. package/dist/styles.css +116 -7
  61. package/llms-full.txt +3082 -0
  62. package/llms.txt +225 -0
  63. package/package.json +39 -16
@@ -0,0 +1,368 @@
1
+ const THEME_MODES = new Set(['light', 'dark', 'auto']);
2
+ const TONE_NAME = /^[a-z][a-z0-9-]{0,47}$/;
3
+
4
+ const COLOR_VARIABLES = Object.freeze({
5
+ canvas: '--nova-color-canvas',
6
+ surface: '--nova-color-surface',
7
+ surfaceRaised: '--nova-color-surface-raised',
8
+ surfaceSubtle: '--nova-color-surface-subtle',
9
+ surfaceMuted: '--nova-color-surface-muted',
10
+ text: '--nova-color-text',
11
+ textSecondary: '--nova-color-text-secondary',
12
+ textMuted: '--nova-color-text-muted',
13
+ textInverse: '--nova-color-text-inverse',
14
+ border: '--nova-color-border',
15
+ borderStrong: '--nova-color-border-strong',
16
+ divider: '--nova-color-divider',
17
+ primary: '--nova-color-primary',
18
+ primaryHover: '--nova-color-primary-hover',
19
+ primarySoft: '--nova-color-primary-soft',
20
+ focusRing: '--nova-color-focus-ring',
21
+ edge: '--nova-color-edge',
22
+ gridDot: '--nova-color-grid-dot',
23
+ backdrop: '--nova-color-backdrop',
24
+ });
25
+
26
+ const SHADOW_VARIABLES = Object.freeze({
27
+ sm: '--nova-shadow-sm',
28
+ md: '--nova-shadow-md',
29
+ lg: '--nova-shadow-lg',
30
+ });
31
+
32
+ const TONE_FIELDS = Object.freeze({
33
+ foreground: 'foreground',
34
+ background: 'background',
35
+ border: 'border',
36
+ strong: 'strong',
37
+ });
38
+
39
+ const SNAPSHOT_TONES = Object.freeze([
40
+ 'primary', 'success', 'danger', 'warning', 'info', 'neutral',
41
+ 'user-task', 'service-task', 'script-task', 'rule-task', 'message-task', 'manual-task',
42
+ ]);
43
+
44
+ const DEFAULT_ACTIONS = Object.freeze({
45
+ submit: Object.freeze({ label: '提交', iconId: 'ui.statusCompleted', tone: 'success' }),
46
+ approve: Object.freeze({ label: '通过', iconId: 'ui.statusCompleted', tone: 'success' }),
47
+ reject: Object.freeze({ label: '驳回', iconId: 'ui.statusFailed', tone: 'danger' }),
48
+ return: Object.freeze({ label: '退回', iconId: 'ui.statusRejected', tone: 'warning' }),
49
+ 'add-sign': Object.freeze({ label: '加签', iconId: 'ui.add', tone: 'info' }),
50
+ transfer: Object.freeze({ label: '转办', iconId: 'ui.locateTarget', tone: 'info' }),
51
+ delegate: Object.freeze({ label: '委派', iconId: 'node.userTask', tone: 'info' }),
52
+ withdraw: Object.freeze({ label: '撤回', iconId: 'ui.statusRejected', tone: 'warning' }),
53
+ comment: Object.freeze({ label: '备注', iconId: 'ui.edit', tone: 'neutral' }),
54
+ skip: Object.freeze({ label: '跳过', iconId: 'ui.statusSkipped', tone: 'neutral' }),
55
+ cancel: Object.freeze({ label: '取消', iconId: 'ui.statusSkipped', tone: 'neutral' }),
56
+ });
57
+
58
+ const DEFAULT_STATUSES = Object.freeze({
59
+ idle: 'neutral',
60
+ active: 'primary',
61
+ completed: 'success',
62
+ failed: 'danger',
63
+ rejected: 'danger',
64
+ cancelled: 'neutral',
65
+ skipped: 'neutral',
66
+ superseded: 'neutral',
67
+ effective: 'success',
68
+ resolved: 'neutral',
69
+ });
70
+
71
+ const DEFAULT_TRANSITIONS = Object.freeze({
72
+ reject: Object.freeze({ tone: 'danger' }),
73
+ return: Object.freeze({ tone: 'warning' }),
74
+ skip: Object.freeze({ tone: 'neutral' }),
75
+ forward: Object.freeze({ tone: 'success' }),
76
+ });
77
+
78
+ function normalizeMode(mode, fallback = 'light') {
79
+ return THEME_MODES.has(mode) ? mode : fallback;
80
+ }
81
+
82
+ function normalizeTone(tone, fallback = 'neutral') {
83
+ const value = String(tone || '').trim().toLowerCase();
84
+ return TONE_NAME.test(value) ? value : fallback;
85
+ }
86
+
87
+ function supports(ownerWindow, property, value) {
88
+ if (typeof value !== 'string' || !value.trim()) return false;
89
+ const css = ownerWindow?.CSS || globalThis.CSS;
90
+ if (typeof css?.supports === 'function') return css.supports(property, value.trim());
91
+ const style = ownerWindow?.document?.createElement?.('span')?.style;
92
+ if (style) {
93
+ style.setProperty(property, value.trim());
94
+ return Boolean(style.getPropertyValue(property));
95
+ }
96
+ if (property === 'color') return !/[;{}]/.test(value) && /^(?:#[\da-f]{3,8}|[a-z]+|(?:rgb|hsl|hwb|lab|lch|oklab|oklch|color|color-mix|var)\(.+\))$/i.test(value.trim());
97
+ return property === 'box-shadow' && !/[;}]/.test(value);
98
+ }
99
+
100
+ function compilePalette(palette, ownerWindow) {
101
+ if (!palette || typeof palette !== 'object') return new Map();
102
+ const variables = new Map();
103
+ for (const [key, variable] of Object.entries(COLOR_VARIABLES)) {
104
+ const value = palette.colors?.[key];
105
+ if (supports(ownerWindow, 'color', value)) variables.set(variable, value.trim());
106
+ }
107
+ for (const [key, variable] of Object.entries(SHADOW_VARIABLES)) {
108
+ const value = palette.shadows?.[key];
109
+ if (supports(ownerWindow, 'box-shadow', value)) variables.set(variable, value.trim());
110
+ }
111
+ for (const [rawName, tone] of Object.entries(palette.tones || {})) {
112
+ const name = normalizeTone(rawName, '');
113
+ if (!name || !tone || typeof tone !== 'object') continue;
114
+ for (const [key, suffix] of Object.entries(TONE_FIELDS)) {
115
+ const value = tone[key];
116
+ if (supports(ownerWindow, 'color', value)) variables.set(`--nova-tone-${name}-${suffix}`, value.trim());
117
+ }
118
+ }
119
+ return variables;
120
+ }
121
+
122
+ function normalizeThemeInput(input, current = null) {
123
+ if (typeof input === 'string') return { ...(current || {}), mode: normalizeMode(input, current?.mode || 'light') };
124
+ if (!input || typeof input !== 'object') return { mode: 'light', light: null, dark: null };
125
+ return {
126
+ mode: normalizeMode(input.mode, current?.mode || 'light'),
127
+ light: input.light === undefined ? (current?.light || null) : input.light,
128
+ dark: input.dark === undefined ? (current?.dark || null) : input.dark,
129
+ };
130
+ }
131
+
132
+ function mediaListener(media, handler, add) {
133
+ if (!media) return;
134
+ if (typeof media[add ? 'addEventListener' : 'removeEventListener'] === 'function') {
135
+ media[add ? 'addEventListener' : 'removeEventListener']('change', handler);
136
+ } else if (typeof media[add ? 'addListener' : 'removeListener'] === 'function') {
137
+ media[add ? 'addListener' : 'removeListener'](handler);
138
+ }
139
+ }
140
+
141
+ function readAttribute(root, name) {
142
+ return typeof root?.getAttribute === 'function' ? root.getAttribute(name) : null;
143
+ }
144
+
145
+ function writeAttribute(root, name, value) {
146
+ if (typeof root?.setAttribute === 'function') root.setAttribute(name, value);
147
+ else if (root?.dataset) root.dataset[name === 'data-nova-theme' ? 'novaTheme' : 'novaThemeMode'] = value;
148
+ }
149
+
150
+ function removeAttribute(root, name) {
151
+ if (typeof root?.removeAttribute === 'function') root.removeAttribute(name);
152
+ else if (root?.dataset) delete root.dataset[name === 'data-nova-theme' ? 'novaTheme' : 'novaThemeMode'];
153
+ }
154
+
155
+ function readStyle(root, name) {
156
+ return root?.style?.getPropertyValue?.(name) || '';
157
+ }
158
+
159
+ function writeStyle(root, name, value) {
160
+ root?.style?.setProperty?.(name, value);
161
+ }
162
+
163
+ function removeStyle(root, name) {
164
+ root?.style?.removeProperty?.(name);
165
+ }
166
+
167
+ /**
168
+ * Owns theme state for one top-level BPMN Nova root. Nested modules inherit the
169
+ * root tokens and share this controller instead of creating duplicate media listeners.
170
+ */
171
+ export class ThemeController {
172
+ constructor({ root, theme = null, onChange = null } = {}) {
173
+ if (!root) throw new Error('ThemeController requires a root element.');
174
+ this.root = root;
175
+ this.ownerWindow = root.ownerDocument?.defaultView || (typeof window !== 'undefined' ? window : null);
176
+ this.options = normalizeThemeInput(theme);
177
+ this.onChange = typeof onChange === 'function' ? onChange : null;
178
+ this.listeners = new Set();
179
+ this.media = null;
180
+ this._managedVariables = new Set();
181
+ this._originalVariables = new Map();
182
+ this._originalTheme = readAttribute(root, 'data-nova-theme');
183
+ this._originalMode = readAttribute(root, 'data-nova-theme-mode');
184
+ this._originalColorScheme = root.style?.colorScheme || '';
185
+ this._mediaChange = () => this._apply(true);
186
+ this.state = Object.freeze({ mode: this.options.mode, resolvedTheme: 'light' });
187
+ this._syncMedia();
188
+ this._apply(false);
189
+ }
190
+
191
+ _resolve() {
192
+ if (this.options.mode !== 'auto') return this.options.mode;
193
+ return this.media?.matches ? 'dark' : 'light';
194
+ }
195
+
196
+ _syncMedia() {
197
+ const needsMedia = this.options.mode === 'auto';
198
+ if (!needsMedia && this.media) {
199
+ mediaListener(this.media, this._mediaChange, false);
200
+ this.media = null;
201
+ return;
202
+ }
203
+ if (needsMedia && !this.media && typeof this.ownerWindow?.matchMedia === 'function') {
204
+ this.media = this.ownerWindow.matchMedia('(prefers-color-scheme: dark)');
205
+ mediaListener(this.media, this._mediaChange, true);
206
+ }
207
+ }
208
+
209
+ _apply(notify = true) {
210
+ const resolvedTheme = this._resolve();
211
+ const variables = compilePalette(this.options[resolvedTheme], this.ownerWindow);
212
+ for (const name of this._managedVariables) {
213
+ if (variables.has(name)) continue;
214
+ const original = this._originalVariables.get(name) || '';
215
+ if (original) writeStyle(this.root, name, original);
216
+ else removeStyle(this.root, name);
217
+ }
218
+ for (const [name, value] of variables) {
219
+ if (!this._originalVariables.has(name)) this._originalVariables.set(name, readStyle(this.root, name));
220
+ writeStyle(this.root, name, value);
221
+ }
222
+ this._managedVariables = new Set(variables.keys());
223
+ writeAttribute(this.root, 'data-nova-theme', resolvedTheme);
224
+ writeAttribute(this.root, 'data-nova-theme-mode', this.options.mode);
225
+ if (this.root.style) this.root.style.colorScheme = resolvedTheme;
226
+ this.state = Object.freeze({ mode: this.options.mode, resolvedTheme });
227
+ if (!notify) return;
228
+ this.onChange?.(this.state);
229
+ this.listeners.forEach((listener) => listener(this.state));
230
+ }
231
+
232
+ setTheme(theme) {
233
+ this.options = normalizeThemeInput(theme, this.options);
234
+ this._syncMedia();
235
+ this._apply(true);
236
+ return this.state;
237
+ }
238
+
239
+ setMode(mode) {
240
+ this.options = { ...this.options, mode: normalizeMode(mode, this.options.mode) };
241
+ this._syncMedia();
242
+ this._apply(true);
243
+ return this.state;
244
+ }
245
+
246
+ getState() { return this.state; }
247
+
248
+ /**
249
+ * Resolves the concrete color tokens used by standalone visual artifacts.
250
+ * A short-lived probe inherits the shipped theme stylesheet and receives the
251
+ * requested palette overrides without changing this controller's root state.
252
+ */
253
+ getSnapshot(theme = 'current') {
254
+ const resolvedTheme = theme === 'current' || !['light', 'dark'].includes(theme)
255
+ ? this.state.resolvedTheme
256
+ : theme;
257
+ const document = this.root.ownerDocument;
258
+ const ownerWindow = document?.defaultView || this.ownerWindow;
259
+ if (!document?.createElement || typeof ownerWindow?.getComputedStyle !== 'function') {
260
+ return Object.freeze({ resolvedTheme, colors: Object.freeze({}), tones: Object.freeze({}), fontFamily: 'sans-serif' });
261
+ }
262
+ const probe = document.createElement('span');
263
+ probe.setAttribute('data-nova-theme', resolvedTheme);
264
+ probe.setAttribute('aria-hidden', 'true');
265
+ Object.assign(probe.style, {
266
+ position: 'fixed',
267
+ left: '-100000px',
268
+ top: '-100000px',
269
+ width: '1px',
270
+ height: '1px',
271
+ visibility: 'hidden',
272
+ pointerEvents: 'none',
273
+ });
274
+ for (const [name, value] of compilePalette(this.options[resolvedTheme], ownerWindow)) probe.style.setProperty(name, value);
275
+ this.root.appendChild(probe);
276
+ const resolveColor = (variable, fallback = 'rgba(0, 0, 0, 0)') => {
277
+ probe.style.color = `var(${variable}, ${fallback})`;
278
+ return ownerWindow.getComputedStyle(probe).color || fallback;
279
+ };
280
+ const colors = {};
281
+ for (const [key, variable] of Object.entries(COLOR_VARIABLES)) colors[key] = resolveColor(variable);
282
+ const customToneNames = Object.keys(this.options[resolvedTheme]?.tones || {}).map((name) => normalizeTone(name, '')).filter(Boolean);
283
+ const tones = {};
284
+ for (const name of new Set([...SNAPSHOT_TONES, ...customToneNames])) {
285
+ tones[name] = {};
286
+ for (const suffix of Object.values(TONE_FIELDS)) tones[name][suffix] = resolveColor(`--nova-tone-${name}-${suffix}`);
287
+ tones[name] = Object.freeze(tones[name]);
288
+ }
289
+ probe.style.removeProperty('color');
290
+ const inherited = ownerWindow.getComputedStyle(this.root);
291
+ const fontFamily = inherited.fontFamily || '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif';
292
+ probe.remove();
293
+ return Object.freeze({
294
+ resolvedTheme,
295
+ colors: Object.freeze(colors),
296
+ tones: Object.freeze(tones),
297
+ fontFamily,
298
+ });
299
+ }
300
+
301
+ subscribe(listener) {
302
+ if (typeof listener !== 'function') return () => {};
303
+ this.listeners.add(listener);
304
+ return () => this.listeners.delete(listener);
305
+ }
306
+
307
+ destroy() {
308
+ if (this.media) mediaListener(this.media, this._mediaChange, false);
309
+ this.media = null;
310
+ this.listeners.clear();
311
+ for (const name of this._managedVariables) {
312
+ const original = this._originalVariables.get(name) || '';
313
+ if (original) writeStyle(this.root, name, original);
314
+ else removeStyle(this.root, name);
315
+ }
316
+ if (this._originalTheme == null) removeAttribute(this.root, 'data-nova-theme');
317
+ else writeAttribute(this.root, 'data-nova-theme', this._originalTheme);
318
+ if (this._originalMode == null) removeAttribute(this.root, 'data-nova-theme-mode');
319
+ else writeAttribute(this.root, 'data-nova-theme-mode', this._originalMode);
320
+ if (this.root.style) this.root.style.colorScheme = this._originalColorScheme;
321
+ }
322
+ }
323
+
324
+ export function createThemeController(options) { return new ThemeController(options); }
325
+
326
+ export function createRuntimeAppearance(options = {}) {
327
+ const statuses = { ...DEFAULT_STATUSES, ...(options.statuses || {}) };
328
+ const actions = { ...DEFAULT_ACTIONS, ...(options.actions || {}) };
329
+ const transitions = { ...DEFAULT_TRANSITIONS, ...(options.transitions || {}) };
330
+ return Object.freeze({
331
+ resolveStatus(status) { return normalizeTone(statuses[status], DEFAULT_STATUSES[status] || 'neutral'); },
332
+ resolveAction(action = {}) {
333
+ const configured = actions[action.type] || {};
334
+ const fallback = DEFAULT_ACTIONS[action.type] || {};
335
+ return Object.freeze({
336
+ label: action.explicitLabel || configured.label || action.label || fallback.label || action.type || '审批操作',
337
+ iconId: configured.iconId || fallback.iconId || 'ui.edit',
338
+ tone: normalizeTone(configured.tone || fallback.tone, 'neutral'),
339
+ });
340
+ },
341
+ resolveTransition(transition = {}) {
342
+ const configured = transitions[transition.type] || {};
343
+ const fallback = DEFAULT_TRANSITIONS[transition.type] || {};
344
+ return Object.freeze({ tone: normalizeTone(configured.tone || fallback.tone, 'neutral') });
345
+ },
346
+ });
347
+ }
348
+
349
+ export function applyRuntimeTone(element, tone) {
350
+ if (!element?.style) return 'neutral';
351
+ const name = normalizeTone(tone, 'neutral');
352
+ element.dataset.tone = name;
353
+ for (const suffix of Object.values(TONE_FIELDS)) {
354
+ element.style.setProperty(
355
+ `--nova-runtime-tone-${suffix}`,
356
+ `var(--nova-tone-${name}-${suffix}, var(--nova-tone-neutral-${suffix}))`,
357
+ );
358
+ }
359
+ return name;
360
+ }
361
+
362
+ export const DEFAULT_RUNTIME_APPEARANCE = Object.freeze({
363
+ statuses: DEFAULT_STATUSES,
364
+ actions: DEFAULT_ACTIONS,
365
+ transitions: DEFAULT_TRANSITIONS,
366
+ });
367
+
368
+ export const NOVA_THEME_MODES = Object.freeze([...THEME_MODES]);
@@ -0,0 +1,265 @@
1
+ import type { BpmnEdge, BpmnNode, NodeType, ProcessModel } from '../core/index.js'
2
+ import type { IconRegistry } from '../icons/index.js'
3
+ import type {
4
+ ActivityInstance,
5
+ ActivityStatus,
6
+ NodeRuntimePresentation,
7
+ ProcessInstanceSnapshot,
8
+ RuntimeApprovalActionPresentation,
9
+ RuntimeAssetRef,
10
+ RuntimeParticipant,
11
+ RuntimePresentation,
12
+ RuntimeTransition,
13
+ RuntimeTransitionPresentation,
14
+ } from '../runtime/index.js'
15
+ import type {
16
+ NovaThemeInput,
17
+ NovaThemeMode,
18
+ NovaThemeState,
19
+ RuntimeAppearanceOptions,
20
+ RuntimeAppearanceResolver,
21
+ } from '../theme/index.js'
22
+ import type { RuntimeTimelineSvgRenderer, SvgExportArtifact, SvgExportOptions, SvgExportPreviewController, SvgNodeRenderer } from '../export-svg/index.js'
23
+
24
+ export type ViewerProjection = 'auto' | 'standard' | 'approval' | 'compact'
25
+ export interface RuntimeTraceItem {
26
+ id: string
27
+ kind: 'activity' | 'milestone' | 'transition'
28
+ elementId?: string
29
+ visitId?: string | null
30
+ transitionId?: string
31
+ type?: RuntimeTransition['type']
32
+ name: string
33
+ nodeType?: NodeType
34
+ status: ActivityStatus | 'active' | 'resolved'
35
+ statusLabel: string
36
+ summary: string
37
+ time: string
38
+ startTime?: string
39
+ endTime?: string
40
+ outcome?: string
41
+ comment?: string
42
+ round?: number
43
+ participants?: RuntimeParticipant[]
44
+ records?: ActivityInstance[]
45
+ actions?: RuntimeApprovalActionPresentation[]
46
+ latestAction?: RuntimeApprovalActionPresentation | null
47
+ actionText?: string
48
+ actionSummary?: string
49
+ imageCount?: number
50
+ fileCount?: number
51
+ assetCount?: number
52
+ predicted: boolean
53
+ automated?: boolean
54
+ order: number
55
+ }
56
+ export interface RuntimeTraceLink {
57
+ id: string
58
+ sourceItemId: string
59
+ targetItemId: string
60
+ sourceElementId: string
61
+ targetElementId: string
62
+ sourceEdgeIds: string[]
63
+ label: string
64
+ predicted: boolean
65
+ }
66
+ export interface RuntimeTraceGroup {
67
+ id: string
68
+ kind: 'parallel'
69
+ gatewayId: string
70
+ label: string
71
+ itemIds: string[]
72
+ completed: number
73
+ total: number
74
+ status: ActivityStatus
75
+ order: number
76
+ }
77
+ export interface RuntimeTraceProjection {
78
+ graphModel: ProcessModel
79
+ graphRuntime: ProcessInstanceSnapshot
80
+ items: RuntimeTraceItem[]
81
+ links: RuntimeTraceLink[]
82
+ groups: RuntimeTraceGroup[]
83
+ warnings: string[]
84
+ mappings: {
85
+ nodes: Record<string, string[]>
86
+ edges: Record<string, string[]>
87
+ items: Record<string, { elementIds: string[]; activityIds: string[]; visitIds: string[] }>
88
+ edgeVisits: Record<string, string[]>
89
+ }
90
+ }
91
+ export interface RuntimeTraceProjectionOptions {
92
+ includePredicted?: boolean
93
+ showStartMilestone?: boolean
94
+ showEndMilestone?: boolean
95
+ }
96
+ export function createRuntimeTraceProjection(context: {
97
+ model: ProcessModel
98
+ runtime?: ProcessInstanceSnapshot | null
99
+ presentation?: RuntimePresentation
100
+ options?: RuntimeTraceProjectionOptions
101
+ }): RuntimeTraceProjection
102
+
103
+ export interface ViewerTimelineContext {
104
+ model: ProcessModel
105
+ runtime: ProcessInstanceSnapshot | null
106
+ projection: Exclude<ViewerProjection, 'auto'>
107
+ traceProjection: RuntimeTraceProjection | null
108
+ viewer: BpmnViewer
109
+ }
110
+ export interface ViewerTimelineOptions {
111
+ title?: string | null | ((context: ViewerTimelineContext) => string | null)
112
+ description?: string | null | ((context: ViewerTimelineContext) => string | null)
113
+ }
114
+ export type RuntimeDetailsPlacement = 'popover' | 'center' | 'bottom'
115
+ export interface RuntimeDetailsLayoutOptions {
116
+ placement?: RuntimeDetailsPlacement
117
+ width?: number | string
118
+ maxHeight?: number | string
119
+ backdrop?: boolean
120
+ dragToDismiss?: boolean
121
+ }
122
+ export interface RuntimeDetailsOptions {
123
+ autoOpen?: boolean
124
+ desktop?: RuntimeDetailsLayoutOptions
125
+ mobile?: RuntimeDetailsLayoutOptions
126
+ }
127
+ export type RuntimeAssetPurpose = 'thumbnail' | 'preview' | 'download' | 'export'
128
+ export type RuntimeAssetResolver = (
129
+ asset: RuntimeAssetRef,
130
+ context: { purpose: RuntimeAssetPurpose; action: RuntimeApprovalActionPresentation; signal: AbortSignal },
131
+ ) => string | null | Promise<string | null>
132
+ export type ResolveRuntimeAsset = (
133
+ asset: RuntimeAssetRef,
134
+ purpose: RuntimeAssetPurpose,
135
+ action: RuntimeApprovalActionPresentation,
136
+ ) => Promise<string | null>
137
+ export type RuntimeDetailsRenderer = (context: {
138
+ container: HTMLElement
139
+ viewer: BpmnViewer
140
+ node: BpmnNode
141
+ presentation: NodeRuntimePresentation
142
+ anchor: Element | null
143
+ layout: Required<RuntimeDetailsLayoutOptions>
144
+ resolveAsset: ResolveRuntimeAsset
145
+ themeState: NovaThemeState
146
+ runtimeAppearance: RuntimeAppearanceResolver
147
+ close(): void
148
+ }) => void | (() => void)
149
+ export type RuntimeTransitionDetailsRenderer = (context: {
150
+ container: HTMLElement
151
+ viewer: BpmnViewer
152
+ transition: RuntimeTransitionPresentation
153
+ sourceNode: BpmnNode | null
154
+ targetNode: BpmnNode | null
155
+ anchor: Element | null
156
+ layout: Required<RuntimeDetailsLayoutOptions>
157
+ resolveAsset: ResolveRuntimeAsset
158
+ themeState: NovaThemeState
159
+ runtimeAppearance: RuntimeAppearanceResolver
160
+ close(): void
161
+ locateSource(): void
162
+ locateTarget(): void
163
+ }) => void | (() => void)
164
+ export type RuntimeTimelineRenderer = (context: {
165
+ container: HTMLElement
166
+ model: ProcessModel
167
+ runtime: ProcessInstanceSnapshot
168
+ presentation: RuntimePresentation
169
+ projection: RuntimeTraceProjection
170
+ title: string | null
171
+ description: string | null
172
+ iconRegistry: IconRegistry
173
+ themeState: NovaThemeState
174
+ appearance: RuntimeAppearanceResolver
175
+ resolveAsset: ResolveRuntimeAsset
176
+ onAssetPreview(asset: RuntimeAssetRef, action: RuntimeApprovalActionPresentation): void
177
+ onItemClick(item: RuntimeTraceItem, event: Event): void
178
+ onDetailsRequest(item: RuntimeTraceItem, anchor: Element, event: Event): void
179
+ onTransitionRequest(item: RuntimeTraceItem, anchor: Element, event: Event): void
180
+ }) => void | (() => void)
181
+ export interface RuntimeTraceClickEvent {
182
+ targetType: 'node' | 'edge' | 'visit' | 'transition'
183
+ elementId?: string
184
+ element?: BpmnNode | BpmnEdge | null
185
+ visitId?: string | null
186
+ traceItem?: RuntimeTraceItem | null
187
+ transition?: RuntimeTransitionPresentation | null
188
+ presentation?: NodeRuntimePresentation | null
189
+ activityInstances: ActivityInstance[]
190
+ actions: RuntimeApprovalActionPresentation[]
191
+ runtime: ProcessInstanceSnapshot | null
192
+ projection: Exclude<ViewerProjection, 'auto'>
193
+ originalEvent?: Event
194
+ viewer: BpmnViewer
195
+ }
196
+ export interface ViewerOptions {
197
+ container: HTMLElement
198
+ model: ProcessModel
199
+ runtime?: ProcessInstanceSnapshot | null
200
+ iconRegistry?: IconRegistry
201
+ nodeRenderers?: Record<string, Function>
202
+ nodeRenderer?: Function
203
+ projection?: ViewerProjection
204
+ responsive?: boolean
205
+ runtimeTraceOptions?: RuntimeTraceProjectionOptions
206
+ timeline?: ViewerTimelineOptions
207
+ runtimeDetails?: RuntimeDetailsOptions
208
+ onTraceClick?: (payload: RuntimeTraceClickEvent) => void
209
+ runtimePresenter?: (context: { model: ProcessModel; runtime: ProcessInstanceSnapshot | null; appearance: RuntimeAppearanceResolver }) => RuntimePresentation
210
+ runtimeTraceProjector?: (context: { model: ProcessModel; runtime: ProcessInstanceSnapshot; presentation: RuntimePresentation; options: RuntimeTraceProjectionOptions }) => RuntimeTraceProjection
211
+ runtimeAssetResolver?: RuntimeAssetResolver
212
+ theme?: NovaThemeInput
213
+ runtimeAppearance?: RuntimeAppearanceOptions
214
+ svgExport?: SvgExportOptions & { label?: string; nodeRenderers?: Record<string, SvgNodeRenderer>; runtimeTimelineRenderer?: RuntimeTimelineSvgRenderer }
215
+ onThemeChange?: (state: NovaThemeState) => void
216
+ runtimeTimelineRenderer?: RuntimeTimelineRenderer
217
+ onRuntimeTraceItemClick?: (payload: { item: RuntimeTraceItem; projection: RuntimeTraceProjection; viewer: BpmnViewer; event: Event }) => void
218
+ onProjectionChange?: (payload: { requested: ViewerProjection; active: Exclude<ViewerProjection, 'auto'> }) => void
219
+ runtimeDetailsRenderer?: RuntimeDetailsRenderer | null
220
+ onRuntimeDetailsOpen?: (payload: { node: BpmnNode; presentation: NodeRuntimePresentation; layout: Required<RuntimeDetailsLayoutOptions>; close(): void }) => void
221
+ runtimeTransitionDetailsRenderer?: RuntimeTransitionDetailsRenderer | null
222
+ onRuntimeTransitionDetailsOpen?: (payload: { transition: RuntimeTransitionPresentation; sourceNode: BpmnNode | null; targetNode: BpmnNode | null; layout: Required<RuntimeDetailsLayoutOptions>; close(): void }) => void
223
+ onElementClick?: (payload: { kind: 'node' | 'edge'; element: BpmnNode | BpmnEdge; runtime: ProcessInstanceSnapshot | null; presentation: NodeRuntimePresentation | null } | null) => void
224
+ }
225
+
226
+ export function renderDefaultRuntimeTimeline(context: Parameters<RuntimeTimelineRenderer>[0]): void | (() => void)
227
+ export function formatRuntimeAssetSize(value: number | null | undefined): string
228
+ export function renderRuntimeApprovalContent(context: {
229
+ container: HTMLElement
230
+ actions: RuntimeApprovalActionPresentation[]
231
+ resolveAsset?: ResolveRuntimeAsset
232
+ onAssetPreview?: (asset: RuntimeAssetRef, action: RuntimeApprovalActionPresentation) => void
233
+ }): void | (() => void)
234
+ export function renderDefaultRuntimeDetails(context: Parameters<RuntimeDetailsRenderer>[0]): void | (() => void)
235
+ export function renderDefaultRuntimeTransitionDetails(context: Parameters<RuntimeTransitionDetailsRenderer>[0]): void | (() => void)
236
+ export function projectModel(model: ProcessModel, projection?: ViewerProjection): ProcessModel
237
+
238
+ export class BpmnViewer {
239
+ constructor(options: ViewerOptions)
240
+ model: ProcessModel
241
+ runtime: ProcessInstanceSnapshot | null
242
+ projection: ViewerProjection
243
+ responsive: boolean
244
+ activeProjection: Exclude<ViewerProjection, 'auto'>
245
+ traceProjection: RuntimeTraceProjection | null
246
+ setModel(model: ProcessModel): void
247
+ setRuntime(runtime: ProcessInstanceSnapshot | null): void
248
+ setDisplayOptions(options: { timeline?: ViewerTimelineOptions; runtimeDetails?: RuntimeDetailsOptions; runtimeTraceOptions?: RuntimeTraceProjectionOptions; runtimeAssetResolver?: RuntimeAssetResolver | null }): void
249
+ setProjection(projection: ViewerProjection): void
250
+ setTheme(theme: NovaThemeInput): NovaThemeState
251
+ setThemeMode(mode: NovaThemeMode): NovaThemeState
252
+ getThemeState(): NovaThemeState
253
+ exportSvg(options?: SvgExportOptions): Promise<SvgExportArtifact>
254
+ openSvgExportPreview(options?: SvgExportOptions & { previewTitle?: string; onDownload?: (artifact: SvgExportArtifact) => void }): SvgExportPreviewController
255
+ setRuntimeAppearance(options: RuntimeAppearanceOptions | null): void
256
+ fitView(): void
257
+ zoomBy(delta: number): void
258
+ clearSelection(): void
259
+ openRuntimeDetails(node: BpmnNode, presentation?: NodeRuntimePresentation, anchor?: Element | null): boolean
260
+ openRuntimeTransitionDetails(transition: RuntimeTransitionPresentation | string, anchor?: Element | null): boolean
261
+ closeRuntimeAssetPreview(): void
262
+ closeRuntimeDetails(): void
263
+ refresh(): void
264
+ destroy(): void
265
+ }